#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 &) {}