mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
[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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user