• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "monster_test.h"
2 
3 #include <limits>
4 #include <vector>
5 
6 #include "flatbuffers/base.h"
7 #include "flatbuffers/flatbuffer_builder.h"
8 #include "flatbuffers/flatbuffers.h"
9 #include "flatbuffers/idl.h"
10 #include "flatbuffers/registry.h"
11 #include "flatbuffers/verifier.h"
12 #include "is_quiet_nan.h"
13 #include "monster_extra_generated.h"
14 #include "monster_test_generated.h"
15 #include "test_assert.h"
16 
17 namespace flatbuffers {
18 namespace tests {
19 
20 // Shortcuts for the infinity.
21 static const auto infinity_f = std::numeric_limits<float>::infinity();
22 static const auto infinity_d = std::numeric_limits<double>::infinity();
23 
24 using namespace MyGame::Example;
25 
26 // example of how to build up a serialized buffer algorithmically:
CreateFlatBufferTest(std::string & buffer)27 flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) {
28   flatbuffers::FlatBufferBuilder builder;
29 
30   auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
31 
32   auto name = builder.CreateString("MyMonster");
33 
34   // Use the initializer_list specialization of CreateVector.
35   auto inventory =
36       builder.CreateVector<uint8_t>({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
37 
38   // Alternatively, create the vector first, and fill in data later:
39   // unsigned char *inv_buf = nullptr;
40   // auto inventory = builder.CreateUninitializedVector<unsigned char>(
41   //                                                              10, &inv_buf);
42   // memcpy(inv_buf, inv_data, 10);
43 
44   Test tests[] = { Test(10, 20), Test(30, 40) };
45   auto testv = builder.CreateVectorOfStructs(tests, 2);
46 
47   // Create a vector of structures from a lambda.
48   auto testv2 = builder.CreateVectorOfStructs<Test>(
49       2, [&](size_t i, Test *s) -> void { *s = tests[i]; });
50 
51   // create monster with very few fields set:
52   // (same functionality as CreateMonster below, but sets fields manually)
53   flatbuffers::Offset<Monster> mlocs[3];
54   auto fred = builder.CreateString("Fred");
55   auto barney = builder.CreateString("Barney");
56   auto wilma = builder.CreateString("Wilma");
57   MonsterBuilder mb1(builder);
58   mb1.add_name(fred);
59   mlocs[0] = mb1.Finish();
60   MonsterBuilder mb2(builder);
61   mb2.add_name(barney);
62   mb2.add_hp(1000);
63   mlocs[1] = mb2.Finish();
64   MonsterBuilder mb3(builder);
65   mb3.add_name(wilma);
66   mlocs[2] = mb3.Finish();
67 
68   // Create an array of strings. Also test string pooling, and lambdas.
69   auto vecofstrings =
70       builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(
71           4,
72           [](size_t i, flatbuffers::FlatBufferBuilder *b)
73               -> flatbuffers::Offset<flatbuffers::String> {
74             static const char *names[] = { "bob", "fred", "bob", "fred" };
75             return b->CreateSharedString(names[i]);
76           },
77           &builder);
78 
79   // Creating vectors of strings in one convenient call.
80   std::vector<std::string> names2;
81   names2.push_back("jane");
82   names2.push_back("mary");
83   auto vecofstrings2 = builder.CreateVectorOfStrings(names2);
84 
85   // Creating vectors from types that are different from std::string
86   std::vector<const char *> names3;
87   names3.push_back("foo");
88   names3.push_back("bar");
89   builder.CreateVectorOfStrings(names3);  // Also an accepted type
90 
91 #ifdef FLATBUFFERS_HAS_STRING_VIEW
92   std::vector<flatbuffers::string_view> names4;
93   names3.push_back("baz");
94   names3.push_back("quux");
95   builder.CreateVectorOfStrings(names4);  // Also an accepted type
96 #endif
97 
98   // Make sure the template deduces an initializer as std::vector<std::string>
99   builder.CreateVectorOfStrings({ "hello", "world" });
100 
101   // Create many vectors of strings
102   std::vector<std::string> manyNames;
103   for (auto i = 0; i < 100; i++) { manyNames.push_back("john_doe"); }
104   auto manyNamesVec = builder.CreateVectorOfStrings(manyNames);
105   TEST_EQ(false, manyNamesVec.IsNull());
106   auto manyNamesVec2 =
107       builder.CreateVectorOfStrings(manyNames.cbegin(), manyNames.cend());
108   TEST_EQ(false, manyNamesVec2.IsNull());
109 
110   // Create an array of sorted tables, can be used with binary search when read:
111   auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3);
112 
113   // Create an array of sorted structs,
114   // can be used with binary search when read:
115   std::vector<Ability> abilities;
116   abilities.push_back(Ability(4, 40));
117   abilities.push_back(Ability(3, 30));
118   abilities.push_back(Ability(2, 20));
119   abilities.push_back(Ability(0, 0));
120   auto vecofstructs = builder.CreateVectorOfSortedStructs(&abilities);
121 
122   flatbuffers::Offset<Stat> mlocs_stats[1];
123   auto miss = builder.CreateString("miss");
124   StatBuilder mb_miss(builder);
125   mb_miss.add_id(miss);
126   mb_miss.add_val(0);
127   mb_miss.add_count(0);  // key
128   mlocs_stats[0] = mb_miss.Finish();
129   auto vec_of_stats = builder.CreateVectorOfSortedTables(mlocs_stats, 1);
130 
131   // Create a nested FlatBuffer.
132   // Nested FlatBuffers are stored in a ubyte vector, which can be convenient
133   // since they can be memcpy'd around much easier than other FlatBuffer
134   // values. They have little overhead compared to storing the table directly.
135   // As a test, create a mostly empty Monster buffer:
136   flatbuffers::FlatBufferBuilder nested_builder;
137   auto nmloc = CreateMonster(nested_builder, nullptr, 0, 0,
138                              nested_builder.CreateString("NestedMonster"));
139   FinishMonsterBuffer(nested_builder, nmloc);
140   // Now we can store the buffer in the parent. Note that by default, vectors
141   // are only aligned to their elements or size field, so in this case if the
142   // buffer contains 64-bit elements, they may not be correctly aligned. We fix
143   // that with:
144   builder.ForceVectorAlignment(nested_builder.GetSize(), sizeof(uint8_t),
145                                nested_builder.GetBufferMinAlignment());
146   // If for whatever reason you don't have the nested_builder available, you
147   // can substitute flatbuffers::largest_scalar_t (64-bit) for the alignment, or
148   // the largest force_align value in your schema if you're using it.
149   auto nested_flatbuffer_vector = builder.CreateVector(
150       nested_builder.GetBufferPointer(), nested_builder.GetSize());
151 
152   // Test a nested FlexBuffer:
153   flexbuffers::Builder flexbuild;
154   flexbuild.Int(1234);
155   flexbuild.Finish();
156   auto flex = builder.CreateVector(flexbuild.GetBuffer());
157   // Test vector of enums.
158   Color colors[] = { Color_Blue, Color_Green };
159   // We use this special creation function because we have an array of
160   // pre-C++11 (enum class) enums whose size likely is int, yet its declared
161   // type in the schema is byte.
162   auto vecofcolors = builder.CreateVectorScalarCast<uint8_t, Color>(colors, 2);
163 
164   // shortcut for creating monster with all fields set:
165   auto mloc = CreateMonster(
166       builder, &vec, 150, 80, name, inventory, Color_Blue, Any_Monster,
167       mlocs[1].Union(),  // Store a union.
168       testv, vecofstrings, vecoftables, 0, nested_flatbuffer_vector, 0, false,
169       0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, vecofstrings2,
170       vecofstructs, flex, testv2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171       AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors,
172       MyGame::Example::Race_None, 0, vec_of_stats);
173 
174   FinishMonsterBuffer(builder, mloc);
175 
176   // clang-format off
177   #ifdef FLATBUFFERS_TEST_VERBOSE
178   // print byte data for debugging:
179   auto p = builder.GetBufferPointer();
180   for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
181     printf("%d ", p[i]);
182   #endif
183   // clang-format on
184 
185   // return the buffer for the caller to use.
186   auto bufferpointer =
187       reinterpret_cast<const char *>(builder.GetBufferPointer());
188   buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
189 
190   return builder.Release();
191 }
192 
193 //  example of accessing a buffer loaded in memory:
AccessFlatBufferTest(const uint8_t * flatbuf,size_t length,bool pooled)194 void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length, bool pooled) {
195   // First, verify the buffers integrity (optional)
196   flatbuffers::Verifier verifier(flatbuf, length);
197   std::vector<uint8_t> flex_reuse_tracker;
198   verifier.SetFlexReuseTracker(&flex_reuse_tracker);
199   TEST_EQ(VerifyMonsterBuffer(verifier), true);
200 
201   // clang-format off
202   #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
203     std::vector<uint8_t> test_buff;
204     test_buff.resize(length * 2);
205     std::memcpy(&test_buff[0], flatbuf, length);
206     std::memcpy(&test_buff[length], flatbuf, length);
207 
208     flatbuffers::Verifier verifier1(&test_buff[0], length);
209     TEST_EQ(VerifyMonsterBuffer(verifier1), true);
210     TEST_EQ(verifier1.GetComputedSize(), length);
211 
212     flatbuffers::Verifier verifier2(&test_buff[length], length);
213     TEST_EQ(VerifyMonsterBuffer(verifier2), true);
214     TEST_EQ(verifier2.GetComputedSize(), length);
215   #endif
216   // clang-format on
217 
218   TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
219   TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
220   TEST_EQ(strcmp(MonsterExtension(), "mon"), 0);
221 
222   // Access the buffer from the root.
223   auto monster = GetMonster(flatbuf);
224 
225   TEST_EQ(monster->hp(), 80);
226   TEST_EQ(monster->mana(), 150);  // default
227   TEST_EQ_STR(monster->name()->c_str(), "MyMonster");
228   // Can't access the following field, it is deprecated in the schema,
229   // which means accessors are not generated:
230   // monster.friendly()
231 
232   auto pos = monster->pos();
233   TEST_NOTNULL(pos);
234   TEST_EQ(pos->z(), 3);
235   TEST_EQ(pos->test3().a(), 10);
236   TEST_EQ(pos->test3().b(), 20);
237 
238   auto inventory = monster->inventory();
239   TEST_EQ(VectorLength(inventory), 10UL);  // Works even if inventory is null.
240   TEST_NOTNULL(inventory);
241   unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
242   // Check compatibilty of iterators with STL.
243   std::vector<unsigned char> inv_vec(inventory->begin(), inventory->end());
244   size_t n = 0;
245   for (auto it = inventory->begin(); it != inventory->end(); ++it, ++n) {
246     auto indx = it - inventory->begin();
247     TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
248     TEST_EQ(*it, inv_data[indx]);
249   }
250   TEST_EQ(n, inv_vec.size());
251 
252   n = 0;
253   for (auto it = inventory->cbegin(); it != inventory->cend(); ++it, ++n) {
254     auto indx = it - inventory->cbegin();
255     TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
256     TEST_EQ(*it, inv_data[indx]);
257   }
258   TEST_EQ(n, inv_vec.size());
259 
260   n = 0;
261   for (auto it = inventory->rbegin(); it != inventory->rend(); ++it, ++n) {
262     auto indx = inventory->rend() - it - 1;
263     TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
264     TEST_EQ(*it, inv_data[indx]);
265   }
266   TEST_EQ(n, inv_vec.size());
267 
268   n = 0;
269   for (auto it = inventory->crbegin(); it != inventory->crend(); ++it, ++n) {
270     auto indx = inventory->crend() - it - 1;
271     TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
272     TEST_EQ(*it, inv_data[indx]);
273   }
274   TEST_EQ(n, inv_vec.size());
275 
276   TEST_EQ(monster->color(), Color_Blue);
277 
278   // Example of accessing a union:
279   TEST_EQ(monster->test_type(), Any_Monster);  // First make sure which it is.
280   auto monster2 = reinterpret_cast<const Monster *>(monster->test());
281   TEST_NOTNULL(monster2);
282   TEST_EQ_STR(monster2->name()->c_str(), "Fred");
283 
284   // Example of accessing a vector of strings:
285   auto vecofstrings = monster->testarrayofstring();
286   TEST_EQ(vecofstrings->size(), 4U);
287   TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
288   TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
289   if (pooled) {
290     // These should have pointer equality because of string pooling.
291     TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
292     TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
293   }
294 
295   auto vecofstrings2 = monster->testarrayofstring2();
296   if (vecofstrings2) {
297     TEST_EQ(vecofstrings2->size(), 2U);
298     TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane");
299     TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary");
300   }
301 
302   // Example of accessing a vector of tables:
303   auto vecoftables = monster->testarrayoftables();
304   TEST_EQ(vecoftables->size(), 3U);
305   for (auto it = vecoftables->begin(); it != vecoftables->end(); ++it) {
306     TEST_EQ(strlen(it->name()->c_str()) >= 4, true);
307   }
308   TEST_EQ_STR(vecoftables->Get(0)->name()->c_str(), "Barney");
309   TEST_EQ(vecoftables->Get(0)->hp(), 1000);
310   TEST_EQ_STR(vecoftables->Get(1)->name()->c_str(), "Fred");
311   TEST_EQ_STR(vecoftables->Get(2)->name()->c_str(), "Wilma");
312   TEST_NOTNULL(vecoftables->LookupByKey("Barney"));
313   TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
314   TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
315 
316   // Verify the same objects are returned for char*-based and string-based
317   // lookups.
318   TEST_EQ(vecoftables->LookupByKey("Barney"),
319           vecoftables->LookupByKey(std::string("Barney")));
320   TEST_EQ(vecoftables->LookupByKey("Fred"),
321           vecoftables->LookupByKey(std::string("Fred")));
322   TEST_EQ(vecoftables->LookupByKey("Wilma"),
323           vecoftables->LookupByKey(std::string("Wilma")));
324 
325 #ifdef FLATBUFFERS_HAS_STRING_VIEW
326   // Tests for LookupByKey with a key that is a truncated
327   // version of a longer, invalid key.
328   const std::string invalid_key = "Barney123";
329   std::string_view valid_truncated_key = invalid_key;
330   valid_truncated_key.remove_suffix(3);  // "Barney"
331   TEST_NOTNULL(vecoftables->LookupByKey(valid_truncated_key));
332   TEST_EQ(vecoftables->LookupByKey("Barney"),
333           vecoftables->LookupByKey(valid_truncated_key));
334 
335   // Tests for LookupByKey with a key that is a truncated
336   // version of a longer, valid key.
337   const std::string valid_key = "Barney";
338   std::string_view invalid_truncated_key = valid_key;
339   invalid_truncated_key.remove_suffix(3);  // "Bar"
340   TEST_NULL(vecoftables->LookupByKey(invalid_truncated_key));
341 #endif  // FLATBUFFERS_HAS_STRING_VIEW
342 
343   // Test accessing a vector of sorted structs
344   auto vecofstructs = monster->testarrayofsortedstruct();
345   if (vecofstructs) {  // not filled in monster_test.bfbs
346     for (flatbuffers::uoffset_t i = 0; i < vecofstructs->size() - 1; i++) {
347       auto left = vecofstructs->Get(i);
348       auto right = vecofstructs->Get(i + 1);
349       TEST_EQ(true, (left->KeyCompareLessThan(right)));
350     }
351     TEST_NOTNULL(vecofstructs->LookupByKey(0));  // test default value
352     TEST_NOTNULL(vecofstructs->LookupByKey(3));
353     TEST_EQ(static_cast<const Ability *>(nullptr),
354             vecofstructs->LookupByKey(5));
355   }
356 
357   if (auto vec_of_stat = monster->scalar_key_sorted_tables()) {
358     auto stat_0 = vec_of_stat->LookupByKey(static_cast<uint16_t>(0u));
359     TEST_NOTNULL(stat_0);
360     TEST_NOTNULL(stat_0->id());
361     TEST_EQ(0, stat_0->count());
362     TEST_EQ_STR("miss", stat_0->id()->c_str());
363   }
364 
365   // Test nested FlatBuffers if available:
366   auto nested_buffer = monster->testnestedflatbuffer();
367   if (nested_buffer) {
368     // nested_buffer is a vector of bytes you can memcpy. However, if you
369     // actually want to access the nested data, this is a convenient
370     // accessor that directly gives you the root table:
371     auto nested_monster = monster->testnestedflatbuffer_nested_root();
372     TEST_EQ_STR(nested_monster->name()->c_str(), "NestedMonster");
373   }
374 
375   // Test flexbuffer if available:
376   auto flex = monster->flex();
377   // flex is a vector of bytes you can memcpy etc.
378   TEST_EQ(flex->size(), 4);  // Encoded FlexBuffer bytes.
379   // However, if you actually want to access the nested data, this is a
380   // convenient accessor that directly gives you the root value:
381   TEST_EQ(monster->flex_flexbuffer_root().AsInt16(), 1234);
382 
383   // Test vector of enums:
384   auto colors = monster->vector_of_enums();
385   if (colors) {
386     TEST_EQ(colors->size(), 2);
387     TEST_EQ(colors->Get(0), Color_Blue);
388     TEST_EQ(colors->Get(1), Color_Green);
389   }
390 
391   // Since Flatbuffers uses explicit mechanisms to override the default
392   // compiler alignment, double check that the compiler indeed obeys them:
393   // (Test consists of a short and byte):
394   TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
395   TEST_EQ(sizeof(Test), 4UL);
396 
397   const flatbuffers::Vector<const Test *> *tests_array[] = {
398     monster->test4(),
399     monster->test5(),
400   };
401   for (size_t i = 0; i < sizeof(tests_array) / sizeof(tests_array[0]); ++i) {
402     auto tests = tests_array[i];
403     TEST_NOTNULL(tests);
404     auto test_0 = tests->Get(0);
405     auto test_1 = tests->Get(1);
406     TEST_EQ(test_0->a(), 10);
407     TEST_EQ(test_0->b(), 20);
408     TEST_EQ(test_1->a(), 30);
409     TEST_EQ(test_1->b(), 40);
410     for (auto it = tests->begin(); it != tests->end(); ++it) {
411       TEST_EQ(it->a() == 10 || it->a() == 30, true);  // Just testing iterators.
412     }
413   }
414 
415   // Checking for presence of fields:
416   TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
417   TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
418 
419   // Obtaining a buffer from a root:
420   TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
421 }
422 
423 // Change a FlatBuffer in-place, after it has been constructed.
MutateFlatBuffersTest(uint8_t * flatbuf,std::size_t length)424 void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
425   // Get non-const pointer to root.
426   auto monster = GetMutableMonster(flatbuf);
427 
428   // Each of these tests mutates, then tests, then set back to the original,
429   // so we can test that the buffer in the end still passes our original test.
430   auto hp_ok = monster->mutate_hp(10);
431   TEST_EQ(hp_ok, true);  // Field was present.
432   TEST_EQ(monster->hp(), 10);
433   // Mutate to default value
434   auto hp_ok_default = monster->mutate_hp(100);
435   TEST_EQ(hp_ok_default, true);  // Field was present.
436   TEST_EQ(monster->hp(), 100);
437   // Test that mutate to default above keeps field valid for further mutations
438   auto hp_ok_2 = monster->mutate_hp(20);
439   TEST_EQ(hp_ok_2, true);
440   TEST_EQ(monster->hp(), 20);
441   monster->mutate_hp(80);
442 
443   // Monster originally at 150 mana (default value)
444   auto mana_default_ok = monster->mutate_mana(150);  // Mutate to default value.
445   TEST_EQ(mana_default_ok,
446           true);  // Mutation should succeed, because default value.
447   TEST_EQ(monster->mana(), 150);
448   auto mana_ok = monster->mutate_mana(10);
449   TEST_EQ(mana_ok, false);  // Field was NOT present, because default value.
450   TEST_EQ(monster->mana(), 150);
451 
452   // Mutate structs.
453   auto pos = monster->mutable_pos();
454   auto &test3 = pos->mutable_test3();  // Struct inside a struct.
455   test3.mutate_a(50);                  // Struct fields never fail.
456   TEST_EQ(test3.a(), 50);
457   test3.mutate_a(10);
458 
459   // Mutate vectors.
460   auto inventory = monster->mutable_inventory();
461   inventory->Mutate(9, 100);
462   TEST_EQ(inventory->Get(9), 100);
463   inventory->Mutate(9, 9);
464 
465   auto tables = monster->mutable_testarrayoftables();
466   auto first = tables->GetMutableObject(0);
467   TEST_EQ(first->hp(), 1000);
468   first->mutate_hp(0);
469   TEST_EQ(first->hp(), 0);
470   first->mutate_hp(1000);
471 
472   // Test for each loop over mutable entries
473   for (auto item : *tables) {
474     TEST_EQ(item->hp(), 1000);
475     item->mutate_hp(0);
476     TEST_EQ(item->hp(), 0);
477     item->mutate_hp(1000);
478     break;  // one iteration is enough, just testing compilation
479   }
480 
481   // Mutate via LookupByKey
482   TEST_NOTNULL(tables->MutableLookupByKey("Barney"));
483   TEST_EQ(static_cast<Monster *>(nullptr),
484           tables->MutableLookupByKey("DoesntExist"));
485   TEST_EQ(tables->MutableLookupByKey("Barney")->hp(), 1000);
486   TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(0), true);
487   TEST_EQ(tables->LookupByKey("Barney")->hp(), 0);
488   TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(1000), true);
489 
490   // Run the verifier and the regular test to make sure we didn't trample on
491   // anything.
492   AccessFlatBufferTest(flatbuf, length);
493 }
494 
495 // Unpack a FlatBuffer into objects.
ObjectFlatBuffersTest(uint8_t * flatbuf)496 void ObjectFlatBuffersTest(uint8_t *flatbuf) {
497   // Optional: we can specify resolver and rehasher functions to turn hashed
498   // strings into object pointers and back, to implement remote references
499   // and such.
500   auto resolver = flatbuffers::resolver_function_t(
501       [](void **pointer_adr, flatbuffers::hash_value_t hash) {
502         (void)pointer_adr;
503         (void)hash;
504         // Don't actually do anything, leave variable null.
505       });
506   auto rehasher = flatbuffers::rehasher_function_t(
507       [](void *pointer) -> flatbuffers::hash_value_t {
508         (void)pointer;
509         return 0;
510       });
511 
512   // Turn a buffer into C++ objects.
513   auto monster1 = UnPackMonster(flatbuf, &resolver);
514 
515   // Re-serialize the data.
516   flatbuffers::FlatBufferBuilder fbb1;
517   fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
518               MonsterIdentifier());
519 
520   // Unpack again, and re-serialize again.
521   auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
522   flatbuffers::FlatBufferBuilder fbb2;
523   fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
524               MonsterIdentifier());
525 
526   // Now we've gone full round-trip, the two buffers should match.
527   const auto len1 = fbb1.GetSize();
528   const auto len2 = fbb2.GetSize();
529   TEST_EQ(len1, len2);
530   TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), len1), 0);
531 
532   // Test it with the original buffer test to make sure all data survived.
533   AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
534 
535   // Test accessing fields, similar to AccessFlatBufferTest above.
536   CheckMonsterObject(monster2.get());
537 
538   // Test object copy.
539   MonsterT monster3 = *monster2;
540   flatbuffers::FlatBufferBuilder fbb3;
541   fbb3.Finish(CreateMonster(fbb3, &monster3, &rehasher), MonsterIdentifier());
542   const auto len3 = fbb3.GetSize();
543   TEST_EQ(len2, len3);
544   TEST_EQ(memcmp(fbb2.GetBufferPointer(), fbb3.GetBufferPointer(), len2), 0);
545   // Delete monster1 and monster2, then test accessing fields in monster3.
546   monster1.reset();
547   monster2.reset();
548   CheckMonsterObject(&monster3);
549 }
550 
551 // Utility function to check a Monster object.
CheckMonsterObject(MonsterT * monster2)552 void CheckMonsterObject(MonsterT *monster2) {
553   TEST_EQ(monster2->hp, 80);
554   TEST_EQ(monster2->mana, 150);  // default
555   TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
556 
557   auto &pos = monster2->pos;
558   TEST_NOTNULL(pos);
559   TEST_EQ(pos->z(), 3);
560   TEST_EQ(pos->test3().a(), 10);
561   TEST_EQ(pos->test3().b(), 20);
562 
563   auto &inventory = monster2->inventory;
564   TEST_EQ(inventory.size(), 10UL);
565   unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
566   for (auto it = inventory.begin(); it != inventory.end(); ++it)
567     TEST_EQ(*it, inv_data[it - inventory.begin()]);
568 
569   TEST_EQ(monster2->color, Color_Blue);
570 
571   auto monster3 = monster2->test.AsMonster();
572   TEST_NOTNULL(monster3);
573   TEST_EQ_STR(monster3->name.c_str(), "Fred");
574 
575   auto &vecofstrings = monster2->testarrayofstring;
576   TEST_EQ(vecofstrings.size(), 4U);
577   TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
578   TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
579 
580   auto &vecofstrings2 = monster2->testarrayofstring2;
581   TEST_EQ(vecofstrings2.size(), 2U);
582   TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
583   TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
584 
585   auto &vecoftables = monster2->testarrayoftables;
586   TEST_EQ(vecoftables.size(), 3U);
587   TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
588   TEST_EQ(vecoftables[0]->hp, 1000);
589   TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
590   TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
591 
592   auto &tests = monster2->test4;
593   TEST_EQ(tests[0].a(), 10);
594   TEST_EQ(tests[0].b(), 20);
595   TEST_EQ(tests[1].a(), 30);
596   TEST_EQ(tests[1].b(), 40);
597 }
598 
599 // Prefix a FlatBuffer with a size field.
SizePrefixedTest()600 void SizePrefixedTest() {
601   // Create size prefixed buffer.
602   flatbuffers::FlatBufferBuilder fbb;
603   FinishSizePrefixedMonsterBuffer(
604       fbb, CreateMonster(fbb, nullptr, 200, 300, fbb.CreateString("bob")));
605 
606   // Verify it.
607   flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
608   TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier), true);
609 
610   // The prefixed size doesn't include itself, so substract the size of the
611   // prefix
612   TEST_EQ(GetPrefixedSize(fbb.GetBufferPointer()),
613           fbb.GetSize() - sizeof(uoffset_t));
614 
615   // Getting the buffer length does include the prefix size, so it should be the
616   // full lenght.
617   TEST_EQ(GetSizePrefixedBufferLength(fbb.GetBufferPointer()), fbb.GetSize());
618 
619   // Access it.
620   auto m = GetSizePrefixedMonster(fbb.GetBufferPointer());
621   TEST_EQ(m->mana(), 200);
622   TEST_EQ(m->hp(), 300);
623   TEST_EQ_STR(m->name()->c_str(), "bob");
624 
625   {
626     // Verify that passing a larger size is OK, but not a smaller
627     flatbuffers::Verifier verifier_larger(fbb.GetBufferPointer(),
628                                           fbb.GetSize() + 10);
629     TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_larger), true);
630 
631     flatbuffers::Verifier verifier_smaller(fbb.GetBufferPointer(),
632                                            fbb.GetSize() - 10);
633     TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_smaller), false);
634   }
635 }
636 
TestMonsterExtraFloats(const std::string & tests_data_path)637 void TestMonsterExtraFloats(const std::string &tests_data_path) {
638 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
639   TEST_EQ(is_quiet_nan(1.0), false);
640   TEST_EQ(is_quiet_nan(infinity_d), false);
641   TEST_EQ(is_quiet_nan(-infinity_f), false);
642   TEST_EQ(is_quiet_nan(std::numeric_limits<float>::quiet_NaN()), true);
643   TEST_EQ(is_quiet_nan(std::numeric_limits<double>::quiet_NaN()), true);
644 
645   using namespace flatbuffers;
646   using namespace MyGame;
647   // Load FlatBuffer schema (.fbs) from disk.
648   std::string schemafile;
649   TEST_EQ(LoadFile((tests_data_path + "monster_extra.fbs").c_str(), false,
650                    &schemafile),
651           true);
652   // Parse schema first, so we can use it to parse the data after.
653   Parser parser;
654   auto include_test_path = ConCatPathFileName(tests_data_path, "include_test");
655   const char *include_directories[] = { tests_data_path.c_str(),
656                                         include_test_path.c_str(), nullptr };
657   TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
658   // Create empty extra and store to json.
659   parser.opts.output_default_scalars_in_json = true;
660   parser.opts.output_enum_identifiers = true;
661   FlatBufferBuilder builder;
662   const auto def_root = MonsterExtraBuilder(builder).Finish();
663   FinishMonsterExtraBuffer(builder, def_root);
664   const auto def_obj = builder.GetBufferPointer();
665   const auto def_extra = GetMonsterExtra(def_obj);
666   TEST_NOTNULL(def_extra);
667   TEST_EQ(is_quiet_nan(def_extra->f0()), true);
668   TEST_EQ(is_quiet_nan(def_extra->f1()), true);
669   TEST_EQ(def_extra->f2(), +infinity_f);
670   TEST_EQ(def_extra->f3(), -infinity_f);
671   TEST_EQ(is_quiet_nan(def_extra->d0()), true);
672   TEST_EQ(is_quiet_nan(def_extra->d1()), true);
673   TEST_EQ(def_extra->d2(), +infinity_d);
674   TEST_EQ(def_extra->d3(), -infinity_d);
675   std::string jsongen;
676   auto result = GenText(parser, def_obj, &jsongen);
677   TEST_NULL(result);
678   // Check expected default values.
679   TEST_EQ(std::string::npos != jsongen.find("f0: nan"), true);
680   TEST_EQ(std::string::npos != jsongen.find("f1: nan"), true);
681   TEST_EQ(std::string::npos != jsongen.find("f2: inf"), true);
682   TEST_EQ(std::string::npos != jsongen.find("f3: -inf"), true);
683   TEST_EQ(std::string::npos != jsongen.find("d0: nan"), true);
684   TEST_EQ(std::string::npos != jsongen.find("d1: nan"), true);
685   TEST_EQ(std::string::npos != jsongen.find("d2: inf"), true);
686   TEST_EQ(std::string::npos != jsongen.find("d3: -inf"), true);
687   // Parse 'mosterdata_extra.json'.
688   const auto extra_base = tests_data_path + "monsterdata_extra";
689   jsongen = "";
690   TEST_EQ(LoadFile((extra_base + ".json").c_str(), false, &jsongen), true);
691   TEST_EQ(parser.Parse(jsongen.c_str()), true);
692   const auto test_file = parser.builder_.GetBufferPointer();
693   const auto test_size = parser.builder_.GetSize();
694   Verifier verifier(test_file, test_size);
695   TEST_ASSERT(VerifyMonsterExtraBuffer(verifier));
696   const auto extra = GetMonsterExtra(test_file);
697   TEST_NOTNULL(extra);
698   TEST_EQ(is_quiet_nan(extra->f0()), true);
699   TEST_EQ(is_quiet_nan(extra->f1()), true);
700   TEST_EQ(extra->f2(), +infinity_f);
701   TEST_EQ(extra->f3(), -infinity_f);
702   TEST_EQ(is_quiet_nan(extra->d0()), true);
703   TEST_EQ(extra->d1(), +infinity_d);
704   TEST_EQ(extra->d2(), -infinity_d);
705   TEST_EQ(is_quiet_nan(extra->d3()), true);
706   TEST_NOTNULL(extra->fvec());
707   TEST_EQ(extra->fvec()->size(), 4);
708   TEST_EQ(extra->fvec()->Get(0), 1.0f);
709   TEST_EQ(extra->fvec()->Get(1), -infinity_f);
710   TEST_EQ(extra->fvec()->Get(2), +infinity_f);
711   TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true);
712   TEST_NOTNULL(extra->dvec());
713   TEST_EQ(extra->dvec()->size(), 4);
714   TEST_EQ(extra->dvec()->Get(0), 2.0);
715   TEST_EQ(extra->dvec()->Get(1), +infinity_d);
716   TEST_EQ(extra->dvec()->Get(2), -infinity_d);
717   TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true);
718 #endif
719 }
720 
EnumNamesTest()721 void EnumNamesTest() {
722   TEST_EQ_STR("Red", EnumNameColor(Color_Red));
723   TEST_EQ_STR("Green", EnumNameColor(Color_Green));
724   TEST_EQ_STR("Blue", EnumNameColor(Color_Blue));
725   // Check that Color to string don't crash while decode a mixture of Colors.
726   // 1) Example::Color enum is enum with unfixed underlying type.
727   // 2) Valid enum range: [0; 2^(ceil(log2(Color_ANY))) - 1].
728   // Consequence: A value is out of this range will lead to UB (since C++17).
729   // For details see C++17 standard or explanation on the SO:
730   // stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class
731   TEST_EQ_STR("", EnumNameColor(static_cast<Color>(0)));
732   TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY - 1)));
733   TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY + 1)));
734 }
735 
TypeAliasesTest()736 void TypeAliasesTest() {
737   flatbuffers::FlatBufferBuilder builder;
738 
739   builder.Finish(CreateTypeAliases(
740       builder, flatbuffers::numeric_limits<int8_t>::min(),
741       flatbuffers::numeric_limits<uint8_t>::max(),
742       flatbuffers::numeric_limits<int16_t>::min(),
743       flatbuffers::numeric_limits<uint16_t>::max(),
744       flatbuffers::numeric_limits<int32_t>::min(),
745       flatbuffers::numeric_limits<uint32_t>::max(),
746       flatbuffers::numeric_limits<int64_t>::min(),
747       flatbuffers::numeric_limits<uint64_t>::max(), 2.3f, 2.3));
748 
749   auto p = builder.GetBufferPointer();
750   auto ta = flatbuffers::GetRoot<TypeAliases>(p);
751 
752   TEST_EQ(ta->i8(), flatbuffers::numeric_limits<int8_t>::min());
753   TEST_EQ(ta->u8(), flatbuffers::numeric_limits<uint8_t>::max());
754   TEST_EQ(ta->i16(), flatbuffers::numeric_limits<int16_t>::min());
755   TEST_EQ(ta->u16(), flatbuffers::numeric_limits<uint16_t>::max());
756   TEST_EQ(ta->i32(), flatbuffers::numeric_limits<int32_t>::min());
757   TEST_EQ(ta->u32(), flatbuffers::numeric_limits<uint32_t>::max());
758   TEST_EQ(ta->i64(), flatbuffers::numeric_limits<int64_t>::min());
759   TEST_EQ(ta->u64(), flatbuffers::numeric_limits<uint64_t>::max());
760   TEST_EQ(ta->f32(), 2.3f);
761   TEST_EQ(ta->f64(), 2.3);
762   using namespace flatbuffers;  // is_same
763   static_assert(is_same<decltype(ta->i8()), int8_t>::value, "invalid type");
764   static_assert(is_same<decltype(ta->i16()), int16_t>::value, "invalid type");
765   static_assert(is_same<decltype(ta->i32()), int32_t>::value, "invalid type");
766   static_assert(is_same<decltype(ta->i64()), int64_t>::value, "invalid type");
767   static_assert(is_same<decltype(ta->u8()), uint8_t>::value, "invalid type");
768   static_assert(is_same<decltype(ta->u16()), uint16_t>::value, "invalid type");
769   static_assert(is_same<decltype(ta->u32()), uint32_t>::value, "invalid type");
770   static_assert(is_same<decltype(ta->u64()), uint64_t>::value, "invalid type");
771   static_assert(is_same<decltype(ta->f32()), float>::value, "invalid type");
772   static_assert(is_same<decltype(ta->f64()), double>::value, "invalid type");
773 }
774 
775 // example of parsing text straight into a buffer, and generating
776 // text back from it:
ParseAndGenerateTextTest(const std::string & tests_data_path,bool binary)777 void ParseAndGenerateTextTest(const std::string &tests_data_path, bool binary) {
778   // load FlatBuffer schema (.fbs) and JSON from disk
779   std::string schemafile;
780   std::string jsonfile;
781   TEST_EQ(flatbuffers::LoadFile(
782               (tests_data_path + "monster_test." + (binary ? "bfbs" : "fbs"))
783                   .c_str(),
784               binary, &schemafile),
785           true);
786   TEST_EQ(flatbuffers::LoadFile(
787               (tests_data_path + "monsterdata_test.golden").c_str(), false,
788               &jsonfile),
789           true);
790 
791   auto include_test_path =
792       flatbuffers::ConCatPathFileName(tests_data_path, "include_test");
793   const char *include_directories[] = { tests_data_path.c_str(),
794                                         include_test_path.c_str(), nullptr };
795 
796   // parse schema first, so we can use it to parse the data after
797   flatbuffers::Parser parser;
798   if (binary) {
799     flatbuffers::Verifier verifier(
800         reinterpret_cast<const uint8_t *>(schemafile.c_str()),
801         schemafile.size());
802     TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
803     // auto schema = reflection::GetSchema(schemafile.c_str());
804     TEST_EQ(parser.Deserialize(
805                 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
806                 schemafile.size()),
807             true);
808   } else {
809     TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
810   }
811   TEST_EQ(parser.ParseJson(jsonfile.c_str()), true);
812 
813   // here, parser.builder_ contains a binary buffer that is the parsed data.
814 
815   // First, verify it, just in case:
816   flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
817                                  parser.builder_.GetSize());
818   TEST_EQ(VerifyMonsterBuffer(verifier), true);
819 
820   AccessFlatBufferTest(parser.builder_.GetBufferPointer(),
821                        parser.builder_.GetSize(), false);
822 
823   // to ensure it is correct, we now generate text back from the binary,
824   // and compare the two:
825   std::string jsongen;
826   auto result = GenText(parser, parser.builder_.GetBufferPointer(), &jsongen);
827   TEST_NULL(result);
828   TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
829 
830   // We can also do the above using the convenient Registry that knows about
831   // a set of file_identifiers mapped to schemas.
832   flatbuffers::Registry registry;
833   // Make sure schemas can find their includes.
834   registry.AddIncludeDirectory(tests_data_path.c_str());
835   registry.AddIncludeDirectory(include_test_path.c_str());
836   // Call this with many schemas if possible.
837   registry.Register(MonsterIdentifier(),
838                     (tests_data_path + "monster_test.fbs").c_str());
839   // Now we got this set up, we can parse by just specifying the identifier,
840   // the correct schema will be loaded on the fly:
841   auto buf = registry.TextToFlatBuffer(jsonfile.c_str(), MonsterIdentifier());
842   // If this fails, check registry.lasterror_.
843   TEST_NOTNULL(buf.data());
844   // Test the buffer, to be sure:
845   AccessFlatBufferTest(buf.data(), buf.size(), false);
846   // We can use the registry to turn this back into text, in this case it
847   // will get the file_identifier from the binary:
848   std::string text;
849   auto ok = registry.FlatBufferToText(buf.data(), buf.size(), &text);
850   // If this fails, check registry.lasterror_.
851   TEST_EQ(ok, true);
852   TEST_EQ_STR(text.c_str(), jsonfile.c_str());
853 
854   // Generate text for UTF-8 strings without escapes.
855   std::string jsonfile_utf8;
856   TEST_EQ(flatbuffers::LoadFile((tests_data_path + "unicode_test.json").c_str(),
857                                 false, &jsonfile_utf8),
858           true);
859   TEST_EQ(parser.Parse(jsonfile_utf8.c_str(), include_directories), true);
860   // To ensure it is correct, generate utf-8 text back from the binary.
861   std::string jsongen_utf8;
862   // request natural printing for utf-8 strings
863   parser.opts.natural_utf8 = true;
864   parser.opts.strict_json = true;
865   TEST_NULL(GenText(parser, parser.builder_.GetBufferPointer(), &jsongen_utf8));
866   TEST_EQ_STR(jsongen_utf8.c_str(), jsonfile_utf8.c_str());
867 }
868 
UnPackTo(const uint8_t * flatbuf)869 void UnPackTo(const uint8_t *flatbuf) {
870   // Get a monster that has a name and no enemy
871   auto orig_monster = GetMonster(flatbuf);
872   TEST_EQ_STR(orig_monster->name()->c_str(), "MyMonster");
873   TEST_ASSERT(orig_monster->enemy() == nullptr);
874 
875   // Create an enemy
876   MonsterT *enemy = new MonsterT();
877   enemy->name = "Enemy";
878 
879   // And create another monster owning the enemy,
880   MonsterT mon;
881   mon.name = "I'm monster 1";
882   mon.enemy.reset(enemy);
883   TEST_ASSERT(mon.enemy != nullptr);
884 
885   // Assert that all the Monster objects are correct.
886   TEST_EQ_STR(mon.name.c_str(), "I'm monster 1");
887   TEST_EQ_STR(enemy->name.c_str(), "Enemy");
888   TEST_EQ_STR(mon.enemy->name.c_str(), "Enemy");
889 
890   // Now unpack monster ("MyMonster") into monster
891   orig_monster->UnPackTo(&mon);
892 
893   // Monster name should be from monster
894   TEST_EQ_STR(mon.name.c_str(), "MyMonster");
895 
896   // The monster shouldn't have any enemies, because monster didn't.
897   TEST_ASSERT(mon.enemy == nullptr);
898 }
899 
900 }  // namespace tests
901 }  // namespace flatbuffers
902