[wpiutil] Change C++ protobuf to nanopb (#7309)

The Google C++ protobuf implementation has issues with dynamic linkage across DLL boundaries because it uses global variables.  It also has a compile-time dependency because the protoc version must exactly match the libprotobuf version.  Using nanopb with a customized generator fixes both of these issues.

Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
This commit is contained in:
Thad House
2024-11-07 22:42:50 -08:00
committed by GitHub
parent fd2e0c0427
commit 8b8b634f65
166 changed files with 17522 additions and 1571 deletions

View File

@@ -8,191 +8,43 @@
#include <vector>
#include <fmt/format.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/message.h>
#include "wpi/ProtoHelper.h"
#include "wpi/SmallVector.h"
using namespace wpi;
using google::protobuf::Arena;
using google::protobuf::FileDescriptor;
using google::protobuf::FileDescriptorProto;
namespace {
class VectorOutputStream final
: public google::protobuf::io::ZeroCopyOutputStream {
public:
// Create a StringOutputStream which appends bytes to the given string.
// The string remains property of the caller, but it is mutated in arbitrary
// ways and MUST NOT be accessed in any way until you're done with the
// stream. Either be sure there's no further usage, or (safest) destroy the
// stream before using the contents.
//
// Hint: If you call target->reserve(n) before creating the stream,
// the first call to Next() will return at least n bytes of buffer
// space.
explicit VectorOutputStream(std::vector<uint8_t>& target) : target_{target} {}
VectorOutputStream(const VectorOutputStream&) = delete;
~VectorOutputStream() override = default;
VectorOutputStream& operator=(const VectorOutputStream&) = delete;
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size) override;
void BackUp(int count) override { target_.resize(target_.size() - count); }
int64_t ByteCount() const override { return target_.size(); }
private:
static constexpr size_t kMinimumSize = 16;
std::vector<uint8_t>& target_;
};
class SmallVectorOutputStream final
: public google::protobuf::io::ZeroCopyOutputStream {
public:
// Create a StringOutputStream which appends bytes to the given string.
// The string remains property of the caller, but it is mutated in arbitrary
// ways and MUST NOT be accessed in any way until you're done with the
// stream. Either be sure there's no further usage, or (safest) destroy the
// stream before using the contents.
//
// Hint: If you call target->reserve(n) before creating the stream,
// the first call to Next() will return at least n bytes of buffer
// space.
explicit SmallVectorOutputStream(wpi::SmallVectorImpl<uint8_t>& target)
: target_{target} {
target.resize(0);
}
SmallVectorOutputStream(const SmallVectorOutputStream&) = delete;
~SmallVectorOutputStream() override = default;
SmallVectorOutputStream& operator=(const SmallVectorOutputStream&) = delete;
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size) override;
void BackUp(int count) override { target_.resize(target_.size() - count); }
int64_t ByteCount() const override { return target_.size(); }
private:
static constexpr size_t kMinimumSize = 16;
wpi::SmallVectorImpl<uint8_t>& target_;
};
} // namespace
bool VectorOutputStream::Next(void** data, int* size) {
size_t old_size = target_.size();
// Grow the string.
size_t new_size;
if (old_size < target_.capacity()) {
// Resize to match its capacity, since we can get away
// without a memory allocation this way.
new_size = target_.capacity();
} else {
// Size has reached capacity, try to double it.
new_size = old_size * 2;
}
// Avoid integer overflow in returned '*size'.
new_size = (std::min)(new_size, old_size + (std::numeric_limits<int>::max)());
// Increase the size, also make sure that it is at least kMinimumSize.
target_.resize((std::max)(new_size, kMinimumSize));
*data = target_.data() + old_size;
*size = target_.size() - old_size;
return true;
}
bool SmallVectorOutputStream::Next(void** data, int* size) {
size_t old_size = target_.size();
// Grow the string.
size_t new_size;
if (old_size < target_.capacity()) {
// Resize to match its capacity, since we can get away
// without a memory allocation this way.
new_size = target_.capacity();
} else {
// Size has reached capacity, try to double it.
new_size = old_size * 2;
}
// Avoid integer overflow in returned '*size'.
new_size = (std::min)(new_size, old_size + (std::numeric_limits<int>::max)());
// Increase the size, also make sure that it is at least kMinimumSize.
target_.resize_for_overwrite((std::max)(new_size, kMinimumSize));
*data = target_.data() + old_size;
*size = target_.size() - old_size;
return true;
}
void detail::DeleteProtobuf(google::protobuf::Message* msg) {
if (msg && !msg->GetArena()) {
delete msg;
}
}
bool detail::ParseProtobuf(google::protobuf::Message* msg,
std::span<const uint8_t> data) {
return msg->ParseFromArray(data.data(), data.size());
}
bool detail::SerializeProtobuf(wpi::SmallVectorImpl<uint8_t>& out,
const google::protobuf::Message& msg) {
SmallVectorOutputStream stream{out};
return msg.SerializeToZeroCopyStream(&stream);
}
bool detail::SerializeProtobuf(std::vector<uint8_t>& out,
const google::protobuf::Message& msg) {
VectorOutputStream stream{out};
return msg.SerializeToZeroCopyStream(&stream);
}
std::string detail::GetTypeString(const google::protobuf::Message& msg) {
return fmt::format("proto:{}", msg.GetDescriptor()->full_name());
}
static void ForEachProtobufDescriptorImpl(
const FileDescriptor* desc,
function_ref<bool(std::string_view typeString)> exists,
function_ref<void(std::string_view typeString,
std::span<const uint8_t> schema)>
fn,
Arena* arena, FileDescriptorProto** descproto) {
std::string name = fmt::format("proto:{}", desc->name());
if (exists(name)) {
return;
}
for (int i = 0, ndep = desc->dependency_count(); i < ndep; ++i) {
ForEachProtobufDescriptorImpl(desc->dependency(i), exists, fn, arena,
descproto);
}
if (!*descproto) {
*descproto = wpi::CreateMessage<FileDescriptorProto>(arena);
}
(*descproto)->Clear();
desc->CopyTo(*descproto);
SmallVector<uint8_t, 128> buf;
detail::SerializeProtobuf(buf, **descproto);
fn(name, buf);
std::string detail::GetTypeString(const pb_msgdesc_t* msg) {
return fmt::format("proto:{}", msg->proto_name);
}
void detail::ForEachProtobufDescriptor(
const google::protobuf::Message& msg,
const pb_msgdesc_t* msg,
function_ref<bool(std::string_view filename)> exists,
function_ref<void(std::string_view filename,
std::span<const uint8_t> descriptor)>
fn) {
FileDescriptorProto* descproto = nullptr;
ForEachProtobufDescriptorImpl(msg.GetDescriptor()->file(), exists, fn,
msg.GetArena(), &descproto);
if (descproto && !msg.GetArena()) {
delete descproto;
std::string name = fmt::format("proto:{}", msg->file_descriptor.file_name);
if (exists(name)) {
return;
}
const pb_msgdesc_t* const* nested = msg->submsg_info;
while (*nested) {
ForEachProtobufDescriptor(*nested, exists, fn);
nested++;
}
fn(name, msg->file_descriptor.file_descriptor);
}
bool detail::WriteFromSmallVector(pb_ostream_t* stream, const pb_byte_t* buf,
size_t count) {
SmallVectorType* vec = reinterpret_cast<SmallVectorType*>(stream->state);
vec->append(buf, buf + count);
return true;
}
bool detail::WriteFromStdVector(pb_ostream_t* stream, const pb_byte_t* buf,
size_t count) {
StdVectorType* vec = reinterpret_cast<StdVectorType*>(stream->state);
vec->insert(vec->end(), buf, buf + count);
return true;
}

View File

@@ -14,18 +14,12 @@
#include <utility>
#include <vector>
#include "pb.h"
#include "pb_decode.h"
#include "pb_encode.h"
#include "wpi/array.h"
#include "wpi/function_ref.h"
namespace google::protobuf {
class Arena;
class Message;
template <typename T>
class RepeatedPtrField;
template <typename T>
class RepeatedField;
} // namespace google::protobuf
namespace wpi {
template <typename T>
@@ -41,43 +35,235 @@ class SmallVectorImpl;
template <typename T>
struct Protobuf {};
namespace detail {
using SmallVectorType = wpi::SmallVectorImpl<uint8_t>;
using StdVectorType = std::vector<uint8_t>;
bool WriteFromSmallVector(pb_ostream_t* stream, const pb_byte_t* buf,
size_t count);
bool WriteFromStdVector(pb_ostream_t* stream, const pb_byte_t* buf,
size_t count);
} // namespace detail
/**
* Class for wrapping a nanopb istream.
*/
template <typename T>
class ProtoInputStream {
public:
/**
* Constructs a nanopb istream from an existing istream object.
* Generally used internally for decoding submessages
*
* @param[in] stream the nanopb istream
*/
explicit ProtoInputStream(pb_istream_t* stream)
: m_streamMsg{stream},
m_msgDesc{
Protobuf<std::remove_cvref_t<T>>::MessageStruct::msg_descriptor()} {
}
/**
* Constructs a nanopb istream from a buffer.
*
* @param[in] stream the stream buffer
*/
explicit ProtoInputStream(std::span<const uint8_t> stream)
: m_streamLocal{pb_istream_from_buffer(
reinterpret_cast<const pb_byte_t*>(stream.data()), stream.size())},
m_msgDesc{
Protobuf<std::remove_cvref_t<T>>::MessageStruct::msg_descriptor()} {
}
/**
* Gets the backing nanopb stream object.
*
* @return nanopb stream
*/
pb_istream_t* Stream() noexcept {
return m_streamMsg ? m_streamMsg : &m_streamLocal;
}
/**
* Gets the nanopb message descriptor
*
* @return the nanopb message descriptor
*/
const pb_msgdesc_t* MsgDesc() const noexcept { return m_msgDesc; }
/**
* Decodes a protobuf. Flags are the same flags passed to pb_decode_ex.
*
* @param[in] msg The message to decode into
* @param[in] flags Flags to pass
* @return true if decoding was successful, false otherwise
*/
bool Decode(typename Protobuf<std::remove_cvref_t<T>>::MessageStruct& msg,
unsigned int flags = 0) {
return pb_decode_ex(Stream(), m_msgDesc, &msg, flags);
}
private:
pb_istream_t m_streamLocal;
pb_istream_t* m_streamMsg{nullptr};
const pb_msgdesc_t* m_msgDesc;
};
/**
* Class for wrapping a nanopb ostream
*/
template <typename T>
class ProtoOutputStream {
public:
/**
* Constructs a nanopb ostream from an existing ostream object
* Generally used internally for encoding messages.
*
* This constructor will cause `Encode` to call pb_encode_submessage
* instead of `pb_encode_ex`
*
* @param[in] stream the nanopb ostream
*/
explicit ProtoOutputStream(pb_ostream_t* stream)
: m_streamMsg{stream},
m_msgDesc{
Protobuf<std::remove_cvref_t<T>>::MessageStruct::msg_descriptor()} {
}
/**
* Constructs a nanopb ostream from a buffer.
*
* This constructor will cause `Encode` to call pb_encode_ex`
*
* @param[in] out the stream buffer
*/
explicit ProtoOutputStream(detail::SmallVectorType& out)
: m_msgDesc{
Protobuf<std::remove_cvref_t<T>>::MessageStruct::msg_descriptor()} {
m_streamLocal.callback = detail::WriteFromSmallVector;
m_streamLocal.state = &out;
m_streamLocal.max_size = SIZE_MAX;
m_streamLocal.bytes_written = 0;
m_streamLocal.errmsg = nullptr;
}
/**
* Constructs a nanopb ostream from a buffer.
*
* This constructor will cause `Encode` to call pb_encode_ex`
*
* @param[in] out the stream buffer
*/
explicit ProtoOutputStream(detail::StdVectorType& out)
: m_msgDesc{
Protobuf<std::remove_cvref_t<T>>::MessageStruct::msg_descriptor()} {
m_streamLocal.callback = detail::WriteFromStdVector;
m_streamLocal.state = &out;
m_streamLocal.max_size = SIZE_MAX;
m_streamLocal.bytes_written = 0;
m_streamLocal.errmsg = nullptr;
}
/**
* Constructs a empty nanopb stream. You must fill out the stream
* returned from `Stream` before calling Encode.
*
* This constructor exists to cause `Encode` to call pb_encode_ex`,
* but allow manipulating the stream manually.
*/
ProtoOutputStream()
: m_msgDesc{Protobuf<
std::remove_cvref_t<T>>::MessageStruct::msg_descriptor()} {}
/**
* Gets the backing nanopb stream object.
*
* @return nanopb stream
*/
pb_ostream_t* Stream() noexcept {
return m_streamMsg ? m_streamMsg : &m_streamLocal;
}
/**
* Gets if this stream points to a submessage, and will call
* pb_encode_submessage instead of pb_encode
*
* @return true if submessage, false otherwise
*/
bool IsSubmessage() const noexcept { return m_streamMsg; }
/**
* Gets the nanopb message descriptor
*
* @return the nanopb message descriptor
*/
const pb_msgdesc_t* MsgDesc() const noexcept { return m_msgDesc; }
/**
* Decodes a protobuf. Flags are the same flags passed to pb_decode_ex.
*
* @param[in] msg The message to encode from
* @return true if encoding was successful, false otherwise
*/
bool Encode(
const typename Protobuf<std::remove_cvref_t<T>>::MessageStruct& msg) {
if (m_streamMsg) {
return pb_encode_submessage(m_streamMsg, m_msgDesc, &msg);
}
return pb_encode(&m_streamLocal, m_msgDesc, &msg);
}
private:
pb_ostream_t m_streamLocal;
pb_ostream_t* m_streamMsg{nullptr};
const pb_msgdesc_t* m_msgDesc;
};
/**
* Specifies that a type is capable of protobuf serialization and
* deserialization.
*
* This is designed for serializing complex flexible data structures using
* code generated from a .proto file. Serialization consists of writing
* values into a mutable protobuf Message and deserialization consists of
* reading values from an immutable protobuf Message.
* values into a nanopb Stream and deserialization consists of
* reading values from nanopb Stream.
*
* Implementations must define a template specialization for wpi::Protobuf with
* T being the type that is being serialized/deserialized, with the following
* static members (as enforced by this concept):
* - google::protobuf::Message* New(google::protobuf::Arena*): create a protobuf
* message
* - T Unpack(const google::protobuf::Message&): function for deserialization
* - void Pack(google::protobuf::Message*, T&& value): function for
* serialization
* - using MessageStruct = nanopb_message_struct_here: typedef to the wpilib
* modified nanopb message struct
* - std::optional<T> Unpack(wpi::ProtoInputStream<T>&): function
* for deserialization
* - bool Pack(wpi::ProtoOutputStream<T>&, T&& value): function
* for serialization
*
* To avoid pulling in the protobuf headers, these functions use
* google::protobuf::Message instead of a more specific type; implementations
* will need to static_cast to the correct type as created by New().
*
* Additionally: In a static block, call StructRegistry.registerClass() to
* register the class
* As a suggestion, 2 extra type usings can be added to simplify the stream
* definitions, however these are not required.
* - using InputStream = wpi::ProtoInputStream<T>;
* - using OutputStream = wpi::ProtoOutputStream<T>;
*/
template <typename T>
concept ProtobufSerializable = requires(
google::protobuf::Arena* arena, const google::protobuf::Message& inmsg,
google::protobuf::Message* outmsg, const T& value) {
wpi::ProtoOutputStream<std::remove_cvref_t<T>>& ostream,
wpi::ProtoInputStream<std::remove_cvref_t<T>>& istream, const T& value) {
typename Protobuf<typename std::remove_cvref_t<T>>;
{
Protobuf<typename std::remove_cvref_t<T>>::New(arena)
} -> std::same_as<google::protobuf::Message*>;
Protobuf<typename std::remove_cvref_t<T>>::Unpack(istream)
} -> std::same_as<std::optional<typename std::remove_cvref_t<T>>>;
{
Protobuf<typename std::remove_cvref_t<T>>::Unpack(inmsg)
} -> std::same_as<typename std::remove_cvref_t<T>>;
Protobuf<typename std::remove_cvref_t<T>>::Pack(outmsg, value);
Protobuf<typename std::remove_cvref_t<T>>::Pack(ostream, value)
} -> std::same_as<bool>;
typename Protobuf<typename std::remove_cvref_t<T>>::MessageStruct;
{
Protobuf<typename std::remove_cvref_t<T>>::MessageStruct::msg_descriptor()
} -> std::same_as<const pb_msgdesc_t*>;
{
Protobuf<typename std::remove_cvref_t<T>>::MessageStruct::msg_name()
} -> std::same_as<std::string_view>;
{
Protobuf<typename std::remove_cvref_t<T>>::MessageStruct::file_descriptor()
} -> std::same_as<pb_filedesc_t>;
};
/**
@@ -85,146 +271,22 @@ concept ProtobufSerializable = requires(
*
* In addition to meeting ProtobufSerializable, implementations must define a
* wpi::Protobuf<T> static member
* `void UnpackInto(T*, const google::protobuf::Message&)` to update the
* pointed-to T with the contents of the message.
* - bool UnpackInto(T*, wpi::ProtoInputStream<T>&)` to update the
* pointed-to T with the contents of the message.
*/
template <typename T>
concept MutableProtobufSerializable =
ProtobufSerializable<T> &&
requires(T* out, const google::protobuf::Message& msg) {
Protobuf<typename std::remove_cvref_t<T>>::UnpackInto(out, msg);
requires(T* out, wpi::ProtoInputStream<T>& istream) {
{
Protobuf<typename std::remove_cvref_t<T>>::UnpackInto(out, istream)
} -> std::same_as<bool>;
};
/**
* Unpack a serialized protobuf message.
*
* @tparam T object type
* @param msg protobuf message
* @return Deserialized object
*/
template <ProtobufSerializable T>
inline T UnpackProtobuf(const google::protobuf::Message& msg) {
return Protobuf<T>::Unpack(msg);
}
/**
* Unpack a serialized protobuf array message.
*
* @tparam Proto element type of the protobuf array
* @tparam T object type
* @tparam N number of objects
* @param msg protobuf array message
* @return Deserialized array
*/
template <std::derived_from<google::protobuf::Message> Proto,
ProtobufSerializable T, size_t N>
wpi::array<T, N> UnpackProtobufArray(
const google::protobuf::RepeatedPtrField<Proto>& msg) {
if (N != std::dynamic_extent && msg.size() != N) {
// TODO
}
wpi::array<T, N> arr(wpi::empty_array);
for (size_t i = 0; i < N; i++) {
arr[i] = wpi::UnpackProtobuf<T>(msg.Get(i));
}
return arr;
}
/**
* Unpack a serialized protobuf array message.
*
* @tparam T element type of the protobuf array
* @tparam N number of objects
* @param msg protobuf array message
* @return Deserialized array
*/
template <typename T, size_t N>
wpi::array<T, N> UnpackProtobufArray(
const google::protobuf::RepeatedField<T>& msg) {
if (N != std::dynamic_extent && msg.size() != N) {
// TODO
}
wpi::array<T, N> arr(wpi::empty_array);
for (size_t i = 0; i < N; i++) {
arr[i] = msg.Get(i);
}
return arr;
}
/**
* Pack a serialized protobuf message.
*
* @param msg protobuf message (mutable, output)
* @param value object
*/
template <ProtobufSerializable T>
inline void PackProtobuf(google::protobuf::Message* msg, const T& value) {
Protobuf<typename std::remove_cvref_t<T>>::Pack(msg, value);
}
/**
* Pack a serialized protobuf array message.
*
* @tparam Proto element type of the protobuf array
* @tparam T object type
* @tparam N number of objects
* @param msg protobuf message (mutable, output)
* @param arr array of objects
*/
template <std::derived_from<google::protobuf::Message> Proto,
ProtobufSerializable T, size_t N>
void PackProtobufArray(google::protobuf::RepeatedPtrField<Proto>* msg,
const wpi::array<T, N>& arr) {
msg->Clear();
msg->Reserve(N);
for (const auto& obj : arr) {
PackProtobuf(msg->Add(), obj);
}
}
/**
* Pack a serialized protobuf array message.
*
* @tparam T object type
* @tparam N number of objects
* @param msg protobuf message (mutable, output)
* @param arr array of objects
*/
template <typename T, size_t N>
void PackProtobufArray(google::protobuf::RepeatedField<T>* msg,
const wpi::array<T, N>& arr) {
msg->Clear();
msg->Reserve(N);
msg->Add(arr.begin(), arr.end());
}
/**
* Unpack a serialized struct into an existing object, overwriting its contents.
*
* @param out object (output)
* @param msg protobuf message
*/
template <ProtobufSerializable T>
inline void UnpackProtobufInto(T* out, const google::protobuf::Message& msg) {
if constexpr (MutableProtobufSerializable<T>) {
Protobuf<T>::UnpackInto(out, msg);
} else {
*out = UnpackProtobuf<T>(msg);
}
}
// these detail functions avoid the need to include protobuf headers
namespace detail {
void DeleteProtobuf(google::protobuf::Message* msg);
bool ParseProtobuf(google::protobuf::Message* msg,
std::span<const uint8_t> data);
bool SerializeProtobuf(wpi::SmallVectorImpl<uint8_t>& out,
const google::protobuf::Message& msg);
bool SerializeProtobuf(std::vector<uint8_t>& out,
const google::protobuf::Message& msg);
std::string GetTypeString(const google::protobuf::Message& msg);
std::string GetTypeString(const pb_msgdesc_t* msg);
void ForEachProtobufDescriptor(
const google::protobuf::Message& msg,
const pb_msgdesc_t* msg,
function_ref<bool(std::string_view filename)> wants,
function_ref<void(std::string_view filename,
std::span<const uint8_t> descriptor)>
@@ -232,48 +294,23 @@ void ForEachProtobufDescriptor(
} // namespace detail
/**
* Owning wrapper (ala std::unique_ptr) for google::protobuf::Message* that does
* not require the protobuf headers be included. Note this object is not thread
* safe; users of this object are required to provide any necessary thread
* safety.
* Ease of use wrapper to make nanopb streams more opaque to the user.
* This class is stateless and thread safe.
*
* @tparam T serialized object type
*/
template <ProtobufSerializable T>
class ProtobufMessage {
public:
explicit ProtobufMessage(google::protobuf::Arena* arena = nullptr)
: m_msg{Protobuf<T>::New(arena)} {}
~ProtobufMessage() { detail::DeleteProtobuf(m_msg); }
ProtobufMessage(const ProtobufMessage&) = delete;
ProtobufMessage& operator=(const ProtobufMessage&) = delete;
ProtobufMessage(ProtobufMessage&& rhs) : m_msg{rhs.m_msg} {
rhs.m_msg = nullptr;
}
ProtobufMessage& operator=(ProtobufMessage&& rhs) {
std::swap(m_msg, rhs.m_msg);
return *this;
}
/**
* Gets the stored message object.
*
* @return google::protobuf::Message*
*/
google::protobuf::Message* GetMessage() { return m_msg; }
const google::protobuf::Message* GetMessage() const { return m_msg; }
/**
* Unpacks from a byte array.
*
* @param data byte array
* @return Optional; empty if parsing failed
*/
std::optional<T> Unpack(std::span<const uint8_t> data) {
if (!detail::ParseProtobuf(m_msg, data)) {
return std::nullopt;
}
return Protobuf<T>::Unpack(*m_msg);
std::optional<std::remove_cvref_t<T>> Unpack(std::span<const uint8_t> data) {
ProtoInputStream<std::remove_cvref_t<T>> stream{data};
return Protobuf<std::remove_cvref_t<T>>::Unpack(stream);
}
/**
@@ -284,11 +321,17 @@ class ProtobufMessage {
* @return true if successful
*/
bool UnpackInto(T* out, std::span<const uint8_t> data) {
if (!detail::ParseProtobuf(m_msg, data)) {
return false;
if constexpr (MutableProtobufSerializable<T>) {
ProtoInputStream<std::remove_cvref_t<T>> stream{data};
return Protobuf<std::remove_cvref_t<T>>::UnpackInto(out, stream);
} else {
auto unpacked = Unpack(data);
if (!unpacked) {
return false;
}
*out = std::move(unpacked.value());
return true;
}
UnpackProtobufInto(out, *m_msg);
return true;
}
/**
@@ -299,8 +342,8 @@ class ProtobufMessage {
* @return true if successful
*/
bool Pack(wpi::SmallVectorImpl<uint8_t>& out, const T& value) {
Protobuf<T>::Pack(m_msg, value);
return detail::SerializeProtobuf(out, *m_msg);
ProtoOutputStream<std::remove_cvref_t<T>> stream{out};
return Protobuf<std::remove_cvref_t<T>>::Pack(stream, value);
}
/**
@@ -311,8 +354,8 @@ class ProtobufMessage {
* @return true if successful
*/
bool Pack(std::vector<uint8_t>& out, const T& value) {
Protobuf<T>::Pack(m_msg, value);
return detail::SerializeProtobuf(out, *m_msg);
ProtoOutputStream<std::remove_cvref_t<T>> stream{out};
return Protobuf<std::remove_cvref_t<T>>::Pack(stream, value);
}
/**
@@ -320,7 +363,10 @@ class ProtobufMessage {
*
* @return type string
*/
std::string GetTypeString() const { return detail::GetTypeString(*m_msg); }
std::string GetTypeString() const {
return detail::GetTypeString(
Protobuf<std::remove_cvref_t<T>>::MessageStruct::msg_descriptor());
}
/**
* Loops over all protobuf descriptors including nested/referenced
@@ -335,11 +381,10 @@ class ProtobufMessage {
function_ref<void(std::string_view filename,
std::span<const uint8_t> descriptor)>
fn) {
detail::ForEachProtobufDescriptor(*m_msg, exists, fn);
detail::ForEachProtobufDescriptor(
Protobuf<std::remove_cvref_t<T>>::MessageStruct::msg_descriptor(),
exists, fn);
}
private:
google::protobuf::Message* m_msg = nullptr;
};
} // namespace wpi

View File

@@ -0,0 +1,670 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <span>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include "pb.h"
#include "wpi/SmallVector.h"
#include "wpi/array.h"
#include "wpi/protobuf/Protobuf.h"
namespace wpi {
/**
* The behavior to use when more elements are in the message then expected when
* decoding.
*/
enum class DecodeLimits {
// Ignore any extra elements
Ignore,
// Add any extra elements to the backing vector
Add,
// Cause decoding to fail if extra elements exist
Fail,
};
template <class T>
concept StringLike = std::is_convertible_v<T, std::string_view>;
template <class T>
concept ConstVectorLike = std::is_convertible_v<T, std::span<const uint8_t>>;
template <class T>
concept MutableVectorLike = std::is_convertible_v<T, std::span<uint8_t>>;
template <typename T>
concept PackBytes = StringLike<T> || ConstVectorLike<T>;
template <typename T>
concept UnpackBytes = requires(T& t) {
{ t.resize(size_t()) }; // NOLINT
{ t.size() } -> std::same_as<size_t>;
{ t.data() } -> std::convertible_to<void*>;
} && (PackBytes<T> || MutableVectorLike<T>);
template <typename T>
concept ProtoEnumeration = std::is_enum_v<T>;
template <typename T>
concept ProtoPackable =
ProtoEnumeration<T> || std::integral<T> || std::floating_point<T>;
template <typename T>
concept ProtoCallbackPackable =
ProtobufSerializable<T> || PackBytes<T> || ProtoPackable<T>;
template <typename T>
concept ProtoCallbackUnpackable =
ProtobufSerializable<T> || UnpackBytes<T> || ProtoPackable<T>;
namespace detail {
template <typename T>
concept Validatable = ProtoCallbackPackable<T> || ProtoCallbackUnpackable<T>;
template <Validatable T>
constexpr bool ValidateType(pb_type_t type) {
switch (type) {
case PB_LTYPE_BOOL:
return std::integral<T>;
case PB_LTYPE_VARINT:
return std::signed_integral<T> || ProtoEnumeration<T>;
case PB_LTYPE_UVARINT:
return std::unsigned_integral<T>;
case PB_LTYPE_SVARINT:
return std::signed_integral<T>;
case PB_LTYPE_FIXED32:
return std::integral<T> || std::floating_point<T>;
case PB_LTYPE_FIXED64:
return std::integral<T> || std::floating_point<T>;
case PB_LTYPE_BYTES:
case PB_LTYPE_STRING:
return PackBytes<T> || UnpackBytes<T>;
case PB_LTYPE_SUBMESSAGE:
return ProtobufSerializable<T>;
default:
return false;
}
}
} // namespace detail
/**
* A callback method that will directly unpack elements into
* the specified vector like data structure. The size passed
* is the expected number of elements.
*
* By default, any elements in the packed buffer past N will
* still be added to the vector.
*
* @tparam T object type
* @tparam U vector type to pack into
* @tparam N number of elements
*/
template <ProtoCallbackUnpackable T, typename U, size_t N = 1>
class DirectUnpackCallback {
public:
/**
* Constructs a callback from a vector like type.
*
* @param storage the vector to store into
*/
explicit DirectUnpackCallback(U& storage) : m_storage{storage} {
m_callback.funcs.decode = CallbackFunc;
m_callback.arg = this;
}
DirectUnpackCallback(const DirectUnpackCallback&) = delete;
DirectUnpackCallback(DirectUnpackCallback&&) = delete;
DirectUnpackCallback& operator=(const DirectUnpackCallback&) = delete;
DirectUnpackCallback& operator=(DirectUnpackCallback&&) = delete;
/**
* Set the limits on what happens if more elements exist in the buffer then
* expected.
*
* @param limit the limit to set
*/
void SetLimits(DecodeLimits limit) noexcept { m_limits = limit; }
/**
* Gets the nanopb callback pointing to this object.
*
* @return nanopb callback
*/
pb_callback_t Callback() const { return m_callback; }
private:
bool SizeCheck(bool* retVal) const {
if (m_storage.size() >= N) {
switch (m_limits) {
case DecodeLimits::Ignore:
*retVal = true;
return false;
case DecodeLimits::Add:
break;
default:
*retVal = false;
return false;
}
}
return true;
}
bool Decode(pb_istream_t* stream, pb_type_t fieldType) {
if constexpr (ProtoPackable<T>) {
switch (fieldType) {
case PB_LTYPE_BOOL:
if constexpr (std::integral<T>) {
bool val = false;
if (!pb_decode_bool(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else {
return false;
}
case PB_LTYPE_VARINT:
if constexpr (std::signed_integral<T> || ProtoEnumeration<T>) {
int64_t val = 0;
if (!pb_decode_varint(stream, reinterpret_cast<uint64_t*>(&val))) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else {
return false;
}
case PB_LTYPE_UVARINT:
if constexpr (std::unsigned_integral<T>) {
uint64_t val = 0;
if (!pb_decode_varint(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else {
return false;
}
case PB_LTYPE_SVARINT:
if constexpr (std::signed_integral<T>) {
int64_t val = 0;
if (!pb_decode_svarint(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else {
return false;
}
case PB_LTYPE_FIXED32:
if constexpr (std::signed_integral<T>) {
int32_t val = 0;
if (!pb_decode_fixed32(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else if constexpr (std::unsigned_integral<T>) {
uint32_t val = 0;
if (!pb_decode_fixed32(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
}
if constexpr (std::floating_point<T>) {
float val = 0;
if (!pb_decode_fixed32(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else {
return false;
}
case PB_LTYPE_FIXED64:
if constexpr (std::signed_integral<T>) {
int64_t val = 0;
if (!pb_decode_fixed64(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else if constexpr (std::unsigned_integral<T>) {
uint64_t val = 0;
if (!pb_decode_fixed64(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
}
if constexpr (std::floating_point<T>) {
double val = 0;
if (!pb_decode_fixed64(stream, &val)) {
return false;
}
m_storage.emplace_back(static_cast<T>(val));
return true;
} else {
return false;
}
default:
return false;
}
} else if constexpr (UnpackBytes<T>) {
T& space = m_storage.emplace_back(T{});
space.resize(stream->bytes_left);
return pb_read(stream, reinterpret_cast<pb_byte_t*>(space.data()),
space.size());
} else if constexpr (ProtobufSerializable<T>) {
ProtoInputStream<T> istream{stream};
auto decoded = wpi::Protobuf<T>::Unpack(istream);
if (decoded.has_value()) {
m_storage.emplace_back(std::move(decoded.value()));
return true;
}
return false;
}
}
bool CallbackFunc(pb_istream_t* stream, const pb_field_t* field) {
pb_type_t fieldType = PB_LTYPE(field->type);
if (!detail::ValidateType<T>(fieldType)) {
return false;
}
// Validate our types
if constexpr (ProtoPackable<T>) {
// Handle decode loop
while (stream->bytes_left > 0) {
bool sizeRetVal = 0;
if (!SizeCheck(&sizeRetVal)) {
return sizeRetVal;
}
if (!Decode(stream, fieldType)) {
return false;
}
}
return true;
} else {
// At this point, do the size check
bool sizeRetVal = 0;
if (!SizeCheck(&sizeRetVal)) {
return sizeRetVal;
}
// At this point, we're good to decode
return Decode(stream, fieldType);
}
}
static bool CallbackFunc(pb_istream_t* stream, const pb_field_t* field,
void** arg) {
return reinterpret_cast<DirectUnpackCallback*>(*arg)->CallbackFunc(stream,
field);
}
U& m_storage;
pb_callback_t m_callback;
DecodeLimits m_limits{DecodeLimits::Add};
};
/**
* A DirectUnpackCallback backed by a SmallVector<T, N>.
*
* By default, any elements in the packed buffer past N will
* be ignored, but decoding will still succeed
*
* @tparam T object type
* @tparam N small vector small size/number of expected elements
*/
template <ProtoCallbackUnpackable T, size_t N = 1>
class UnpackCallback
: public DirectUnpackCallback<T, wpi::SmallVector<T, N>, N> {
public:
/**
* Constructs an UnpackCallback.
*/
UnpackCallback()
: DirectUnpackCallback<T, wpi::SmallVector<T, N>, N>{m_storedBuffer} {
this->SetLimits(DecodeLimits::Ignore);
}
/**
* Gets a span pointing to the storage buffer.
*
* @return storage buffer span
*/
std::span<T> Items() noexcept { return m_storedBuffer; }
/**
* Gets a const span pointing to the storage buffer.
*
* @return storage buffer span
*/
std::span<const T> Items() const noexcept { return m_storedBuffer; }
/**
* Gets a reference to the backing small vector.
*
* @return small vector reference
*/
wpi::SmallVector<T, N>& Vec() noexcept { return m_storedBuffer; }
private:
wpi::SmallVector<T, N> m_storedBuffer;
};
/**
* A DirectUnpackCallback backed by a std::vector.
*
* By default, any elements in the packed buffer past N will
* be ignored, but decoding will still succeed
*
* @tparam T object type
* @tparam N number of expected elements
*/
template <ProtoCallbackUnpackable T, size_t N = 1>
class StdVectorUnpackCallback
: public DirectUnpackCallback<T, std::vector<T>, N> {
public:
/**
* Constructs a StdVectorUnpackCallback.
*/
StdVectorUnpackCallback()
: DirectUnpackCallback<T, std::vector<T>, N>{m_storedBuffer} {
this->SetLimits(DecodeLimits::Ignore);
}
/**
* Gets a span pointing to the storage buffer.
*
* @return storage buffer span
*/
std::span<T> Items() noexcept { return m_storedBuffer; }
/**
* Gets a const span pointing to the storage buffer.
*
* @return storage buffer span
*/
std::span<const T> Items() const noexcept { return m_storedBuffer; }
/**
* Gets a reference to the backing vector.
*
* @return vector reference
*/
std::vector<T>& Vec() noexcept { return m_storedBuffer; }
private:
std::vector<T> m_storedBuffer;
};
/**
* A wrapper around a wpi::array that lets us
* treat it as a limited sized vector.
*/
template <ProtoCallbackUnpackable T, size_t N>
struct WpiArrayEmplaceWrapper {
wpi::array<T, N> m_array{wpi::empty_array_t{}};
size_t m_currentIndex = 0;
size_t size() const { return m_currentIndex; }
template <typename... ArgTypes>
T& emplace_back(ArgTypes&&... Args) {
m_array[m_currentIndex] = T(std::forward<ArgTypes>(Args)...);
m_currentIndex++;
return m_array[m_currentIndex - 1];
}
};
/**
* A DirectUnpackCallback backed by a wpi::array<T, N>.
*
* Any elements in the packed buffer past N will
* be cause decoding to fail.
*
* @tparam T object type
* @tparam N small vector small size/number of expected elements
*/
template <ProtoCallbackUnpackable T, size_t N>
struct WpiArrayUnpackCallback
: public DirectUnpackCallback<T, WpiArrayEmplaceWrapper<T, N>, N> {
/**
* Constructs a WpiArrayUnpackCallback.
*/
WpiArrayUnpackCallback()
: DirectUnpackCallback<T, WpiArrayEmplaceWrapper<T, N>, N>{m_array} {
this->SetLimits(DecodeLimits::Fail);
}
/**
* Returns if the buffer is completely filled up.
*
* @return true if buffer is full
*/
bool IsFull() const noexcept { return m_array.m_currentIndex == N; }
/**
* Returns the number of elements in the buffer.
*
* @return number of elements
*/
size_t Size() const noexcept { return m_array.m_currentIndex; }
/**
* Returns a reference to the backing array.
*
* @return array reference
*/
wpi::array<T, N>& Array() noexcept { return m_array.m_array; }
private:
WpiArrayEmplaceWrapper<T, N> m_array;
};
/**
* A callback method that will pack elements when called.
*
* @tparam T object type
*/
template <ProtoCallbackPackable T>
class PackCallback {
public:
/**
* Constructs a pack callback from a span of elements. The elements in the
* buffer _MUST_ stay alive throughout the entire encode call.
*/
explicit PackCallback(std::span<const T> buffer) : m_buffer{buffer} {
m_callback.funcs.encode = CallbackFunc;
m_callback.arg = this;
}
/**
* Constructs a pack callback from a pointer to a single element.
* This element _MUST_ stay alive throughout the entire encode call.
* Do not pass a temporary here (This is why its a pointer and not a
* reference)
*/
explicit PackCallback(const T* element)
: m_buffer{std::span<const T>{element, 1}} {
m_callback.funcs.encode = CallbackFunc;
m_callback.arg = this;
}
PackCallback(const PackCallback&) = delete;
PackCallback(PackCallback&&) = delete;
PackCallback& operator=(const PackCallback&) = delete;
PackCallback& operator=(PackCallback&&) = delete;
/**
* Gets the nanopb callback pointing to this object.
*
* @return nanopb callback
*/
pb_callback_t Callback() const { return m_callback; }
/**
* Gets a span pointing to the items
*
* @return span
*/
std::span<const T> Bufs() const { return m_buffer; }
private:
static auto EncodeStreamTypeFinder() {
if constexpr (ProtobufSerializable<T>) {
return ProtoOutputStream<T>(nullptr);
} else {
return pb_ostream_t{};
}
}
using EncodeStreamType = decltype(EncodeStreamTypeFinder());
bool EncodeItem(EncodeStreamType& stream, const pb_field_t* field,
const T& value) const {
if constexpr (std::floating_point<T>) {
pb_type_t fieldType = PB_LTYPE(field->type);
switch (fieldType) {
case PB_LTYPE_FIXED32: {
float flt = static_cast<float>(value);
return pb_encode_fixed32(&stream, &flt);
}
case PB_LTYPE_FIXED64: {
double dbl = static_cast<double>(value);
return pb_encode_fixed64(&stream, &dbl);
}
default:
return false;
}
} else if constexpr (std::integral<T> || ProtoEnumeration<T>) {
pb_type_t fieldType = PB_LTYPE(field->type);
switch (fieldType) {
case PB_LTYPE_BOOL:
case PB_LTYPE_VARINT:
case PB_LTYPE_UVARINT:
return pb_encode_varint(&stream, value);
case PB_LTYPE_SVARINT:
return pb_encode_svarint(&stream, value);
case PB_LTYPE_FIXED32: {
uint32_t f = value;
return pb_encode_fixed32(&stream, &f);
}
case PB_LTYPE_FIXED64: {
uint64_t f = value;
return pb_encode_fixed64(&stream, &f);
}
default:
return false;
}
} else if constexpr (StringLike<T>) {
std::string_view view{value};
return pb_encode_string(&stream,
reinterpret_cast<const pb_byte_t*>(view.data()),
view.size());
} else if constexpr (ConstVectorLike<T>) {
std::span<const uint8_t> view{value};
return pb_encode_string(&stream,
reinterpret_cast<const pb_byte_t*>(view.data()),
view.size());
} else if constexpr (ProtobufSerializable<T>) {
return wpi::Protobuf<T>::Pack(stream, value);
}
}
bool EncodeLoop(pb_ostream_t* stream, const pb_field_t* field,
bool writeTag) const {
if constexpr (ProtobufSerializable<T>) {
ProtoOutputStream<T> ostream{stream};
for (auto&& i : m_buffer) {
if (writeTag) {
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
}
if (!EncodeItem(ostream, field, i)) {
return false;
}
}
} else {
for (auto&& i : m_buffer) {
if (writeTag) {
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
}
if (!EncodeItem(*stream, field, i)) {
return false;
}
}
}
return true;
}
bool PackedEncode(pb_ostream_t* stream, const pb_field_t* field) const {
// We're always going to used packed encoding.
// So first we need to get the packed size.
pb_ostream_t substream = PB_OSTREAM_SIZING;
if (!EncodeLoop(&substream, field, false)) {
return false;
}
// Encode as a string tag
if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) {
return false;
}
// Write length as varint
size_t size = substream.bytes_written;
if (!pb_encode_varint(stream, static_cast<uint64_t>(size))) {
return false;
}
return EncodeLoop(stream, field, false);
}
bool CallbackFunc(pb_ostream_t* stream, const pb_field_t* field) const {
// First off, if we're empty, do nothing, but say we were successful
if (m_buffer.empty()) {
return true;
}
pb_type_t fieldType = PB_LTYPE(field->type);
if (!detail::ValidateType<T>(fieldType)) {
return false;
}
if constexpr (ProtoPackable<T>) {
return PackedEncode(stream, field);
} else {
return EncodeLoop(stream, field, true);
}
}
static bool CallbackFunc(pb_ostream_t* stream, const pb_field_t* field,
void* const* arg) {
return reinterpret_cast<const PackCallback*>(*arg)->CallbackFunc(stream,
field);
}
std::span<const T> m_buffer;
pb_callback_t m_callback;
};
} // namespace wpi

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env python3
# Allow calling nanopb_generator.py as simply nanopb_generator.
# This provides consistency with packages installed through CMake or pip.
from nanopb_generator import *
if __name__ == '__main__':
main_cli()

View File

@@ -0,0 +1,5 @@
@echo off
:: Allow calling nanopb_generator.py as simply nanopb_generator.
:: This provides consistency with packages installed through CMake or pip.
set mydir=%~dp0
python "%mydir%\nanopb_generator.py" %*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python2
# This file is a wrapper around nanopb_generator.py in case you want to run
# it with Python 2 instead of default Python 3. This only exists for backwards
# compatibility, do not use for new projects.
from nanopb_generator import *
if __name__ == '__main__':
# Check if we are running as a plugin under protoc
if 'protoc-gen-' in sys.argv[0] or '--protoc-plugin' in sys.argv:
main_plugin()
else:
main_cli()

View File

@@ -0,0 +1,157 @@
import os
import hashlib
import pathlib
import shlex
import subprocess
import SCons.Action
from platformio import fs
Import("env")
# We don't use `env.Execute` because it does not handle spaces in path
# See https://github.com/nanopb/nanopb/pull/834
# So, we resolve the path to the executable and then use `subprocess.run`
python_exe = env.subst("$PYTHONEXE")
try:
import google.protobuf
except ImportError:
print("[nanopb] Installing Protocol Buffers dependencies");
# We need to specify protobuf version. In other case got next (on Ubuntu 20.04):
# Requirement already satisfied: protobuf in /usr/lib/python3/dist-packages (3.6.1)
subprocess.run([python_exe, '-m', 'pip', 'install', "protobuf>=3.19.1"])
try:
import grpc_tools.protoc
except ImportError:
print("[nanopb] Installing gRPC dependencies");
subprocess.run([python_exe, '-m', 'pip', 'install', "grpcio-tools>=1.43.0"])
nanopb_root = os.path.join(os.getcwd(), '..')
project_dir = env.subst("$PROJECT_DIR")
build_dir = env.subst("$BUILD_DIR")
generated_src_dir = os.path.join(build_dir, 'nanopb', 'generated-src')
generated_build_dir = os.path.join(build_dir, 'nanopb', 'generated-build')
md5_dir = os.path.join(build_dir, 'nanopb', 'md5')
nanopb_protos = env.subst(env.GetProjectOption("custom_nanopb_protos", ""))
nanopb_plugin_options = env.GetProjectOption("custom_nanopb_options", "")
if not nanopb_protos:
print("[nanopb] No generation needed.")
else:
if isinstance(nanopb_plugin_options, (list, tuple)):
nanopb_plugin_options = " ".join(nanopb_plugin_options)
nanopb_plugin_options = shlex.split(nanopb_plugin_options)
protos_files = fs.match_src_files(project_dir, nanopb_protos)
if not len(protos_files):
print("[nanopb] ERROR: No files matched pattern:")
print(f"custom_nanopb_protos: {nanopb_protos}")
exit(1)
nanopb_generator = os.path.join(nanopb_root, 'generator', 'nanopb_generator.py')
nanopb_options = []
nanopb_options.extend(["--output-dir", generated_src_dir])
for opt in nanopb_plugin_options:
nanopb_options.append(opt)
try:
os.makedirs(generated_src_dir)
except FileExistsError:
pass
try:
os.makedirs(md5_dir)
except FileExistsError:
pass
# Collect include dirs based on
proto_include_dirs = set()
for proto_file in protos_files:
proto_file_abs = os.path.join(project_dir, proto_file)
proto_dir = os.path.dirname(proto_file_abs)
proto_include_dirs.add(proto_dir)
for proto_include_dir in proto_include_dirs:
nanopb_options.extend(["--proto-path", proto_include_dir])
for proto_file in protos_files:
proto_file_abs = os.path.join(project_dir, proto_file)
proto_file_path_abs = os.path.dirname(proto_file_abs)
proto_file_basename = os.path.basename(proto_file_abs)
proto_file_without_ext = os.path.splitext(proto_file_basename)[0]
proto_file_md5_abs = os.path.join(md5_dir, proto_file_basename + '.md5')
proto_file_current_md5 = hashlib.md5(pathlib.Path(proto_file_abs).read_bytes()).hexdigest()
options_file = proto_file_without_ext + ".options"
options_file_abs = os.path.join(proto_file_path_abs, options_file)
options_file_md5_abs = None
options_file_current_md5 = None
if pathlib.Path(options_file_abs).exists():
options_file_md5_abs = os.path.join(md5_dir, options_file + '.md5')
options_file_current_md5 = hashlib.md5(pathlib.Path(options_file_abs).read_bytes()).hexdigest()
else:
options_file = None
header_file = proto_file_without_ext + ".pb.h"
source_file = proto_file_without_ext + ".pb.c"
header_file_abs = os.path.join(generated_src_dir, source_file)
source_file_abs = os.path.join(generated_src_dir, header_file)
need_generate = False
# Check proto file md5
try:
last_md5 = pathlib.Path(proto_file_md5_abs).read_text()
if last_md5 != proto_file_current_md5:
need_generate = True
except FileNotFoundError:
need_generate = True
if options_file:
# Check options file md5
try:
last_md5 = pathlib.Path(options_file_md5_abs).read_text()
if last_md5 != options_file_current_md5:
need_generate = True
except FileNotFoundError:
need_generate = True
options_info = f"{options_file}" if options_file else "no options"
if not need_generate:
print(f"[nanopb] Skipping '{proto_file}' ({options_info})")
else:
print(f"[nanopb] Processing '{proto_file}' ({options_info})")
cmd = [python_exe, nanopb_generator] + nanopb_options + [proto_file_basename]
action = SCons.Action.CommandAction(cmd)
result = env.Execute(action)
if result != 0:
print(f"[nanopb] ERROR: ({result}) processing cmd: '{cmd}'")
exit(1)
pathlib.Path(proto_file_md5_abs).write_text(proto_file_current_md5)
if options_file:
pathlib.Path(options_file_md5_abs).write_text(options_file_current_md5)
#
# Add generated includes and sources to build environment
#
env.Append(CPPPATH=[generated_src_dir])
# Fix for ESP32 ESP-IDF https://github.com/nanopb/nanopb/issues/734#issuecomment-1001544447
global_env = DefaultEnvironment()
already_called_env_name = "_PROTOBUF_GENERATOR_ALREADY_CALLED_" + env['PIOENV'].replace("-", "_")
if not global_env.get(already_called_env_name, False):
env.BuildSources(generated_build_dir, generated_src_dir)
global_env[already_called_env_name] = True

View File

@@ -0,0 +1,10 @@
PROTOC?=../protoc
all: nanopb_pb2.py
%_pb2.py: %.proto
$(PROTOC) --python_out=. $<
.PHONY: clean
clean:
rm nanopb_pb2.py

View File

@@ -0,0 +1,126 @@
'''This file dynamically builds the proto definitions for Python.'''
from __future__ import absolute_import
import os
import os.path
import sys
import tempfile
import shutil
import traceback
from ._utils import has_grpcio_protoc, invoke_protoc, print_versions
# Compatibility layer to make TemporaryDirectory() available on Python 2.
try:
from tempfile import TemporaryDirectory
except ImportError:
class TemporaryDirectory:
'''TemporaryDirectory fallback for Python 2'''
def __init__(self, prefix = 'tmp', dir = None):
self.prefix = prefix
self.dir = dir
def __enter__(self):
self.dir = tempfile.mkdtemp(prefix = self.prefix, dir = self.dir)
return self.dir
def __exit__(self, *args):
shutil.rmtree(self.dir)
def build_nanopb_proto(protosrc, dirname):
'''Try to build a .proto file for python-protobuf.
Returns True if successful.
'''
cmd = [
"protoc",
"--python_out={}".format(dirname),
protosrc,
"-I={}".format(dirname),
]
if has_grpcio_protoc():
# grpcio-tools has an extra CLI argument
# from grpc.tools.protoc __main__ invocation.
cmd.append("-I={}".format(_utils.get_grpc_tools_proto_path()))
try:
invoke_protoc(argv=cmd)
except:
sys.stderr.write("Failed to build nanopb_pb2.py: " + ' '.join(cmd) + "\n")
sys.stderr.write(traceback.format_exc() + "\n")
return False
return True
def load_nanopb_pb2():
# To work, the generator needs python-protobuf built version of nanopb.proto.
# There are three methods to provide this:
#
# 1) Load a previously generated generator/proto/nanopb_pb2.py
# 2) Use protoc to build it and store it permanently generator/proto/nanopb_pb2.py
# 3) Use protoc to build it, but store only temporarily in system-wide temp folder
#
# By default these are tried in numeric order.
# If NANOPB_PB2_TEMP_DIR environment variable is defined, the 2) is skipped.
# If the value of the $NANOPB_PB2_TEMP_DIR exists as a directory, it is used instead
# of system temp folder.
tmpdir = os.getenv("NANOPB_PB2_TEMP_DIR")
temporary_only = (tmpdir is not None)
dirname = os.path.dirname(__file__)
protosrc = os.path.join(dirname, "nanopb.proto")
protodst = os.path.join(dirname, "nanopb_pb2.py")
if tmpdir is not None and not os.path.isdir(tmpdir):
tmpdir = None # Use system-wide temp dir
no_rebuild = bool(int(os.getenv("NANOPB_PB2_NO_REBUILD", default = 0)))
if bool(no_rebuild):
# Don't attempt to autogenerate nanopb_pb2.py, external build rules
# should have already done so.
import nanopb_pb2 as nanopb_pb2_mod
return nanopb_pb2_mod
if os.path.isfile(protosrc):
src_date = os.path.getmtime(protosrc)
if os.path.isfile(protodst) and os.path.getmtime(protodst) >= src_date:
try:
from . import nanopb_pb2 as nanopb_pb2_mod
return nanopb_pb2_mod
except Exception as e:
sys.stderr.write("Failed to import nanopb_pb2.py: " + str(e) + "\n"
"Will automatically attempt to rebuild this.\n"
"Verify that python-protobuf and protoc versions match.\n")
print_versions()
# Try to rebuild into generator/proto directory
if not temporary_only:
build_nanopb_proto(protosrc, dirname)
try:
from . import nanopb_pb2 as nanopb_pb2_mod
return nanopb_pb2_mod
except:
sys.stderr.write("Failed to import generator/proto/nanopb_pb2.py:\n")
sys.stderr.write(traceback.format_exc() + "\n")
# Try to rebuild into temporary directory
with TemporaryDirectory(prefix = 'nanopb-', dir = tmpdir) as protodir:
build_nanopb_proto(protosrc, protodir)
if protodir not in sys.path:
sys.path.insert(0, protodir)
try:
import nanopb_pb2 as nanopb_pb2_mod
return nanopb_pb2_mod
except:
sys.stderr.write("Failed to import %s/nanopb_pb2.py:\n" % protodir)
sys.stderr.write(traceback.format_exc() + "\n")
# If everything fails
sys.stderr.write("\n\nGenerating nanopb_pb2.py failed.\n")
sys.stderr.write("Make sure that a protoc generator is available and matches python-protobuf version.\n")
print_versions()
sys.exit(1)

View File

@@ -0,0 +1,104 @@
import sys
import subprocess
import os.path
import traceback
def has_grpcio_protoc(verbose = False):
# type: () -> bool
""" checks if grpcio-tools protoc is installed"""
try:
import grpc_tools.protoc
except ImportError:
if verbose:
sys.stderr.write("Failed to import grpc_tools: %s\n" % traceback.format_exc())
return False
return True
def get_grpc_tools_proto_path():
if sys.hexversion > 0x03090000:
import importlib.resources as ir
with ir.as_file(ir.files('grpc_tools') / '_proto') as path:
return str(path)
else:
import pkg_resources
return pkg_resources.resource_filename('grpc_tools', '_proto')
def get_proto_builtin_include_path():
"""Find include path for standard google/protobuf includes and for
nanopb.proto.
"""
if getattr(sys, 'frozen', False):
# Pyinstaller package
paths = [
os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'proto'),
os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'grpc_tools', '_proto')
]
else:
# Stand-alone script
paths = [
os.path.dirname(os.path.abspath(__file__))
]
if has_grpcio_protoc():
paths.append(get_grpc_tools_proto_path())
return paths
def invoke_protoc(argv):
# type: (list) -> typing.Any
"""
Invoke protoc.
This routine will use grpcio-provided protoc if it exists,
using system-installed protoc as a fallback.
Args:
argv: protoc CLI invocation, first item must be 'protoc'
"""
# Add current directory to include path if nothing else is specified
if not [x for x in argv if x.startswith('-I')]:
argv.append("-I.")
# Add default protoc include paths
for incpath in get_proto_builtin_include_path():
argv.append('-I' + incpath)
if has_grpcio_protoc():
import grpc_tools.protoc as protoc
return protoc.main(argv)
else:
return subprocess.call(argv)
def print_versions():
try:
if has_grpcio_protoc(verbose = True):
import grpc_tools.protoc
sys.stderr.write("Using grpcio-tools protoc from " + grpc_tools.protoc.__file__ + "\n")
else:
sys.stderr.write("Using protoc from system path\n")
invoke_protoc(['protoc', '--version'])
except Exception as e:
sys.stderr.write("Failed to determine protoc version: " + str(e) + "\n")
try:
sys.stderr.write("protoc builtin include path: " + str(get_proto_builtin_include_path()) + "\n")
except Exception as e:
sys.stderr.write("Failed to construct protoc include path: " + str(e) + "\n")
try:
import google.protobuf
sys.stderr.write("Python version " + sys.version + "\n")
sys.stderr.write("Using python-protobuf from " + google.protobuf.__file__ + "\n")
sys.stderr.write("Python-protobuf version: " + google.protobuf.__version__ + "\n")
except Exception as e:
sys.stderr.write("Failed to determine python-protobuf version: " + str(e) + "\n")
if __name__ == '__main__':
print_versions()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
// This file contains definitions of custom options used to control the
// code generator in nanopb protocol buffers library.
//
// Most commonly used options are max_count and max_size, which allow
// the generator to allocate static arrays for repeated and string fields.
//
// There are three ways to use these options:
// 1. Use a separate <protofile>.options file
// 2. Use command line switches to nanopb_generator.py
// 3. Use [(nanopb).option = value] in your <protofile>.proto file
//
// For detailed documentation, refer to "Generator options" in docs/reference.md
syntax = "proto2";
import "google/protobuf/descriptor.proto";
option java_package = "fi.kapsi.koti.jpa.nanopb";
enum FieldType {
FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible.
FT_CALLBACK = 1; // Always generate a callback field.
FT_POINTER = 4; // Always generate a dynamically allocated field.
FT_STATIC = 2; // Generate a static field or raise an exception if not possible.
FT_IGNORE = 3; // Ignore the field completely.
FT_INLINE = 5; // Legacy option, use the separate 'fixed_length' option instead
}
enum IntSize {
IS_DEFAULT = 0; // Default, 32/64bit based on type in .proto
IS_8 = 8;
IS_16 = 16;
IS_32 = 32;
IS_64 = 64;
}
enum TypenameMangling {
M_NONE = 0; // Default, no typename mangling
M_STRIP_PACKAGE = 1; // Strip current package name
M_FLATTEN = 2; // Only use last path component
M_PACKAGE_INITIALS = 3; // Replace the package name by the initials
}
enum DescriptorSize {
DS_AUTO = 0; // Select minimal size based on field type
DS_1 = 1; // 1 word; up to 15 byte fields, no arrays
DS_2 = 2; // 2 words; up to 4095 byte fields, 4095 entry arrays
DS_4 = 4; // 4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays
DS_8 = 8; // 8 words; up to 2^32-1 entry arrays
}
// This is the inner options message, which basically defines options for
// a field. When it is used in message or file scope, it applies to all
// fields.
message NanoPBOptions {
// Allocated size for 'bytes' and 'string' fields.
// For string fields, this should include the space for null terminator.
optional int32 max_size = 1;
// Maximum length for 'string' fields. Setting this is equivalent
// to setting max_size to a value of length+1.
optional int32 max_length = 14;
// Allocated number of entries in arrays ('repeated' fields)
optional int32 max_count = 2;
// Size of integer fields. Can save some memory if you don't need
// full 32 bits for the value.
optional IntSize int_size = 7 [default = IS_DEFAULT];
// Size for enum fields. Supported by C++11 and C23 standards.
optional IntSize enum_intsize = 34 [default = IS_DEFAULT];
// Force type of field (callback or static allocation)
optional FieldType type = 3 [default = FT_DEFAULT];
// Use long names for enums, i.e. EnumName_EnumValue.
optional bool long_names = 4 [default = true];
// Add 'packed' attribute to generated structs.
// Note: this cannot be used on CPUs that break on unaligned
// accesses to variables.
optional bool packed_struct = 5 [default = false];
// Add 'packed' attribute to generated enums.
optional bool packed_enum = 10 [default = false];
// Skip this message
optional bool skip_message = 6 [default = false];
// Generate oneof fields as normal optional fields instead of union.
optional bool no_unions = 8 [default = false];
// integer type tag for a message
optional uint32 msgid = 9;
// decode oneof as anonymous union
optional bool anonymous_oneof = 11 [default = false];
// Proto3 singular field does not generate a "has_" flag
optional bool proto3 = 12 [default = false];
// Force proto3 messages to have no "has_" flag.
// This was default behavior until nanopb-0.4.0.
optional bool proto3_singular_msgs = 21 [default = false];
// Generate an enum->string mapping function (can take up lots of space).
optional bool enum_to_string = 13 [default = false];
// Generate validation methods for enums
optional bool enum_validate = 32 [default = false];
// Generate bytes arrays with fixed length
optional bool fixed_length = 15 [default = false];
// Generate repeated field with fixed count
optional bool fixed_count = 16 [default = false];
// Generate message-level callback that is called before decoding submessages.
// This can be used to set callback fields for submsgs inside oneofs.
optional bool submsg_callback = 22 [default = false];
// Shorten or remove package names from type names.
// This option applies only on the file level.
optional TypenameMangling mangle_names = 17 [default = M_NONE];
// Data type for storage associated with callback fields.
optional string callback_datatype = 18 [default = "pb_callback_t"];
// Callback function used for encoding and decoding.
// Prior to nanopb-0.4.0, the callback was specified in per-field pb_callback_t
// structure. This is still supported, but does not work inside e.g. oneof or pointer
// fields. Instead, a new method allows specifying a per-message callback that
// will be called for all callback fields in a message type.
optional string callback_function = 19 [default = "pb_default_field_callback"];
// Select the size of field descriptors. This option has to be defined
// for the whole message, not per-field. Usually automatic selection is
// ok, but if it results in compilation errors you can increase the field
// size here.
optional DescriptorSize descriptorsize = 20 [default = DS_AUTO];
// Set default value for has_ fields.
optional bool default_has = 23 [default = false];
// Extra files to include in generated `.pb.h`
repeated string include = 24;
// Automatic includes to exclude from generated `.pb.h`
// Same as nanopb_generator.py command line flag -x.
repeated string exclude = 26;
// Package name that applies only for nanopb.
optional string package = 25;
// Override type of the field in generated C code. Only to be used with related field types
optional google.protobuf.FieldDescriptorProto.Type type_override = 27;
// Override of the label of the field (see FieldDescriptorProto.Label). Can be used to create
// fields which nanopb considers required in proto3, or whether nanopb treats the field as
// optional/required/repeated.
optional google.protobuf.FieldDescriptorProto.Label label_override = 31;
// Due to historical reasons, nanopb orders fields in structs by their tag number
// instead of the order in .proto. Set this to false to keep the .proto order.
// The default value will probably change to false in nanopb-0.5.0.
optional bool sort_by_tag = 28 [default = true];
// Set the FT_DEFAULT field conversion strategy.
// A field that can become a static member of a c struct (e.g. int, bool, etc)
// will be a a static field.
// Fields with dynamic length are converted to either a pointer or a callback.
optional FieldType fallback_type = 29 [default = FT_CALLBACK];
// Override initializer used in generated MyMessage_init_zero and MyMessage_init_default macros
// By default decided automatically based on field default value and datatype.
optional string initializer = 30;
// Discard unused types that are automatically generated by protoc if they are not actually
// needed. Currently this applies to map< > types when the field is ignored by options.
optional bool discard_unused_automatic_types = 33 [default = true];
// Discard messages and fields marked with [deprecated = true] in the proto file.
optional bool discard_deprecated = 35 [default = false];
}
// Extensions to protoc 'Descriptor' type in order to define options
// inside a .proto file.
//
// Protocol Buffers extension number registry
// --------------------------------
// Project: Nanopb
// Contact: Petteri Aimonen <jpa@kapsi.fi>
// Web site: http://kapsi.fi/~jpa/nanopb
// Extensions: 1010 (all types)
// --------------------------------
extend google.protobuf.FileOptions {
optional NanoPBOptions nanopb_fileopt = 1010;
}
extend google.protobuf.MessageOptions {
optional NanoPBOptions nanopb_msgopt = 1010;
}
extend google.protobuf.EnumOptions {
optional NanoPBOptions nanopb_enumopt = 1010;
}
extend google.protobuf.FieldOptions {
optional NanoPBOptions nanopb = 1010;
}

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# This file acts as a drop-in replacement of binary protoc.exe.
# It will use either Python-based protoc from grpcio-tools package,
# or if it is not available, protoc.exe from path if found.
import sys
import os
import os.path
# Depending on how this script is run, we may or may not have PEP366 package name
# available for relative imports.
if not __package__:
from proto._utils import invoke_protoc
else:
from .proto._utils import invoke_protoc
if __name__ == '__main__':
# Get path of the directory where this script is stored.
if getattr(sys, 'frozen', False):
mypath = os.path.dirname(sys.executable) # For pyInstaller
else:
mypath = os.path.dirname(__file__)
# Avoid recursive calls to self
env_paths = os.environ["PATH"].split(os.pathsep)
if mypath in env_paths:
env_paths.remove(mypath)
os.environ["PATH"] = os.pathsep.join(env_paths)
# Add argument for finding the nanopb generator when using --nanopb_out=
# argument to protoc.
if os.path.isfile(os.path.join(mypath, "protoc-gen-nanopb.exe")):
protoc_gen_nanopb = os.path.join(mypath, "protoc-gen-nanopb.exe")
elif os.name == 'nt':
protoc_gen_nanopb = os.path.join(mypath, "protoc-gen-nanopb.bat")
else:
protoc_gen_nanopb = os.path.join(mypath, "protoc-gen-nanopb")
args = sys.argv[1:]
if os.path.isfile(protoc_gen_nanopb):
args = ['--plugin=protoc-gen-nanopb=%s' % protoc_gen_nanopb] + args
status = invoke_protoc(['protoc'] + args)
sys.exit(status)

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python3
# This file is used to invoke nanopb_generator.py as a plugin
# to protoc on Linux and other *nix-style systems.
# Use it like this:
# protoc --plugin=protoc-gen-nanopb=..../protoc-gen-nanopb --nanopb_out=dir foo.proto
from nanopb_generator import *
if __name__ == '__main__':
# Assume we are running as a plugin under protoc.
main_plugin()

View File

@@ -0,0 +1,16 @@
#!/bin/sh
# This file is used to invoke nanopb_generator.py2 as a plugin
# to protoc on Linux and other *nix-style systems.
#
# The difference from protoc-gen-nanopb is that this executes with Python 2.
#
# Use it like this:
# protoc --plugin=protoc-gen-nanopb=..../protoc-gen-nanopb-py2 --nanopb_out=dir foo.proto
#
# Note that if you use the binary package of nanopb, the protoc
# path is already set up properly and there is no need to give
# --plugin= on the command line.
MYPATH=$(dirname "$0")
exec "$MYPATH/nanopb_generator.py2" --protoc-plugin

View File

@@ -0,0 +1,12 @@
@echo off
:: This file is used to invoke nanopb_generator.py as a plugin
:: to protoc on Windows.
:: Use it like this:
:: protoc --plugin=protoc-gen-nanopb=..../protoc-gen-nanopb.bat --nanopb_out=dir foo.proto
::
:: Note that if you use the binary package of nanopb, the protoc
:: path is already set up properly and there is no need to give
:: --plugin= on the command line.
set mydir=%~dp0
python "%mydir%\nanopb_generator.py" --protoc-plugin %*

View File

@@ -0,0 +1,9 @@
@echo off
:: This file acts as a drop-in replacement of binary protoc.exe.
:: It will use either Python-based protoc from grpcio-tools package,
:: or if it is not available, protoc.exe from path if found.
setLocal enableDelayedExpansion
set mydir=%~dp0
python "%mydir%\protoc" %*
exit /b %ERRORLEVEL%

View File

@@ -0,0 +1,931 @@
/* Common parts of the nanopb library. Most of these are quite low-level
* stuff. For the high-level interface, see pb_encode.h and pb_decode.h.
*
* Modified for WPILib Use
*/
#ifndef PB_H_INCLUDED
#define PB_H_INCLUDED
/*****************************************************************
* Nanopb compilation time options. You can change these here by *
* uncommenting the lines, or on the compiler command line. *
*****************************************************************/
/* Enable support for dynamically allocated fields */
/* #define PB_ENABLE_MALLOC 1 */
/* Define this if your CPU / compiler combination does not support
* unaligned memory access to packed structures. Note that packed
* structures are only used when requested in .proto options. */
/* #define PB_NO_PACKED_STRUCTS 1 */
/* Increase the number of required fields that are tracked.
* A compiler warning will tell if you need this. */
/* #define PB_MAX_REQUIRED_FIELDS 256 */
/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */
/* #define PB_FIELD_32BIT 1 */
/* Disable support for error messages in order to save some code space. */
/* #define PB_NO_ERRMSG 1 */
/* Disable support for custom streams (support only memory buffers). */
/* #define PB_BUFFER_ONLY 1 */
/* Disable support for 64-bit datatypes, for compilers without int64_t
or to save some code space. */
/* #define PB_WITHOUT_64BIT 1 */
/* Don't encode scalar arrays as packed. This is only to be used when
* the decoder on the receiving side cannot process packed scalar arrays.
* Such example is older protobuf.js. */
/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */
/* Enable conversion of doubles to floats for platforms that do not
* support 64-bit doubles. Most commonly AVR. */
/* #define PB_CONVERT_DOUBLE_FLOAT 1 */
/* Check whether incoming strings are valid UTF-8 sequences. Slows down
* the string processing slightly and slightly increases code size. */
/* #define PB_VALIDATE_UTF8 1 */
/* This can be defined if the platform is little-endian and has 8-bit bytes.
* Normally it is automatically detected based on __BYTE_ORDER__ macro. */
/* #define PB_LITTLE_ENDIAN_8BIT 1 */
/* Configure static assert mechanism. Instead of changing these, set your
* compiler to C11 standard mode if possible. */
/* #define PB_C99_STATIC_ASSERT 1 */
/* #define PB_NO_STATIC_ASSERT 1 */
/******************************************************************
* You usually don't need to change anything below this line. *
* Feel free to look around and use the defined macros, though. *
******************************************************************/
/* Version of the nanopb library. Just in case you want to check it in
* your own program. */
#define NANOPB_VERSION "nanopb-0.4.9"
/* Include all the system headers needed by nanopb. You will need the
* definitions of the following:
* - strlen, memcpy, memset functions
* - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t
* - size_t
* - bool
*
* If you don't have the standard header files, you can instead provide
* a custom header that defines or includes all this. In that case,
* define PB_SYSTEM_HEADER to the path of this file.
*/
#ifdef PB_SYSTEM_HEADER
#include PB_SYSTEM_HEADER
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <span>
#include <string_view>
#ifdef PB_ENABLE_MALLOC
#include <stdlib.h>
#endif
#endif
/* Macro for defining packed structures (compiler dependent).
* This just reduces memory requirements, but is not required.
*/
#if defined(PB_NO_PACKED_STRUCTS)
/* Disable struct packing */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed
#elif defined(__GNUC__) || defined(__clang__)
/* For GCC and clang */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed __attribute__((packed))
#elif defined(__ICCARM__) || defined(__CC_ARM)
/* For IAR ARM and Keil MDK-ARM compilers */
# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
# define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
# define pb_packed
#elif defined(_MSC_VER) && (_MSC_VER >= 1500)
/* For Microsoft Visual C++ */
# define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
# define PB_PACKED_STRUCT_END __pragma(pack(pop))
# define pb_packed
#else
/* Unknown compiler */
# define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END
# define pb_packed
#endif
/* Detect endianness */
#ifndef PB_LITTLE_ENDIAN_8BIT
#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \
defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \
defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \
&& CHAR_BIT == 8
#define PB_LITTLE_ENDIAN_8BIT 1
#endif
#endif
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
#ifndef PB_UNUSED
#define PB_UNUSED(x) (void)(x)
#endif
/* Harvard-architecture processors may need special attributes for storing
* field information in program memory. */
#ifndef PB_PROGMEM
#ifdef __AVR__
#include <avr/pgmspace.h>
#define PB_PROGMEM PROGMEM
#define PB_PROGMEM_READU32(x) pgm_read_dword(&x)
#else
#define PB_PROGMEM
#define PB_PROGMEM_READU32(x) (x)
#endif
#endif
/* Compile-time assertion, used for checking compatible compilation options.
* If this does not work properly on your compiler, use
* #define PB_NO_STATIC_ASSERT to disable it.
*
* But before doing that, check carefully the error message / place where it
* comes from to see if the error has a real cause. Unfortunately the error
* message is not always very clear to read, but you can see the reason better
* in the place where the PB_STATIC_ASSERT macro was called.
*/
#ifndef PB_NO_STATIC_ASSERT
# ifndef PB_STATIC_ASSERT
# if defined(__ICCARM__)
/* IAR has static_assert keyword but no _Static_assert */
# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG);
# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112)
/* MSVC in C89 mode supports static_assert() keyword anyway */
# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG);
# elif defined(PB_C99_STATIC_ASSERT)
/* Classic negative-size-array static assert mechanism */
# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER
# elif defined(__cplusplus)
/* C++11 standard static_assert mechanism */
# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG);
# else
/* C11 standard _Static_assert mechanism */
# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
# endif
# endif
#else
/* Static asserts disabled by PB_NO_STATIC_ASSERT */
# define PB_STATIC_ASSERT(COND,MSG)
#endif
/* Test that PB_STATIC_ASSERT works
* If you get errors here, you may need to do one of these:
* - Enable C11 standard support in your compiler
* - Define PB_C99_STATIC_ASSERT to enable C99 standard support
* - Define PB_NO_STATIC_ASSERT to disable static asserts altogether
*/
PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING)
/* Number of required fields to keep track of. */
#ifndef PB_MAX_REQUIRED_FIELDS
#define PB_MAX_REQUIRED_FIELDS 64
#endif
#if PB_MAX_REQUIRED_FIELDS < 64
#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
#endif
#ifdef PB_WITHOUT_64BIT
#ifdef PB_CONVERT_DOUBLE_FLOAT
/* Cannot use doubles without 64-bit types */
#undef PB_CONVERT_DOUBLE_FLOAT
#endif
#endif
/* Data type for storing encoded data and other byte streams.
* This typedef exists to support platforms where uint8_t does not exist.
* You can regard it as equivalent on uint8_t on other platforms.
*/
#if defined(PB_BYTE_T_OVERRIDE)
typedef PB_BYTE_T_OVERRIDE pb_byte_t;
#elif defined(UINT8_MAX)
typedef uint8_t pb_byte_t;
#else
typedef uint_least8_t pb_byte_t;
#endif
/* List of possible field types. These are used in the autogenerated code.
* Least-significant 4 bits tell the scalar type
* Most-significant 4 bits specify repeated/required/packed etc.
*/
typedef pb_byte_t pb_type_t;
/**** Field data types ****/
/* Numeric types */
#define PB_LTYPE_BOOL 0x00U /* bool */
#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */
#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */
#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */
#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */
#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */
/* Marker for last packable field type. */
#define PB_LTYPE_LAST_PACKABLE 0x05U
/* Byte array with pre-allocated buffer.
* data_size is the length of the allocated PB_BYTES_ARRAY structure. */
#define PB_LTYPE_BYTES 0x06U
/* String with pre-allocated buffer.
* data_size is the maximum length. */
#define PB_LTYPE_STRING 0x07U
/* Submessage
* submsg_fields is pointer to field descriptions */
#define PB_LTYPE_SUBMESSAGE 0x08U
/* Submessage with pre-decoding callback
* The pre-decoding callback is stored as pb_callback_t right before pSize.
* submsg_fields is pointer to field descriptions */
#define PB_LTYPE_SUBMSG_W_CB 0x09U
/* Extension pseudo-field
* The field contains a pointer to pb_extension_t */
#define PB_LTYPE_EXTENSION 0x0AU
/* Byte array with inline, pre-allocated byffer.
* data_size is the length of the inline, allocated buffer.
* This differs from PB_LTYPE_BYTES by defining the element as
* pb_byte_t[data_size] rather than pb_bytes_array_t. */
#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU
/* Number of declared LTYPES */
#define PB_LTYPES_COUNT 0x0CU
#define PB_LTYPE_MASK 0x0FU
/**** Field repetition rules ****/
#define PB_HTYPE_REQUIRED 0x00U
#define PB_HTYPE_OPTIONAL 0x10U
#define PB_HTYPE_SINGULAR 0x10U
#define PB_HTYPE_REPEATED 0x20U
#define PB_HTYPE_FIXARRAY 0x20U
#define PB_HTYPE_ONEOF 0x30U
#define PB_HTYPE_MASK 0x30U
/**** Field allocation types ****/
#define PB_ATYPE_STATIC 0x00U
#define PB_ATYPE_POINTER 0x80U
#define PB_ATYPE_CALLBACK 0x40U
#define PB_ATYPE_MASK 0xC0U
#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \
PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB)
/* Data type used for storing sizes of struct fields
* and array counts.
*/
#if defined(PB_FIELD_32BIT)
typedef uint32_t pb_size_t;
typedef int32_t pb_ssize_t;
#else
typedef uint_least16_t pb_size_t;
typedef int_least16_t pb_ssize_t;
#endif
#define PB_SIZE_MAX ((pb_size_t)-1)
/* Forward declaration of struct types */
typedef struct pb_istream_s pb_istream_t;
typedef struct pb_ostream_s pb_ostream_t;
typedef struct pb_field_iter_s pb_field_iter_t;
typedef struct pb_filedesc_s pb_filedesc_t;
struct pb_filedesc_s {
std::string_view file_name;
std::span<const uint8_t> file_descriptor;
};
/* This structure is used in auto-generated constants
* to specify struct fields.
*/
typedef struct pb_msgdesc_s pb_msgdesc_t;
struct pb_msgdesc_s {
const uint32_t *field_info;
const pb_msgdesc_t * const * submsg_info;
const pb_byte_t *default_value;
bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field);
pb_size_t field_count;
pb_size_t required_field_count;
pb_size_t largest_tag;
pb_filedesc_t file_descriptor;
std::string_view proto_name;
};
/* Iterator for message descriptor */
struct pb_field_iter_s {
const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */
void *message; /* Pointer to start of the structure */
pb_size_t index; /* Index of the field */
pb_size_t field_info_index; /* Index to descriptor->field_info array */
pb_size_t required_field_index; /* Index that counts only the required fields */
pb_size_t submessage_index; /* Index that counts only submessages */
pb_size_t tag; /* Tag of current field */
pb_size_t data_size; /* sizeof() of a single item */
pb_size_t array_size; /* Number of array entries */
pb_type_t type; /* Type of current field */
void *pField; /* Pointer to current field in struct */
void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */
void *pSize; /* Pointer to count/has field */
const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */
};
/* For compatibility with legacy code */
typedef pb_field_iter_t pb_field_t;
/* Make sure that the standard integer types are of the expected sizes.
* Otherwise fixed32/fixed64 fields can break.
*
* If you get errors here, it probably means that your stdint.h is not
* correct for your platform.
*/
#ifndef PB_WITHOUT_64BIT
PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE)
PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE)
#endif
/* This structure is used for 'bytes' arrays.
* It has the number of bytes in the beginning, and after that an array.
* Note that actual structs used will have a different length of bytes array.
*/
#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; }
#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes))
struct pb_bytes_array_s {
pb_size_t size;
pb_byte_t bytes[1];
};
typedef struct pb_bytes_array_s pb_bytes_array_t;
/* This structure is used for giving the callback function.
* It is stored in the message structure and filled in by the method that
* calls pb_decode.
*
* The decoding callback will be given a limited-length stream
* If the wire type was string, the length is the length of the string.
* If the wire type was a varint/fixed32/fixed64, the length is the length
* of the actual value.
* The function may be called multiple times (especially for repeated types,
* but also otherwise if the message happens to contain the field multiple
* times.)
*
* The encoding callback will receive the actual output stream.
* It should write all the data in one call, including the field tag and
* wire type. It can write multiple fields.
*
* The callback can be null if you want to skip a field.
*/
typedef struct pb_callback_s pb_callback_t;
struct pb_callback_s {
/* Callback functions receive a pointer to the arg field.
* You can access the value of the field as *arg, and modify it if needed.
*/
union {
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
} funcs;
/* Free arg for use by callback */
void *arg;
};
extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);
/* Wire types. Library user needs these only in encoder callbacks. */
typedef enum {
PB_WT_VARINT = 0,
PB_WT_64BIT = 1,
PB_WT_STRING = 2,
PB_WT_32BIT = 5,
PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */
} pb_wire_type_t;
/* Structure for defining the handling of unknown/extension fields.
* Usually the pb_extension_type_t structure is automatically generated,
* while the pb_extension_t structure is created by the user. However,
* if you want to catch all unknown fields, you can also create a custom
* pb_extension_type_t with your own callback.
*/
typedef struct pb_extension_type_s pb_extension_type_t;
typedef struct pb_extension_s pb_extension_t;
struct pb_extension_type_s {
/* Called for each unknown field in the message.
* If you handle the field, read off all of its data and return true.
* If you do not handle the field, do not read anything and return true.
* If you run into an error, return false.
* Set to NULL for default handler.
*/
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
uint32_t tag, pb_wire_type_t wire_type);
/* Called once after all regular fields have been encoded.
* If you have something to write, do so and return true.
* If you do not have anything to write, just return true.
* If you run into an error, return false.
* Set to NULL for default handler.
*/
bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
/* Free field for use by the callback. */
const void *arg;
};
struct pb_extension_s {
/* Type describing the extension field. Usually you'll initialize
* this to a pointer to the automatically generated structure. */
const pb_extension_type_t *type;
/* Destination for the decoded data. This must match the datatype
* of the extension field. */
void *dest;
/* Pointer to the next extension handler, or NULL.
* If this extension does not match a field, the next handler is
* automatically called. */
pb_extension_t *next;
/* The decoder sets this to true if the extension was found.
* Ignored for encoding. */
bool found;
};
#define pb_extension_init_zero {NULL,NULL,NULL,false}
/* Memory allocation functions to use. You can define pb_realloc and
* pb_free to custom functions if you want. */
#ifdef PB_ENABLE_MALLOC
# ifndef pb_realloc
# define pb_realloc(ptr, size) realloc(ptr, size)
# endif
# ifndef pb_free
# define pb_free(ptr) free(ptr)
# endif
#endif
/* This is used to inform about need to regenerate .pb.h/.pb.c files. */
#define PB_PROTO_HEADER_VERSION 40
/* These macros are used to declare pb_field_t's in the constant array. */
/* Size of a structure member, in bytes. */
#define pb_membersize(st, m) (sizeof ((st*)0)->m)
/* Number of entries in an array. */
#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
/* Delta from start of one member to the start of another member. */
#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2))
/* Force expansion of macro value */
#define PB_EXPAND(x) x
/* Binding of a message field set into a specific structure */
#define PB_BIND(msgname, structname, width) \
static const uint32_t structname ## _field_info[] PB_PROGMEM = \
{ \
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \
0 \
}; \
static const pb_msgdesc_t* const structname ## _submsg_info[] = \
{ \
msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \
NULL \
}; \
static const pb_msgdesc_t structname ## _msg = \
{ \
structname ## _field_info, \
structname ## _submsg_info, \
msgname ## _DEFAULT, \
msgname ## _CALLBACK, \
0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \
0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \
0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \
structname::file_descriptor(), \
structname::msg_name(), \
}; \
const pb_msgdesc_t* structname::msg_descriptor(void) noexcept { return &(structname ## _msg); } \
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname)
#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1
#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \
+ (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED)
#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \
* 0 + tag
/* X-macro for generating the entries in struct_field_info[] array. */
#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
/* X-macro for generating asserts that entries fit in struct_field_info[] array.
* The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(),
* but it is not easily reused because of how macro substitutions work. */
#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \
PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname))
#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname)
#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname)
#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname)
#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname)
#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname)
#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0
#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0
#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname))
#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname)
#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname)
#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname)
#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count)
#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0
#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname)
#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0
#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0
#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname)
#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname)
#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1
#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1
#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1
#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1
#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1
#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname)
#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname)
#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1
#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0])
#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname)
#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname)
#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname)
#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0])
#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0])
#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname)
#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple)
#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname
#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername
#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname
#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \
PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname)
#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname))
#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername)
#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE)
#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
#define PB_SI_PB_LTYPE_BOOL(t)
#define PB_SI_PB_LTYPE_BYTES(t)
#define PB_SI_PB_LTYPE_DOUBLE(t)
#define PB_SI_PB_LTYPE_ENUM(t)
#define PB_SI_PB_LTYPE_UENUM(t)
#define PB_SI_PB_LTYPE_FIXED32(t)
#define PB_SI_PB_LTYPE_FIXED64(t)
#define PB_SI_PB_LTYPE_FLOAT(t)
#define PB_SI_PB_LTYPE_INT32(t)
#define PB_SI_PB_LTYPE_INT64(t)
#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t)
#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t)
#define PB_SI_PB_LTYPE_SFIXED32(t)
#define PB_SI_PB_LTYPE_SFIXED64(t)
#define PB_SI_PB_LTYPE_SINT32(t)
#define PB_SI_PB_LTYPE_SINT64(t)
#define PB_SI_PB_LTYPE_STRING(t)
#define PB_SI_PB_LTYPE_UINT32(t)
#define PB_SI_PB_LTYPE_UINT64(t)
#define PB_SI_PB_LTYPE_EXTENSION(t)
#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t)
#define PB_SUBMSG_DESCRIPTOR(t) (t::msg_descriptor()),
/* The field descriptors use a variable width format, with width of either
* 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always
* encode the descriptor size, 6 lowest bits of field tag number, and 8 bits
* of the field type.
*
* Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words.
*
* Formats, listed starting with the least significant bit of the first word.
* 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size]
*
* 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset]
* [16-bit data_offset] [12-bit data_size] [4-bit tag>>6]
*
* 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size]
* [8-bit size_offset] [24-bit tag>>6]
* [32-bit data_offset]
* [32-bit data_size]
*
* 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved]
* [8-bit size_offset] [24-bit tag>>6]
* [32-bit data_offset]
* [32-bit data_size]
* [32-bit array_size]
* [32-bit reserved]
* [32-bit reserved]
* [32-bit reserved]
*/
#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \
(0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \
(((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)),
#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \
(1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \
(((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)),
#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \
(2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
(data_offset), (data_size),
#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \
(3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
(data_offset), (data_size), (array_size), 0, 0, 0,
/* These assertions verify that the field information fits in the allocated space.
* The generator tries to automatically determine the correct width that can fit all
* data associated with a message. These asserts will fail only if there has been a
* problem in the automatic logic - this may be worth reporting as a bug. As a workaround,
* you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting
* descriptorsize option in .options file.
*/
#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<<bits))
#define PB_FIELDINFO_ASSERT_1(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,6) && PB_FITS(data_offset,8) && PB_FITS(size_offset,4) && PB_FITS(data_size,4) && PB_FITS(array_size,1), FIELDINFO_DOES_NOT_FIT_width1_field ## tag)
#define PB_FIELDINFO_ASSERT_2(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,10) && PB_FITS(data_offset,16) && PB_FITS(size_offset,4) && PB_FITS(data_size,12) && PB_FITS(array_size,12), FIELDINFO_DOES_NOT_FIT_width2_field ## tag)
#ifndef PB_FIELD_32BIT
/* Maximum field sizes are still 16-bit if pb_size_t is 16-bit */
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
#else
/* Up to 32-bit fields supported.
* Note that the checks are against 31 bits to avoid compiler warnings about shift wider than type in the test.
* I expect that there is no reasonable use for >2GB messages with nanopb anyway.
*/
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
#endif
/* Automatic picking of FIELDINFO width:
* Uses width 1 when possible, otherwise resorts to width 2.
* This is used when PB_BIND() is called with "AUTO" as the argument.
* The generator will give explicit size argument when it knows that a message
* structure grows beyond 1-word format limits.
*/
#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype)
#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype)
#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype)
#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2
#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype
#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2
#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2
#define PB_FI_WIDTH_PB_LTYPE_BOOL 1
#define PB_FI_WIDTH_PB_LTYPE_BYTES 2
#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1
#define PB_FI_WIDTH_PB_LTYPE_ENUM 1
#define PB_FI_WIDTH_PB_LTYPE_UENUM 1
#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1
#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1
#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1
#define PB_FI_WIDTH_PB_LTYPE_INT32 1
#define PB_FI_WIDTH_PB_LTYPE_INT64 1
#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2
#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2
#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1
#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1
#define PB_FI_WIDTH_PB_LTYPE_SINT32 1
#define PB_FI_WIDTH_PB_LTYPE_SINT64 1
#define PB_FI_WIDTH_PB_LTYPE_STRING 2
#define PB_FI_WIDTH_PB_LTYPE_UINT32 1
#define PB_FI_WIDTH_PB_LTYPE_UINT64 1
#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1
#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2
/* The mapping from protobuf types to LTYPEs is done using these macros. */
#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT
#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT
#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT
#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE
#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB
#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32
#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT
#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT
#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING
#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT
#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT
#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION
#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES
/* These macros are used for giving out error messages.
* They are mostly a debugging aid; the main error information
* is the true/false return value from functions.
* Some code space can be saved by disabling the error
* messages if not used.
*
* PB_SET_ERROR() sets the error message if none has been set yet.
* msg must be a constant string literal.
* PB_GET_ERROR() always returns a pointer to a string.
* PB_RETURN_ERROR() sets the error and returns false from current
* function.
*/
#ifdef PB_NO_ERRMSG
#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream)
#define PB_GET_ERROR(stream) "(errmsg disabled)"
#else
#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg))
#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
#endif
#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false
#ifdef __cplusplus
#if __cplusplus >= 201103L
#define PB_CONSTEXPR constexpr
#else // __cplusplus >= 201103L
#define PB_CONSTEXPR
#endif // __cplusplus >= 201103L
#if __cplusplus >= 201703L
#define PB_INLINE_CONSTEXPR inline constexpr
#else // __cplusplus >= 201703L
#define PB_INLINE_CONSTEXPR PB_CONSTEXPR
#endif // __cplusplus >= 201703L
extern "C++"
{
namespace nanopb {
// Each type will be partially specialized by the generator.
template <typename GenMessageT> struct MessageDescriptor;
} // namespace nanopb
}
#endif /* __cplusplus */
#endif

View File

@@ -0,0 +1,42 @@
/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c.
* These functions are rarely needed by applications directly.
*
* Modified for WPILib Use
*/
#ifndef PB_COMMON_H_INCLUDED
#define PB_COMMON_H_INCLUDED
#include "pb.h"
/* Initialize the field iterator structure to beginning.
* Returns false if the message type is empty. */
bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message);
/* Get a field iterator for extension field. */
bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension);
/* Same as pb_field_iter_begin(), but for const message pointer.
* Note that the pointers in pb_field_iter_t will be non-const but shouldn't
* be written to when using these functions. */
bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message);
bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension);
/* Advance the iterator to the next field.
* Returns false when the iterator wraps back to the first field. */
bool pb_field_iter_next(pb_field_iter_t *iter);
/* Advance the iterator until it points at a field with the given tag.
* Returns false if no such field exists. */
bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag);
/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found.
* There can be only one extension range field per message. */
bool pb_field_iter_find_extension(pb_field_iter_t *iter);
#ifdef PB_VALIDATE_UTF8
/* Validate UTF-8 text string */
bool pb_validate_utf8(const char *s);
#endif
#endif

View File

@@ -0,0 +1,198 @@
/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c.
* The main function is pb_decode. You also need an input stream, and the
* field descriptions created by nanopb_generator.py.
*
* Modified for WPILib Use
*/
#ifndef PB_DECODE_H_INCLUDED
#define PB_DECODE_H_INCLUDED
#include "pb.h"
/* Structure for defining custom input streams. You will need to provide
* a callback function to read the bytes from your storage, which can be
* for example a file or a network socket.
*
* The callback must conform to these rules:
*
* 1) Return false on IO errors. This will cause decoding to abort.
* 2) You can use state to store your own data (e.g. buffer pointer),
* and rely on pb_read to verify that no-body reads past bytes_left.
* 3) Your callback may be used with substreams, in which case bytes_left
* is different than from the main stream. Don't use bytes_left to compute
* any pointers.
*/
struct pb_istream_s
{
#ifdef PB_BUFFER_ONLY
/* Callback pointer is not used in buffer-only configuration.
* Having an int pointer here allows binary compatibility but
* gives an error if someone tries to assign callback function.
*/
int *callback;
#else
bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count);
#endif
/* state is a free field for use of the callback function defined above.
* Note that when pb_istream_from_buffer() is used, it reserves this field
* for its own use.
*/
void *state;
/* Maximum number of bytes left in this stream. Callback can report
* EOF before this limit is reached. Setting a limit is recommended
* when decoding directly from file or network streams to avoid
* denial-of-service by excessively long messages.
*/
size_t bytes_left;
#ifndef PB_NO_ERRMSG
/* Pointer to constant (ROM) string when decoding function returns error */
const char *errmsg;
#endif
};
#ifndef PB_NO_ERRMSG
#define PB_ISTREAM_EMPTY {0,0,0,0}
#else
#define PB_ISTREAM_EMPTY {0,0,0}
#endif
/***************************
* Main decoding functions *
***************************/
/* Decode a single protocol buffers message from input stream into a C structure.
* Returns true on success, false on any failure.
* The actual struct pointed to by dest must match the description in fields.
* Callback fields of the destination structure must be initialized by caller.
* All other fields will be initialized by this function.
*
* Example usage:
* MyMessage msg = {};
* uint8_t buffer[64];
* pb_istream_t stream;
*
* // ... read some data into buffer ...
*
* stream = pb_istream_from_buffer(buffer, count);
* pb_decode(&stream, MyMessage_fields, &msg);
*/
bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct);
/* Extended version of pb_decode, with several options to control
* the decoding process:
*
* PB_DECODE_NOINIT: Do not initialize the fields to default values.
* This is slightly faster if you do not need the default
* values and instead initialize the structure to 0 using
* e.g. memset(). This can also be used for merging two
* messages, i.e. combine already existing data with new
* values.
*
* PB_DECODE_DELIMITED: Input message starts with the message size as varint.
* Corresponds to parseDelimitedFrom() in Google's
* protobuf API.
*
* PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows
* reading null terminated messages.
* NOTE: Until nanopb-0.4.0, pb_decode() also allows
* null-termination. This behaviour is not supported in
* most other protobuf implementations, so PB_DECODE_DELIMITED
* is a better option for compatibility.
*
* Multiple flags can be combined with bitwise or (| operator)
*/
#define PB_DECODE_NOINIT 0x01U
#define PB_DECODE_DELIMITED 0x02U
#define PB_DECODE_NULLTERMINATED 0x04U
bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags);
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT)
#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED)
#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT)
#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED)
/* Release any allocated pointer fields. If you use dynamic allocation, you should
* call this for any successfully decoded message when you are done with it. If
* pb_decode() returns with an error, the message is already released.
*/
void pb_release(const pb_msgdesc_t *fields, void *dest_struct);
/**************************************
* Functions for manipulating streams *
**************************************/
/* Create an input stream for reading from a memory buffer.
*
* msglen should be the actual length of the message, not the full size of
* allocated buffer.
*
* Alternatively, you can use a custom stream that reads directly from e.g.
* a file or a network socket.
*/
pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen);
/* Function to read from a pb_istream_t. You can use this if you need to
* read some custom header data, or to read data in field callbacks.
*/
bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
/************************************************
* Helper functions for writing field callbacks *
************************************************/
/* Decode the tag for the next field in the stream. Gives the wire type and
* field tag. At end of the message, returns false and sets eof to true. */
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
/* Skip the field payload data, given the wire type. */
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
/* Decode an integer in the varint format. This works for enum, int32,
* int64, uint32 and uint64 field types. */
#ifndef PB_WITHOUT_64BIT
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
#else
#define pb_decode_varint pb_decode_varint32
#endif
/* Decode an integer in the varint format. This works for enum, int32,
* and uint32 field types. */
bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
/* Decode a bool value in varint format. */
bool pb_decode_bool(pb_istream_t *stream, bool *dest);
/* Decode an integer in the zig-zagged svarint format. This works for sint32
* and sint64. */
#ifndef PB_WITHOUT_64BIT
bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
#else
bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest);
#endif
/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
* a 4-byte wide C variable. */
bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
#ifndef PB_WITHOUT_64BIT
/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
* a 8-byte wide C variable. */
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
#endif
#ifdef PB_CONVERT_DOUBLE_FLOAT
/* Decode a double value into float variable. */
bool pb_decode_double_as_float(pb_istream_t *stream, float *dest);
#endif
/* Make a limited-length substream for reading a PB_WT_STRING field. */
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
#endif

View File

@@ -0,0 +1,189 @@
/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c.
* The main function is pb_encode. You also need an output stream, and the
* field descriptions created by nanopb_generator.py.
*
* Modified for WPILib Use
*/
#ifndef PB_ENCODE_H_INCLUDED
#define PB_ENCODE_H_INCLUDED
#include "pb.h"
/* Structure for defining custom output streams. You will need to provide
* a callback function to write the bytes to your storage, which can be
* for example a file or a network socket.
*
* The callback must conform to these rules:
*
* 1) Return false on IO errors. This will cause encoding to abort.
* 2) You can use state to store your own data (e.g. buffer pointer).
* 3) pb_write will update bytes_written after your callback runs.
* 4) Substreams will modify max_size and bytes_written. Don't use them
* to calculate any pointers.
*/
struct pb_ostream_s
{
#ifdef PB_BUFFER_ONLY
/* Callback pointer is not used in buffer-only configuration.
* Having an int pointer here allows binary compatibility but
* gives an error if someone tries to assign callback function.
* Also, NULL pointer marks a 'sizing stream' that does not
* write anything.
*/
const int *callback;
#else
bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
#endif
/* state is a free field for use of the callback function defined above.
* Note that when pb_ostream_from_buffer() is used, it reserves this field
* for its own use.
*/
void *state;
/* Limit number of output bytes written. Can be set to SIZE_MAX. */
size_t max_size;
/* Number of bytes written so far. */
size_t bytes_written;
#ifndef PB_NO_ERRMSG
/* Pointer to constant (ROM) string when decoding function returns error */
const char *errmsg;
#endif
};
/***************************
* Main encoding functions *
***************************/
/* Encode a single protocol buffers message from C structure into a stream.
* Returns true on success, false on any failure.
* The actual struct pointed to by src_struct must match the description in fields.
* All required fields in the struct are assumed to have been filled in.
*
* Example usage:
* MyMessage msg = {};
* uint8_t buffer[64];
* pb_ostream_t stream;
*
* msg.field1 = 42;
* stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
* pb_encode(&stream, MyMessage_fields, &msg);
*/
bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct);
/* Extended version of pb_encode, with several options to control the
* encoding process:
*
* PB_ENCODE_DELIMITED: Prepend the length of message as a varint.
* Corresponds to writeDelimitedTo() in Google's
* protobuf API.
*
* PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination.
* NOTE: This behaviour is not supported in most other
* protobuf implementations, so PB_ENCODE_DELIMITED
* is a better option for compatibility.
*/
#define PB_ENCODE_DELIMITED 0x02U
#define PB_ENCODE_NULLTERMINATED 0x04U
bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags);
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED)
#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED)
/* Encode the message to get the size of the encoded data, but do not store
* the data. */
bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct);
/**************************************
* Functions for manipulating streams *
**************************************/
/* Create an output stream for writing into a memory buffer.
* The number of bytes written can be found in stream.bytes_written after
* encoding the message.
*
* Alternatively, you can use a custom stream that writes directly to e.g.
* a file or a network socket.
*/
pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize);
/* Pseudo-stream for measuring the size of a message without actually storing
* the encoded data.
*
* Example usage:
* MyMessage msg = {};
* pb_ostream_t stream = PB_OSTREAM_SIZING;
* pb_encode(&stream, MyMessage_fields, &msg);
* printf("Message size is %d\n", stream.bytes_written);
*/
#ifndef PB_NO_ERRMSG
#define PB_OSTREAM_SIZING {0,0,0,0,0}
#else
#define PB_OSTREAM_SIZING {0,0,0,0}
#endif
/* Function to write into a pb_ostream_t stream. You can use this if you need
* to append or prepend some custom headers to the message.
*/
bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
/************************************************
* Helper functions for writing field callbacks *
************************************************/
/* Encode field header based on type and field number defined in the field
* structure. Call this from the callback before writing out field contents. */
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field);
/* Encode field header by manually specifying wire type. You need to use this
* if you want to write out packed arrays from a callback field. */
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
/* Encode an integer in the varint format.
* This works for bool, enum, int32, int64, uint32 and uint64 field types. */
#ifndef PB_WITHOUT_64BIT
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
#else
bool pb_encode_varint(pb_ostream_t *stream, uint32_t value);
#endif
/* Encode an integer in the zig-zagged svarint format.
* This works for sint32 and sint64. */
#ifndef PB_WITHOUT_64BIT
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
#else
bool pb_encode_svarint(pb_ostream_t *stream, int32_t value);
#endif
/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size);
/* Encode a fixed32, sfixed32 or float value.
* You need to pass a pointer to a 4-byte wide C variable. */
bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
#ifndef PB_WITHOUT_64BIT
/* Encode a fixed64, sfixed64 or double value.
* You need to pass a pointer to a 8-byte wide C variable. */
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
#endif
#ifdef PB_CONVERT_DOUBLE_FLOAT
/* Encode a float value so that it appears like a double in the encoded
* message. */
bool pb_encode_float_as_double(pb_ostream_t *stream, float value);
#endif
/* Encode a submessage field.
* You need to pass the pb_field_t array and pointer to struct, just like
* with pb_encode(). This internally encodes the submessage twice, first to
* calculate message size and then to actually write it out.
*/
bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct);
#endif

View File

@@ -0,0 +1,389 @@
/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c.
*
* Modified for WPILib Use
*
* 2014 Petteri Aimonen <jpa@kapsi.fi>
*/
#include "pb_common.h"
static bool load_descriptor_values(pb_field_iter_t *iter)
{
uint32_t word0;
uint32_t data_offset;
int_least8_t size_offset;
if (iter->index >= iter->descriptor->field_count)
return false;
word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
iter->type = (pb_type_t)((word0 >> 8) & 0xFF);
switch(word0 & 3)
{
case 0: {
/* 1-word format */
iter->array_size = 1;
iter->tag = (pb_size_t)((word0 >> 2) & 0x3F);
size_offset = (int_least8_t)((word0 >> 24) & 0x0F);
data_offset = (word0 >> 16) & 0xFF;
iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F);
break;
}
case 1: {
/* 2-word format */
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF);
iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6));
size_offset = (int_least8_t)((word0 >> 28) & 0x0F);
data_offset = word1 & 0xFFFF;
iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF);
break;
}
case 2: {
/* 4-word format */
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
iter->array_size = (pb_size_t)(word0 >> 16);
iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6));
size_offset = (int_least8_t)(word1 & 0xFF);
data_offset = word2;
iter->data_size = (pb_size_t)word3;
break;
}
default: {
/* 8-word format */
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]);
iter->array_size = (pb_size_t)word4;
iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6));
size_offset = (int_least8_t)(word1 & 0xFF);
data_offset = word2;
iter->data_size = (pb_size_t)word3;
break;
}
}
if (!iter->message)
{
/* Avoid doing arithmetic on null pointers, it is undefined */
iter->pField = NULL;
iter->pSize = NULL;
}
else
{
iter->pField = (char*)iter->message + data_offset;
if (size_offset)
{
iter->pSize = (char*)iter->pField - size_offset;
}
else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED &&
(PB_ATYPE(iter->type) == PB_ATYPE_STATIC ||
PB_ATYPE(iter->type) == PB_ATYPE_POINTER))
{
/* Fixed count array */
iter->pSize = &iter->array_size;
}
else
{
iter->pSize = NULL;
}
if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL)
{
iter->pData = *(void**)iter->pField;
}
else
{
iter->pData = iter->pField;
}
}
if (PB_LTYPE_IS_SUBMSG(iter->type))
{
iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index];
}
else
{
iter->submsg_desc = NULL;
}
return true;
}
static void advance_iterator(pb_field_iter_t *iter)
{
iter->index++;
if (iter->index >= iter->descriptor->field_count)
{
/* Restart */
iter->index = 0;
iter->field_info_index = 0;
iter->submessage_index = 0;
iter->required_field_index = 0;
}
else
{
/* Increment indexes based on previous field type.
* All field info formats have the following fields:
* - lowest 2 bits tell the amount of words in the descriptor (2^n words)
* - bits 2..7 give the lowest bits of tag number.
* - bits 8..15 give the field type.
*/
uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF;
pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3));
/* Add to fields.
* The cast to pb_size_t is needed to avoid -Wconversion warning.
* Because the data is is constants from generator, there is no danger of overflow.
*/
iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len);
iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED));
iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type));
}
}
bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message)
{
memset(iter, 0, sizeof(*iter));
iter->descriptor = desc;
iter->message = message;
return load_descriptor_values(iter);
}
bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension)
{
const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg;
bool status;
uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]);
if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER)
{
/* For pointer extensions, the pointer is stored directly
* in the extension structure. This avoids having an extra
* indirection. */
status = pb_field_iter_begin(iter, msg, &extension->dest);
}
else
{
status = pb_field_iter_begin(iter, msg, extension->dest);
}
iter->pSize = &extension->found;
return status;
}
bool pb_field_iter_next(pb_field_iter_t *iter)
{
advance_iterator(iter);
(void)load_descriptor_values(iter);
return iter->index != 0;
}
bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
{
if (iter->tag == tag)
{
return true; /* Nothing to do, correct field already. */
}
else if (tag > iter->descriptor->largest_tag)
{
return false;
}
else
{
pb_size_t start = iter->index;
uint32_t fieldinfo;
if (tag < iter->tag)
{
/* Fields are in tag number order, so we know that tag is between
* 0 and our start position. Setting index to end forces
* advance_iterator() call below to restart from beginning. */
iter->index = iter->descriptor->field_count;
}
do
{
/* Advance iterator but don't load values yet */
advance_iterator(iter);
/* Do fast check for tag number match */
fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F))
{
/* Good candidate, check further */
(void)load_descriptor_values(iter);
if (iter->tag == tag &&
PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION)
{
/* Found it */
return true;
}
}
} while (iter->index != start);
/* Searched all the way back to start, and found nothing. */
(void)load_descriptor_values(iter);
return false;
}
}
bool pb_field_iter_find_extension(pb_field_iter_t *iter)
{
if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION)
{
return true;
}
else
{
pb_size_t start = iter->index;
uint32_t fieldinfo;
do
{
/* Advance iterator but don't load values yet */
advance_iterator(iter);
/* Do fast check for field type */
fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION)
{
return load_descriptor_values(iter);
}
} while (iter->index != start);
/* Searched all the way back to start, and found nothing. */
(void)load_descriptor_values(iter);
return false;
}
}
static void *pb_const_cast(const void *p)
{
/* Note: this casts away const, in order to use the common field iterator
* logic for both encoding and decoding. The cast is done using union
* to avoid spurious compiler warnings. */
union {
void *p1;
const void *p2;
} t;
t.p2 = p;
return t.p1;
}
bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message)
{
return pb_field_iter_begin(iter, desc, pb_const_cast(message));
}
bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension)
{
return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension));
}
bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
{
if (field->data_size == sizeof(pb_callback_t))
{
pb_callback_t *pCallback = (pb_callback_t*)field->pData;
if (pCallback != NULL)
{
if (istream != NULL && pCallback->funcs.decode != NULL)
{
return pCallback->funcs.decode(istream, field, &pCallback->arg);
}
if (ostream != NULL && pCallback->funcs.encode != NULL)
{
return pCallback->funcs.encode(ostream, field, &pCallback->arg);
}
}
}
return true; /* Success, but didn't do anything */
}
#ifdef PB_VALIDATE_UTF8
/* This function checks whether a string is valid UTF-8 text.
*
* Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
* Original copyright: Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> 2005-03-30
* Licensed under "Short code license", which allows use under MIT license or
* any compatible with it.
*/
bool pb_validate_utf8(const char *str)
{
const pb_byte_t *s = (const pb_byte_t*)str;
while (*s)
{
if (*s < 0x80)
{
/* 0xxxxxxx */
s++;
}
else if ((s[0] & 0xe0) == 0xc0)
{
/* 110XXXXx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[0] & 0xfe) == 0xc0) /* overlong? */
return false;
else
s += 2;
}
else if ((s[0] & 0xf0) == 0xe0)
{
/* 1110XXXX 10Xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 ||
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
(s[0] == 0xef && s[1] == 0xbf &&
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
return false;
else
s += 3;
}
else if ((s[0] & 0xf8) == 0xf0)
{
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 ||
(s[3] & 0xc0) != 0x80 ||
(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
(s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */
return false;
else
s += 4;
}
else
{
return false;
}
}
return true;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff