1#include "offset64_test.h"
2
3#include <stdint.h>
4
5#include <cstdint>
6#include <fstream>
7#include <limits>
8#include <ostream>
9
10#include "flatbuffers/base.h"
11#include "flatbuffers/buffer.h"
12#include "flatbuffers/flatbuffer_builder.h"
13#include "flatbuffers/flatbuffers.h"
14#include "tests/64bit/evolution/v1_generated.h"
15#include "tests/64bit/evolution/v2_generated.h"
16#include "tests/64bit/test_64bit_generated.h"
17#include "tests/test_assert.h"
18
19namespace flatbuffers {
20namespace tests {
21
22void Offset64Test() {
23 FlatBufferBuilder64 builder;
24
25 const size_t far_vector_size = 1LL << 2;
26 // Make a large number if wanting to test a real large buffer.
27 const size_t big_vector_size = 1LL << 2;
28
29 {
30 // First create the vectors that will be copied to the buffer.
31 std::vector<uint8_t> far_data;
32 far_data.resize(far_vector_size);
33 far_data[0] = 4;
34 far_data[far_vector_size - 1] = 2;
35
36 std::vector<uint8_t> big_data;
37 big_data.resize(big_vector_size);
38 big_data[0] = 8;
39 big_data[big_vector_size - 1] = 3;
40
41 // Then serialize all the fields that have 64-bit offsets, as these must be
42 // serialized before any 32-bit fields are added to the buffer.
43 const Offset64<Vector<uint8_t>> far_vector_offset =
44 builder.CreateVector64<Vector>(far_data);
45
46 const Offset64<String> far_string_offset =
47 builder.CreateString<Offset64>("some far string");
48
49 const Offset64<Vector64<uint8_t>> big_vector_offset =
50 builder.CreateVector64(big_data);
51
52 // Now that we are done with the 64-bit fields, we can create and add the
53 // normal fields.
54 const Offset<String> near_string_offset =
55 builder.CreateString("some near string");
56
57 // Finish by building the root table by passing in all the offsets.
58 const Offset<RootTable> root_table_offset =
59 CreateRootTable(builder, far_vector_offset, 0, far_string_offset,
60 big_vector_offset, near_string_offset);
61
62 // Finish the buffer.
63 builder.Finish(root_table_offset);
64
65 Verifier::Options options;
66 // Allow the verifier to verify 64-bit buffers.
67 options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE;
68 options.assert = true;
69
70 Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options);
71
72 TEST_EQ(VerifyRootTableBuffer(verifier), true);
73 }
74
75 {
76 const RootTable *root_table = GetRootTable(builder.GetBufferPointer());
77
78 // Expect the far vector to be properly sized.
79 TEST_EQ(root_table->far_vector()->size(), far_vector_size);
80 TEST_EQ(root_table->far_vector()->Get(0), 4);
81 TEST_EQ(root_table->far_vector()->Get(far_vector_size - 1), 2);
82
83 TEST_EQ_STR(root_table->far_string()->c_str(), "some far string");
84
85 // Expect the big vector to be properly sized.
86 TEST_EQ(root_table->big_vector()->size(), big_vector_size);
87 TEST_EQ(root_table->big_vector()->Get(0), 8);
88 TEST_EQ(root_table->big_vector()->Get(big_vector_size - 1), 3);
89
90 TEST_EQ_STR(root_table->near_string()->c_str(), "some near string");
91 }
92}
93
94void Offset64SerializedFirst() {
95 FlatBufferBuilder64 fbb;
96
97 // First create the vectors that will be copied to the buffer.
98 std::vector<uint8_t> data;
99 data.resize(64);
100
101 // Then serialize all the fields that have 64-bit offsets, as these must be
102 // serialized before any 32-bit fields are added to the buffer.
103 fbb.CreateVector64(data);
104
105 // TODO(derekbailey): figure out how to test assertions.
106 // Uncommenting this line should fail the test with an assertion.
107 // fbb.CreateString("some near string");
108
109 fbb.CreateVector64(data);
110}
111
112void Offset64NestedFlatBuffer() {
113 FlatBufferBuilder64 fbb;
114
115 // First serialize a nested buffer.
116 const Offset<String> near_string_offset =
117 fbb.CreateString("nested: some near string");
118
119 // Finish by building the root table by passing in all the offsets.
120 const Offset<RootTable> root_table_offset =
121 CreateRootTable(fbb, 0, 0, 0, 0, near_string_offset, 0);
122
123 // Finish the buffer.
124 fbb.Finish(root_table_offset);
125
126 // Ensure the buffer is valid.
127 const RootTable *root_table = GetRootTable(fbb.GetBufferPointer());
128 TEST_EQ_STR(root_table->near_string()->c_str(), "nested: some near string");
129
130 // Copy the data out of the builder.
131 std::vector<uint8_t> nested_data{ fbb.GetBufferPointer(),
132 fbb.GetBufferPointer() + fbb.GetSize() };
133
134 {
135 // Clear so we can reuse the builder.
136 fbb.Clear();
137
138 const Offset64<Vector64<uint8_t>> nested_flatbuffer_offset =
139 fbb.CreateVector64<Vector64>(nested_data);
140
141 // Now that we are done with the 64-bit fields, we can create and add the
142 // normal fields.
143 const Offset<String> near_string_offset =
144 fbb.CreateString("some near string");
145
146 // Finish by building the root table by passing in all the offsets.
147 const Offset<RootTable> root_table_offset = CreateRootTable(
148 fbb, 0, 0, 0, 0, near_string_offset, nested_flatbuffer_offset);
149
150 // Finish the buffer.
151 fbb.Finish(root_table_offset);
152
153 Verifier::Options options;
154 // Allow the verifier to verify 64-bit buffers.
155 options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE;
156 options.assert = true;
157
158 Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize(), options);
159
160 TEST_EQ(VerifyRootTableBuffer(verifier), true);
161 }
162
163 {
164 const RootTable *root_table = GetRootTable(fbb.GetBufferPointer());
165
166 // Test that the parent buffer field is ok.
167 TEST_EQ_STR(root_table->near_string()->c_str(), "some near string");
168
169 // Expect nested buffer to be properly sized.
170 TEST_EQ(root_table->nested_root()->size(), nested_data.size());
171
172 // Expect the direct accessors to the nested buffer work.
173 TEST_EQ_STR(root_table->nested_root_nested_root()->near_string()->c_str(),
174 "nested: some near string");
175 }
176}
177
178void Offset64CreateDirect() {
179 FlatBufferBuilder64 fbb;
180
181 // Create a vector of some data
182 std::vector<uint8_t> data{ 0, 1, 2 };
183
184 // Call the "Direct" creation method to ensure that things are added to the
185 // buffer in the correct order, Offset64 first followed by any Offsets.
186 const Offset<RootTable> root_table_offset = CreateRootTableDirect(
187 fbb, &data, 0, "some far string", &data, "some near string");
188
189 // Finish the buffer.
190 fbb.Finish(root_table_offset);
191
192 Verifier::Options options;
193 // Allow the verifier to verify 64-bit buffers.
194 options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE;
195 options.assert = true;
196
197 Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize(), options);
198
199 TEST_EQ(VerifyRootTableBuffer(verifier), true);
200
201 // Verify the data.
202 const RootTable *root_table = GetRootTable(fbb.GetBufferPointer());
203 TEST_EQ(root_table->far_vector()->size(), data.size());
204 TEST_EQ(root_table->big_vector()->size(), data.size());
205 TEST_EQ_STR(root_table->far_string()->c_str(), "some far string");
206 TEST_EQ_STR(root_table->near_string()->c_str(), "some near string");
207}
208
209void Offset64Evolution() {
210 // Some common data for the tests.
211 const std::vector<uint8_t> data = { 1, 2, 3, 4 };
212 const std::vector<uint8_t> big_data = { 6, 7, 8, 9, 10 };
213
214 // Built V1 read V2
215 {
216 // Use the 32-bit builder since V1 doesn't have any 64-bit offsets.
217 FlatBufferBuilder builder;
218
219 builder.Finish(v1::CreateRootTableDirect(builder, 1234, &data));
220
221 // Use each version to get a view at the root table.
222 auto v1_root = v1::GetRootTable(builder.GetBufferPointer());
223 auto v2_root = v2::GetRootTable(builder.GetBufferPointer());
224
225 // Test field equivalents for fields common to V1 and V2.
226 TEST_EQ(v1_root->a(), v2_root->a());
227
228 TEST_EQ(v1_root->b(), v2_root->b());
229 TEST_EQ(v1_root->b()->Get(2), 3);
230 TEST_EQ(v2_root->b()->Get(2), 3);
231
232 // This field is added in V2, so it should be null since V1 couldn't have
233 // written it.
234 TEST_ASSERT(v2_root->big_vector() == nullptr);
235 }
236
237 // Built V2 read V1
238 {
239 // Use the 64-bit builder since V2 has 64-bit offsets.
240 FlatBufferBuilder64 builder;
241
242 builder.Finish(v2::CreateRootTableDirect(builder, 1234, &data, &big_data));
243
244 // Use each version to get a view at the root table.
245 auto v1_root = v1::GetRootTable(builder.GetBufferPointer());
246 auto v2_root = v2::GetRootTable(builder.GetBufferPointer());
247
248 // Test field equivalents for fields common to V1 and V2.
249 TEST_EQ(v1_root->a(), v2_root->a());
250
251 TEST_EQ(v1_root->b(), v2_root->b());
252 TEST_EQ(v1_root->b()->Get(2), 3);
253 TEST_EQ(v2_root->b()->Get(2), 3);
254
255 // Test that V2 can read the big vector, which V1 doesn't even have
256 // accessors for (i.e. v1_root->big_vector() doesn't exist).
257 TEST_ASSERT(v2_root->big_vector() != nullptr);
258 TEST_EQ(v2_root->big_vector()->size(), big_data.size());
259 TEST_EQ(v2_root->big_vector()->Get(2), 8);
260 }
261
262 // Built V2 read V1, bigger than max 32-bit buffer sized.
263 // This checks that even a large buffer can still be read by V1.
264 {
265 // Use the 64-bit builder since V2 has 64-bit offsets.
266 FlatBufferBuilder64 builder;
267
268 std::vector<uint8_t> giant_data;
269 giant_data.resize(1LL << 3);
270 giant_data[2] = 42;
271
272 builder.Finish(
273 v2::CreateRootTableDirect(builder, 1234, &data, &giant_data));
274
275 // Use each version to get a view at the root table.
276 auto v1_root = v1::GetRootTable(builder.GetBufferPointer());
277 auto v2_root = v2::GetRootTable(builder.GetBufferPointer());
278
279 // Test field equivalents for fields common to V1 and V2.
280 TEST_EQ(v1_root->a(), v2_root->a());
281
282 TEST_EQ(v1_root->b(), v2_root->b());
283 TEST_EQ(v1_root->b()->Get(2), 3);
284 TEST_EQ(v2_root->b()->Get(2), 3);
285
286 // Test that V2 can read the big vector, which V1 doesn't even have
287 // accessors for (i.e. v1_root->big_vector() doesn't exist).
288 TEST_ASSERT(v2_root->big_vector() != nullptr);
289 TEST_EQ(v2_root->big_vector()->size(), giant_data.size());
290 TEST_EQ(v2_root->big_vector()->Get(2), 42);
291 }
292}
293
294void Offset64VectorOfStructs() {
295 FlatBufferBuilder64 builder;
296
297 std::vector<LeafStruct> far_leaves;
298 far_leaves.emplace_back(LeafStruct{ 123, 4.567 });
299 far_leaves.emplace_back(LeafStruct{ 987, 6.543 });
300
301 std::vector<LeafStruct> big_leaves;
302 big_leaves.emplace_back(LeafStruct{ 72, 72.8 });
303 big_leaves.emplace_back(LeafStruct{ 82, 82.8 });
304 big_leaves.emplace_back(LeafStruct{ 92, 92.8 });
305
306 // Add the two vectors of leaf structs.
307 const Offset<RootTable> root_table_offset =
308 CreateRootTableDirect(builder, nullptr, 0, nullptr, nullptr, nullptr,
309 nullptr, &far_leaves, &big_leaves);
310
311 // Finish the buffer.
312 builder.Finish(root_table_offset);
313
314 Verifier::Options options;
315 // Allow the verifier to verify 64-bit buffers.
316 options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE;
317 options.assert = true;
318
319 Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options);
320
321 TEST_EQ(VerifyRootTableBuffer(verifier), true);
322
323 // Verify the data.
324 const RootTable *root_table = GetRootTable(builder.GetBufferPointer());
325 TEST_EQ(root_table->far_struct_vector()->size(), far_leaves.size());
326 TEST_EQ(root_table->far_struct_vector()->Get(0)->a(), 123);
327 TEST_EQ(root_table->far_struct_vector()->Get(0)->b(), 4.567);
328 TEST_EQ(root_table->far_struct_vector()->Get(1)->a(), 987);
329 TEST_EQ(root_table->far_struct_vector()->Get(1)->b(), 6.543);
330
331 TEST_EQ(root_table->big_struct_vector()->size(), big_leaves.size());
332 TEST_EQ(root_table->big_struct_vector()->Get(0)->a(), 72);
333 TEST_EQ(root_table->big_struct_vector()->Get(0)->b(), 72.8);
334 TEST_EQ(root_table->big_struct_vector()->Get(1)->a(), 82);
335 TEST_EQ(root_table->big_struct_vector()->Get(1)->b(), 82.8);
336 TEST_EQ(root_table->big_struct_vector()->Get(2)->a(), 92);
337 TEST_EQ(root_table->big_struct_vector()->Get(2)->b(), 92.8);
338}
339
340void Offset64SizePrefix() {
341 FlatBufferBuilder64 builder;
342
343 // First serialize a nested buffer.
344 const Offset<String> near_string_offset =
345 builder.CreateString("some near string");
346
347 // Finish by building the root table by passing in all the offsets.
348 const Offset<RootTable> root_table_offset =
349 CreateRootTable(builder, 0, 0, 0, 0, near_string_offset, 0);
350
351 // Finish the buffer.
352 FinishSizePrefixedRootTableBuffer(builder, root_table_offset);
353
354 TEST_EQ(GetPrefixedSize<uoffset64_t>(builder.GetBufferPointer()),
355 builder.GetSize() - sizeof(uoffset64_t));
356
357 Verifier::Options options;
358 // Allow the verifier to verify 64-bit buffers.
359 options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE;
360 options.assert = true;
361
362 Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options);
363
364 TEST_EQ(VerifySizePrefixedRootTableBuffer(verifier), true);
365
366 const RootTable *root_table =
367 GetSizePrefixedRootTable(builder.GetBufferPointer());
368
369 // Verify the fields.
370 TEST_EQ_STR(root_table->near_string()->c_str(), "some near string");
371}
372
373void Offset64ManyVectors() {
374 FlatBufferBuilder64 builder;
375
376 // Setup some data to serialize.
377 std::vector<int8_t> data;
378 data.resize(20);
379 data.front() = 42;
380 data.back() = 18;
381
382 const size_t kNumVectors = 20;
383
384 // First serialize all the 64-bit address vectors. We need to store all the
385 // offsets to later add to a wrapper table. We cannot serialize one vector and
386 // then add it to a table immediately, as it would violate the strict ordering
387 // of putting all 64-bit things at the tail of the buffer.
388 std::array<Offset64<Vector<int8_t>>, kNumVectors> offsets_64bit;
389 for (size_t i = 0; i < kNumVectors; ++i) {
390 offsets_64bit[i] = builder.CreateVector64<Vector>(data);
391 }
392
393 // Create some unrelated, 64-bit offset value for later testing.
394 const Offset64<String> far_string_offset =
395 builder.CreateString<Offset64>("some far string");
396
397 // Now place all the offsets into their own wrapper tables. Again, we have to
398 // store the offsets before we can add them to the root table vector.
399 std::array<Offset<WrapperTable>, kNumVectors> offsets_wrapper;
400 for (size_t i = 0; i < kNumVectors; ++i) {
401 offsets_wrapper[i] = CreateWrapperTable(builder, offsets_64bit[i]);
402 }
403
404 // Now create the 32-bit vector that is stored in the root table.
405 // TODO(derekbailey): the array type wasn't auto deduced, see if that could be
406 // fixed.
407 const Offset<Vector<Offset<WrapperTable>>> many_vectors_offset =
408 builder.CreateVector<Offset<WrapperTable>>(offsets_wrapper);
409
410 // Finish by building using the root table builder, to exercise a different
411 // code path than the other tests.
412 RootTableBuilder root_table_builder(builder);
413 root_table_builder.add_many_vectors(many_vectors_offset);
414 root_table_builder.add_far_string(far_string_offset);
415 const Offset<RootTable> root_table_offset = root_table_builder.Finish();
416
417 // Finish the buffer.
418 FinishRootTableBuffer(builder, root_table_offset);
419
420 Verifier::Options options;
421 // Allow the verifier to verify 64-bit buffers.
422 options.max_size = FLATBUFFERS_MAX_64_BUFFER_SIZE;
423 options.assert = true;
424
425 Verifier verifier(builder.GetBufferPointer(), builder.GetSize(), options);
426
427 TEST_EQ(VerifyRootTableBuffer(verifier), true);
428
429 const RootTable *root_table = GetRootTable(builder.GetBufferPointer());
430
431 // Verify the fields.
432 TEST_EQ_STR(root_table->far_string()->c_str(), "some far string");
433 TEST_EQ(root_table->many_vectors()->size(), kNumVectors);
434
435 // Spot check one of the vectors.
436 TEST_EQ(root_table->many_vectors()->Get(12)->vector()->size(), 20);
437 TEST_EQ(root_table->many_vectors()->Get(12)->vector()->Get(0), 42);
438 TEST_EQ(root_table->many_vectors()->Get(12)->vector()->Get(19), 18);
439}
440
441} // namespace tests
442} // namespace flatbuffers
View as plain text