#include "test_builder.h" #include "flatbuffers/flatbuffer_builder.h" #include "flatbuffers/stl_emulation.h" #include "monster_test_generated.h" using namespace MyGame::Example; using namespace flatbuffers; struct OwnedAllocator : public DefaultAllocator {}; class TestHeapBuilder : public FlatBufferBuilder { private: TestHeapBuilder(const TestHeapBuilder&); TestHeapBuilder& operator=(const TestHeapBuilder&); public: TestHeapBuilder() : FlatBufferBuilder(2048, new OwnedAllocator(), true) {} TestHeapBuilder(TestHeapBuilder&& other) : FlatBufferBuilder(std::move(other)) {} TestHeapBuilder& operator=(TestHeapBuilder&& other) { FlatBufferBuilder::operator=(std::move(other)); return *this; } }; // This class simulates flatbuffers::grpc::detail::SliceAllocatorMember struct AllocatorMember { flatbuffers::DefaultAllocator member_allocator_; }; struct GrpcLikeMessageBuilder : private AllocatorMember, public FlatBufferBuilder { private: GrpcLikeMessageBuilder(const GrpcLikeMessageBuilder&); GrpcLikeMessageBuilder& operator=(const GrpcLikeMessageBuilder&); public: GrpcLikeMessageBuilder() : FlatBufferBuilder(1024, &member_allocator_, false) {} GrpcLikeMessageBuilder(GrpcLikeMessageBuilder&& other) : FlatBufferBuilder(1024, &member_allocator_, false) { // Default construct and swap idiom. Swap(other); } GrpcLikeMessageBuilder& operator=(GrpcLikeMessageBuilder&& other) { // Construct temporary and swap idiom GrpcLikeMessageBuilder temp(std::move(other)); Swap(temp); return *this; } void Swap(GrpcLikeMessageBuilder& other) { // No need to swap member_allocator_ because it's stateless. FlatBufferBuilder::Swap(other); // After swapping the FlatBufferBuilder, we swap back the allocator, which // restores the original allocator back in place. This is necessary because // MessageBuilder's allocator is its own member (SliceAllocatorMember). The // allocator passed to FlatBufferBuilder::vector_downward must point to this // member. buf_.swap_allocator(other.buf_); } }; flatbuffers::Offset populate1( flatbuffers::FlatBufferBuilder& builder) { auto name_offset = builder.CreateString(m1_name()); return CreateMonster(builder, nullptr, 0, 0, name_offset, 0, m1_color()); } flatbuffers::Offset populate2( flatbuffers::FlatBufferBuilder& builder) { auto name_offset = builder.CreateString(m2_name()); return CreateMonster(builder, nullptr, 0, 0, name_offset, 0, m2_color()); } uint8_t* release_raw_base(flatbuffers::FlatBufferBuilder& fbb, size_t& size, size_t& offset) { return fbb.ReleaseRaw(size, offset); } void free_raw(flatbuffers::grpc::MessageBuilder&, uint8_t*) { // release_raw_base calls FlatBufferBuilder::ReleaseRaw on the argument // MessageBuilder. It's semantically wrong as MessageBuilder has its own // ReleaseRaw member function that takes three arguments. In such cases // though, ~MessageBuilder() invokes ~SliceAllocator() that takes care of // deleting memory as it calls grpc_slice_unref. Obviously, this behavior is // very surprising as the pointer returned by FlatBufferBuilder::ReleaseRaw is // not valid as soon as MessageBuilder goes out of scope. This problem does // not occur with FlatBufferBuilder. } void free_raw(flatbuffers::FlatBufferBuilder&, uint8_t* buf) { flatbuffers::DefaultAllocator().deallocate(buf, 0); } bool verify(const flatbuffers::DetachedBuffer& buf, const std::string& expected_name, Color color) { const Monster* monster = flatbuffers::GetRoot(buf.data()); return (monster->name()->str() == expected_name) && (monster->color() == color); } bool verify(const uint8_t* buf, size_t offset, const std::string& expected_name, Color color) { const Monster* monster = flatbuffers::GetRoot(buf + offset); return (monster->name()->str() == expected_name) && (monster->color() == color); } bool release_n_verify(flatbuffers::FlatBufferBuilder& fbb, const std::string& expected_name, Color color) { flatbuffers::DetachedBuffer buf = fbb.Release(); return verify(buf, expected_name, color); } // forward-declared in test.cpp void FlatBufferBuilderTest(); void FlatBufferBuilderTest() { using flatbuffers::FlatBufferBuilder; BuilderTests::all_tests(); BuilderTests::all_tests(); BuilderTests::all_tests(); BuilderReuseTestSelector tests[4] = { REUSABLE_AFTER_RELEASE, REUSABLE_AFTER_RELEASE_RAW, REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN, REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN}; BuilderReuseTests::run_tests( TestSelector(tests, tests + 4)); BuilderReuseTests::run_tests( TestSelector(tests, tests + 4)); BuilderReuseTests::run_tests( TestSelector(tests, tests + 4)); } // forward-declared in test_builder.h void CheckTestGeneratedIsValid(const MyGame::Example::Color&); // Link-time check using pointer type. void CheckTestGeneratedIsValid(const MyGame::Example::Color&) {}