#include "offset64_test.h" #include #include #include #include #include #include "flatbuffers/base.h" #include "flatbuffers/buffer.h" #include "flatbuffers/flatbuffer_builder.h" #include "flatbuffers/flatbuffers.h" #include "tests/64bit/evolution/v1_generated.h" #include "tests/64bit/evolution/v2_generated.h" #include "tests/64bit/test_64bit_generated.h" #include "tests/test_assert.h" namespace flatbuffers { namespace tests { void Offset64Test() { FlatBufferBuilder64 builder; const size_t far_vector_size = 1LL << 2; // Make a large number if wanting to test a real large buffer. const size_t big_vector_size = 1LL << 2; { // First create the vectors that will be copied to the buffer. std::vector far_data; far_data.resize(far_vector_size); far_data[0] = 4; far_data[far_vector_size - 1] = 2; std::vector big_data; big_data.resize(big_vector_size); big_data[0] = 8; big_data[big_vector_size - 1] = 3; // Then serialize all the fields that have 64-bit offsets, as these must be // serialized before any 32-bit fields are added to the buffer. const Offset64> far_vector_offset = builder.CreateVector64(far_data); const Offset64 far_string_offset = builder.CreateString("some far string"); const Offset64> big_vector_offset = builder.CreateVector64(big_data); // Now that we are done with the 64-bit fields, we can create and add the // normal fields. const Offset near_string_offset = builder.CreateString("some near string"); // Finish by building the root table by passing in all the offsets. const Offset root_table_offset = CreateRootTable(builder, far_vector_offset, 0, far_string_offset, big_vector_offset, near_string_offset); // Finish the buffer. builder.Finish(root_table_offset); Verifier::Options options; // Allow the verifier to verify 64-bit buffers. options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE; options.assert = true; Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options); TEST_EQ(VerifyRootTableBuffer(verifier), true); } { const RootTable *root_table = GetRootTable(builder.GetBufferPointer()); // Expect the far vector to be properly sized. TEST_EQ(root_table->far_vector()->size(), far_vector_size); TEST_EQ(root_table->far_vector()->Get(0), 4); TEST_EQ(root_table->far_vector()->Get(far_vector_size - 1), 2); TEST_EQ_STR(root_table->far_string()->c_str(), "some far string"); // Expect the big vector to be properly sized. TEST_EQ(root_table->big_vector()->size(), big_vector_size); TEST_EQ(root_table->big_vector()->Get(0), 8); TEST_EQ(root_table->big_vector()->Get(big_vector_size - 1), 3); TEST_EQ_STR(root_table->near_string()->c_str(), "some near string"); } } void Offset64SerializedFirst() { FlatBufferBuilder64 fbb; // First create the vectors that will be copied to the buffer. std::vector data; data.resize(64); // Then serialize all the fields that have 64-bit offsets, as these must be // serialized before any 32-bit fields are added to the buffer. fbb.CreateVector64(data); // TODO(derekbailey): figure out how to test assertions. // Uncommenting this line should fail the test with an assertion. // fbb.CreateString("some near string"); fbb.CreateVector64(data); } void Offset64NestedFlatBuffer() { FlatBufferBuilder64 fbb; // First serialize a nested buffer. const Offset near_string_offset = fbb.CreateString("nested: some near string"); // Finish by building the root table by passing in all the offsets. const Offset root_table_offset = CreateRootTable(fbb, 0, 0, 0, 0, near_string_offset, 0); // Finish the buffer. fbb.Finish(root_table_offset); // Ensure the buffer is valid. const RootTable *root_table = GetRootTable(fbb.GetBufferPointer()); TEST_EQ_STR(root_table->near_string()->c_str(), "nested: some near string"); // Copy the data out of the builder. std::vector nested_data{ fbb.GetBufferPointer(), fbb.GetBufferPointer() + fbb.GetSize() }; { // Clear so we can reuse the builder. fbb.Clear(); const Offset64> nested_flatbuffer_offset = fbb.CreateVector64(nested_data); // Now that we are done with the 64-bit fields, we can create and add the // normal fields. const Offset near_string_offset = fbb.CreateString("some near string"); // Finish by building the root table by passing in all the offsets. const Offset root_table_offset = CreateRootTable( fbb, 0, 0, 0, 0, near_string_offset, nested_flatbuffer_offset); // Finish the buffer. fbb.Finish(root_table_offset); Verifier::Options options; // Allow the verifier to verify 64-bit buffers. options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE; options.assert = true; Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize(), options); TEST_EQ(VerifyRootTableBuffer(verifier), true); } { const RootTable *root_table = GetRootTable(fbb.GetBufferPointer()); // Test that the parent buffer field is ok. TEST_EQ_STR(root_table->near_string()->c_str(), "some near string"); // Expect nested buffer to be properly sized. TEST_EQ(root_table->nested_root()->size(), nested_data.size()); // Expect the direct accessors to the nested buffer work. TEST_EQ_STR(root_table->nested_root_nested_root()->near_string()->c_str(), "nested: some near string"); } } void Offset64CreateDirect() { FlatBufferBuilder64 fbb; // Create a vector of some data std::vector data{ 0, 1, 2 }; // Call the "Direct" creation method to ensure that things are added to the // buffer in the correct order, Offset64 first followed by any Offsets. const Offset root_table_offset = CreateRootTableDirect( fbb, &data, 0, "some far string", &data, "some near string"); // Finish the buffer. fbb.Finish(root_table_offset); Verifier::Options options; // Allow the verifier to verify 64-bit buffers. options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE; options.assert = true; Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize(), options); TEST_EQ(VerifyRootTableBuffer(verifier), true); // Verify the data. const RootTable *root_table = GetRootTable(fbb.GetBufferPointer()); TEST_EQ(root_table->far_vector()->size(), data.size()); TEST_EQ(root_table->big_vector()->size(), data.size()); TEST_EQ_STR(root_table->far_string()->c_str(), "some far string"); TEST_EQ_STR(root_table->near_string()->c_str(), "some near string"); } void Offset64Evolution() { // Some common data for the tests. const std::vector data = { 1, 2, 3, 4 }; const std::vector big_data = { 6, 7, 8, 9, 10 }; // Built V1 read V2 { // Use the 32-bit builder since V1 doesn't have any 64-bit offsets. FlatBufferBuilder builder; builder.Finish(v1::CreateRootTableDirect(builder, 1234, &data)); // Use each version to get a view at the root table. auto v1_root = v1::GetRootTable(builder.GetBufferPointer()); auto v2_root = v2::GetRootTable(builder.GetBufferPointer()); // Test field equivalents for fields common to V1 and V2. TEST_EQ(v1_root->a(), v2_root->a()); TEST_EQ(v1_root->b(), v2_root->b()); TEST_EQ(v1_root->b()->Get(2), 3); TEST_EQ(v2_root->b()->Get(2), 3); // This field is added in V2, so it should be null since V1 couldn't have // written it. TEST_ASSERT(v2_root->big_vector() == nullptr); } // Built V2 read V1 { // Use the 64-bit builder since V2 has 64-bit offsets. FlatBufferBuilder64 builder; builder.Finish(v2::CreateRootTableDirect(builder, 1234, &data, &big_data)); // Use each version to get a view at the root table. auto v1_root = v1::GetRootTable(builder.GetBufferPointer()); auto v2_root = v2::GetRootTable(builder.GetBufferPointer()); // Test field equivalents for fields common to V1 and V2. TEST_EQ(v1_root->a(), v2_root->a()); TEST_EQ(v1_root->b(), v2_root->b()); TEST_EQ(v1_root->b()->Get(2), 3); TEST_EQ(v2_root->b()->Get(2), 3); // Test that V2 can read the big vector, which V1 doesn't even have // accessors for (i.e. v1_root->big_vector() doesn't exist). TEST_ASSERT(v2_root->big_vector() != nullptr); TEST_EQ(v2_root->big_vector()->size(), big_data.size()); TEST_EQ(v2_root->big_vector()->Get(2), 8); } // Built V2 read V1, bigger than max 32-bit buffer sized. // This checks that even a large buffer can still be read by V1. { // Use the 64-bit builder since V2 has 64-bit offsets. FlatBufferBuilder64 builder; std::vector giant_data; giant_data.resize(1LL << 3); giant_data[2] = 42; builder.Finish( v2::CreateRootTableDirect(builder, 1234, &data, &giant_data)); // Use each version to get a view at the root table. auto v1_root = v1::GetRootTable(builder.GetBufferPointer()); auto v2_root = v2::GetRootTable(builder.GetBufferPointer()); // Test field equivalents for fields common to V1 and V2. TEST_EQ(v1_root->a(), v2_root->a()); TEST_EQ(v1_root->b(), v2_root->b()); TEST_EQ(v1_root->b()->Get(2), 3); TEST_EQ(v2_root->b()->Get(2), 3); // Test that V2 can read the big vector, which V1 doesn't even have // accessors for (i.e. v1_root->big_vector() doesn't exist). TEST_ASSERT(v2_root->big_vector() != nullptr); TEST_EQ(v2_root->big_vector()->size(), giant_data.size()); TEST_EQ(v2_root->big_vector()->Get(2), 42); } } void Offset64VectorOfStructs() { FlatBufferBuilder64 builder; std::vector far_leaves; far_leaves.emplace_back(LeafStruct{ 123, 4.567 }); far_leaves.emplace_back(LeafStruct{ 987, 6.543 }); std::vector big_leaves; big_leaves.emplace_back(LeafStruct{ 72, 72.8 }); big_leaves.emplace_back(LeafStruct{ 82, 82.8 }); big_leaves.emplace_back(LeafStruct{ 92, 92.8 }); // Add the two vectors of leaf structs. const Offset root_table_offset = CreateRootTableDirect(builder, nullptr, 0, nullptr, nullptr, nullptr, nullptr, &far_leaves, &big_leaves); // Finish the buffer. builder.Finish(root_table_offset); Verifier::Options options; // Allow the verifier to verify 64-bit buffers. options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE; options.assert = true; Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options); TEST_EQ(VerifyRootTableBuffer(verifier), true); // Verify the data. const RootTable *root_table = GetRootTable(builder.GetBufferPointer()); TEST_EQ(root_table->far_struct_vector()->size(), far_leaves.size()); TEST_EQ(root_table->far_struct_vector()->Get(0)->a(), 123); TEST_EQ(root_table->far_struct_vector()->Get(0)->b(), 4.567); TEST_EQ(root_table->far_struct_vector()->Get(1)->a(), 987); TEST_EQ(root_table->far_struct_vector()->Get(1)->b(), 6.543); TEST_EQ(root_table->big_struct_vector()->size(), big_leaves.size()); TEST_EQ(root_table->big_struct_vector()->Get(0)->a(), 72); TEST_EQ(root_table->big_struct_vector()->Get(0)->b(), 72.8); TEST_EQ(root_table->big_struct_vector()->Get(1)->a(), 82); TEST_EQ(root_table->big_struct_vector()->Get(1)->b(), 82.8); TEST_EQ(root_table->big_struct_vector()->Get(2)->a(), 92); TEST_EQ(root_table->big_struct_vector()->Get(2)->b(), 92.8); } void Offset64SizePrefix() { FlatBufferBuilder64 builder; // First serialize a nested buffer. const Offset near_string_offset = builder.CreateString("some near string"); // Finish by building the root table by passing in all the offsets. const Offset root_table_offset = CreateRootTable(builder, 0, 0, 0, 0, near_string_offset, 0); // Finish the buffer. FinishSizePrefixedRootTableBuffer(builder, root_table_offset); TEST_EQ(GetPrefixedSize(builder.GetBufferPointer()), builder.GetSize() - sizeof(uoffset64_t)); Verifier::Options options; // Allow the verifier to verify 64-bit buffers. options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE; options.assert = true; Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options); TEST_EQ(VerifySizePrefixedRootTableBuffer(verifier), true); const RootTable *root_table = GetSizePrefixedRootTable(builder.GetBufferPointer()); // Verify the fields. TEST_EQ_STR(root_table->near_string()->c_str(), "some near string"); } void Offset64ManyVectors() { FlatBufferBuilder64 builder; // Setup some data to serialize. std::vector data; data.resize(20); data.front() = 42; data.back() = 18; const size_t kNumVectors = 20; // First serialize all the 64-bit address vectors. We need to store all the // offsets to later add to a wrapper table. We cannot serialize one vector and // then add it to a table immediately, as it would violate the strict ordering // of putting all 64-bit things at the tail of the buffer. std::array>, kNumVectors> offsets_64bit; for (size_t i = 0; i < kNumVectors; ++i) { offsets_64bit[i] = builder.CreateVector64(data); } // Create some unrelated, 64-bit offset value for later testing. const Offset64 far_string_offset = builder.CreateString("some far string"); // Now place all the offsets into their own wrapper tables. Again, we have to // store the offsets before we can add them to the root table vector. std::array, kNumVectors> offsets_wrapper; for (size_t i = 0; i < kNumVectors; ++i) { offsets_wrapper[i] = CreateWrapperTable(builder, offsets_64bit[i]); } // Now create the 32-bit vector that is stored in the root table. // TODO(derekbailey): the array type wasn't auto deduced, see if that could be // fixed. const Offset>> many_vectors_offset = builder.CreateVector>(offsets_wrapper); // Finish by building using the root table builder, to exercise a different // code path than the other tests. RootTableBuilder root_table_builder(builder); root_table_builder.add_many_vectors(many_vectors_offset); root_table_builder.add_far_string(far_string_offset); const Offset root_table_offset = root_table_builder.Finish(); // Finish the buffer. FinishRootTableBuffer(builder, root_table_offset); Verifier::Options options; // Allow the verifier to verify 64-bit buffers. options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE; options.assert = true; Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options); TEST_EQ(VerifyRootTableBuffer(verifier), true); const RootTable *root_table = GetRootTable(builder.GetBufferPointer()); // Verify the fields. TEST_EQ_STR(root_table->far_string()->c_str(), "some far string"); TEST_EQ(root_table->many_vectors()->size(), kNumVectors); // Spot check one of the vectors. TEST_EQ(root_table->many_vectors()->Get(12)->vector()->size(), 20); TEST_EQ(root_table->many_vectors()->Get(12)->vector()->Get(0), 42); TEST_EQ(root_table->many_vectors()->Get(12)->vector()->Get(19), 18); } } // namespace tests } // namespace flatbuffers