• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "flexbuffers_test.h"
2 
3 #include <limits>
4 
5 #include "flatbuffers/flexbuffers.h"
6 #include "flatbuffers/idl.h"
7 #include "is_quiet_nan.h"
8 #include "test_assert.h"
9 
10 namespace flatbuffers {
11 namespace tests {
12 
13 // Shortcuts for the infinity.
14 static const auto infinity_d = std::numeric_limits<double>::infinity();
15 
FlexBuffersTest()16 void FlexBuffersTest() {
17   flexbuffers::Builder slb(512,
18                            flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
19 
20   // Write the equivalent of:
21   // { vec: [ -100, "Fred", 4.0, false ], bar: [ 1, 2, 3 ], bar3: [ 1, 2, 3 ],
22   // foo: 100, bool: true, mymap: { foo: "Fred" } }
23 
24   // It's possible to do this without std::function support as well.
25   slb.Map([&]() {
26     slb.Vector("vec", [&]() {
27       slb += -100;  // Equivalent to slb.Add(-100) or slb.Int(-100);
28       slb += "Fred";
29       slb.IndirectFloat(4.0f);
30       auto i_f = slb.LastValue();
31       uint8_t blob[] = { 77 };
32       slb.Blob(blob, 1);
33       slb += false;
34       slb.ReuseValue(i_f);
35     });
36     int ints[] = { 1, 2, 3 };
37     slb.Vector("bar", ints, 3);
38     slb.FixedTypedVector("bar3", ints, 3);
39     bool bools[] = { true, false, true, false };
40     slb.Vector("bools", bools, 4);
41     slb.Bool("bool", true);
42     slb.Double("foo", 100);
43     slb.Map("mymap", [&]() {
44       slb.String("foo", "Fred");  // Testing key and string reuse.
45     });
46   });
47   slb.Finish();
48 
49   // clang-format off
50   #ifdef FLATBUFFERS_TEST_VERBOSE
51     for (size_t i = 0; i < slb.GetBuffer().size(); i++)
52       printf("%d ", slb.GetBuffer().data()[i]);
53     printf("\n");
54   #endif
55   // clang-format on
56 
57   std::vector<uint8_t> reuse_tracker;
58   TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
59                                     slb.GetBuffer().size(), &reuse_tracker),
60           true);
61 
62   auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
63   TEST_EQ(map.size(), 7);
64   auto vec = map["vec"].AsVector();
65   TEST_EQ(vec.size(), 6);
66   TEST_EQ(vec[0].AsInt64(), -100);
67   TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
68   TEST_EQ(vec[1].AsInt64(), 0);  // Number parsing failed.
69   TEST_EQ(vec[2].AsDouble(), 4.0);
70   TEST_EQ(vec[2].AsString().IsTheEmptyString(), true);  // Wrong Type.
71   TEST_EQ_STR(vec[2].AsString().c_str(), "");     // This still works though.
72   TEST_EQ_STR(vec[2].ToString().c_str(), "4.0");  // Or have it converted.
73   // Few tests for templated version of As.
74   TEST_EQ(vec[0].As<int64_t>(), -100);
75   TEST_EQ_STR(vec[1].As<std::string>().c_str(), "Fred");
76   TEST_EQ(vec[1].As<int64_t>(), 0);  // Number parsing failed.
77   TEST_EQ(vec[2].As<double>(), 4.0);
78   // Test that the blob can be accessed.
79   TEST_EQ(vec[3].IsBlob(), true);
80   auto blob = vec[3].AsBlob();
81   TEST_EQ(blob.size(), 1);
82   TEST_EQ(blob.data()[0], 77);
83   TEST_EQ(vec[4].IsBool(), true);   // Check if type is a bool
84   TEST_EQ(vec[4].AsBool(), false);  // Check if value is false
85   TEST_EQ(vec[5].AsDouble(), 4.0);  // This is shared with vec[2] !
86   auto tvec = map["bar"].AsTypedVector();
87   TEST_EQ(tvec.size(), 3);
88   TEST_EQ(tvec[2].AsInt8(), 3);
89   auto tvec3 = map["bar3"].AsFixedTypedVector();
90   TEST_EQ(tvec3.size(), 3);
91   TEST_EQ(tvec3[2].AsInt8(), 3);
92   TEST_EQ(map["bool"].AsBool(), true);
93   auto tvecb = map["bools"].AsTypedVector();
94   TEST_EQ(tvecb.ElementType(), flexbuffers::FBT_BOOL);
95   TEST_EQ(map["foo"].AsUInt8(), 100);
96   TEST_EQ(map["unknown"].IsNull(), true);
97   auto mymap = map["mymap"].AsMap();
98   // These should be equal by pointer equality, since key and value are shared.
99   TEST_EQ(mymap.Keys()[0].AsKey(), map.Keys()[4].AsKey());
100   TEST_EQ(mymap.Values()[0].AsString().c_str(), vec[1].AsString().c_str());
101   // We can mutate values in the buffer.
102   TEST_EQ(vec[0].MutateInt(-99), true);
103   TEST_EQ(vec[0].AsInt64(), -99);
104   TEST_EQ(vec[1].MutateString("John"), true);  // Size must match.
105   TEST_EQ_STR(vec[1].AsString().c_str(), "John");
106   TEST_EQ(vec[1].MutateString("Alfred"), false);  // Too long.
107   TEST_EQ(vec[2].MutateFloat(2.0f), true);
108   TEST_EQ(vec[2].AsFloat(), 2.0f);
109   TEST_EQ(vec[2].MutateFloat(3.14159), false);  // Double does not fit in float.
110   TEST_EQ(vec[4].AsBool(), false);              // Is false before change
111   TEST_EQ(vec[4].MutateBool(true), true);       // Can change a bool
112   TEST_EQ(vec[4].AsBool(), true);               // Changed bool is now true
113 
114   // Parse from JSON:
115   flatbuffers::Parser parser;
116   slb.Clear();
117   auto jsontest = "{ a: [ 123, 456.0 ], b: \"hello\", c: true, d: false }";
118   TEST_EQ(parser.ParseFlexBuffer(jsontest, nullptr, &slb), true);
119   TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
120                                     slb.GetBuffer().size(), &reuse_tracker),
121           true);
122   auto jroot = flexbuffers::GetRoot(slb.GetBuffer());
123   auto jmap = jroot.AsMap();
124   auto jvec = jmap["a"].AsVector();
125   TEST_EQ(jvec[0].AsInt64(), 123);
126   TEST_EQ(jvec[1].AsDouble(), 456.0);
127   TEST_EQ_STR(jmap["b"].AsString().c_str(), "hello");
128   TEST_EQ(jmap["c"].IsBool(), true);   // Parsed correctly to a bool
129   TEST_EQ(jmap["c"].AsBool(), true);   // Parsed correctly to true
130   TEST_EQ(jmap["d"].IsBool(), true);   // Parsed correctly to a bool
131   TEST_EQ(jmap["d"].AsBool(), false);  // Parsed correctly to false
132   // And from FlexBuffer back to JSON:
133   auto jsonback = jroot.ToString();
134   TEST_EQ_STR(jsontest, jsonback.c_str());
135   // With indentation:
136   std::string jsonback_indented;
137   jroot.ToString(true, false, jsonback_indented, true, 0, "  ");
138   auto jsontest_indented =
139     "{\n  a: [\n    123,\n    456.0\n  ],\n  b: \"hello\",\n  c: true,\n  d: false\n}";
140   TEST_EQ_STR(jsontest_indented, jsonback_indented.c_str());
141 
142   slb.Clear();
143   slb.Vector([&]() {
144     for (int i = 0; i < 130; ++i) slb.Add(static_cast<uint8_t>(255));
145     slb.Vector([&]() {
146       for (int i = 0; i < 130; ++i) slb.Add(static_cast<uint8_t>(255));
147       slb.Vector([] {});
148     });
149   });
150   slb.Finish();
151   TEST_EQ(slb.GetSize(), 664);
152 }
153 
FlexBuffersReuseBugTest()154 void FlexBuffersReuseBugTest() {
155   flexbuffers::Builder slb;
156   slb.Map([&]() {
157     slb.Vector("vec", [&]() {});
158     slb.Bool("bool", true);
159   });
160   slb.Finish();
161   std::vector<uint8_t> reuse_tracker;
162   // This would fail before, since the reuse_tracker would use the address of
163   // the vector reference to check for reuse, but in this case we have an empty
164   // vector, and since the size field is before the pointer, its address is the
165   // same as thing after it, the key "bool".
166   // We fix this by using the address of the size field for tracking reuse.
167   TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
168                                     slb.GetBuffer().size(), &reuse_tracker),
169           true);
170 }
171 
FlexBuffersFloatingPointTest()172 void FlexBuffersFloatingPointTest() {
173 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
174   flexbuffers::Builder slb(512,
175                            flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
176   // Parse floating-point values from JSON:
177   flatbuffers::Parser parser;
178   slb.Clear();
179   auto jsontest =
180       "{ a: [1.0, nan, inf, infinity, -inf, +inf, -infinity, 8.0] }";
181   TEST_EQ(parser.ParseFlexBuffer(jsontest, nullptr, &slb), true);
182   auto jroot = flexbuffers::GetRoot(slb.GetBuffer());
183   TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
184                                     slb.GetBuffer().size(), nullptr),
185           true);
186   auto jmap = jroot.AsMap();
187   auto jvec = jmap["a"].AsVector();
188   TEST_EQ(8, jvec.size());
189   TEST_EQ(1.0, jvec[0].AsDouble());
190   TEST_ASSERT(is_quiet_nan(jvec[1].AsDouble()));
191   TEST_EQ(infinity_d, jvec[2].AsDouble());
192   TEST_EQ(infinity_d, jvec[3].AsDouble());
193   TEST_EQ(-infinity_d, jvec[4].AsDouble());
194   TEST_EQ(+infinity_d, jvec[5].AsDouble());
195   TEST_EQ(-infinity_d, jvec[6].AsDouble());
196   TEST_EQ(8.0, jvec[7].AsDouble());
197 #endif
198 }
199 
FlexBuffersDeprecatedTest()200 void FlexBuffersDeprecatedTest() {
201   // FlexBuffers as originally designed had a flaw involving the
202   // FBT_VECTOR_STRING datatype, and this test documents/tests the fix for it.
203   // Discussion: https://github.com/google/flatbuffers/issues/5627
204   flexbuffers::Builder slb;
205   // FBT_VECTOR_* are "typed vectors" where all elements are of the same type.
206   // Problem is, when storing FBT_STRING elements, it relies on that type to
207   // get the bit-width for the size field of the string, which in this case
208   // isn't present, and instead defaults to 8-bit. This means that any strings
209   // stored inside such a vector, when accessed thru the old API that returns
210   // a String reference, will appear to be truncated if the string stored is
211   // actually >=256 bytes.
212   std::string test_data(300, 'A');
213   auto start = slb.StartVector();
214   // This one will have a 16-bit size field.
215   slb.String(test_data);
216   // This one will have an 8-bit size field.
217   slb.String("hello");
218   // We're asking this to be serialized as a typed vector (true), but not
219   // fixed size (false). The type will be FBT_VECTOR_STRING with a bit-width
220   // of whatever the offsets in the vector need, the bit-widths of the strings
221   // are not stored(!) <- the actual design flaw.
222   // Note that even in the fixed code, we continue to serialize the elements of
223   // FBT_VECTOR_STRING as FBT_STRING, since there may be old code out there
224   // reading new data that we want to continue to function.
225   // Thus, FBT_VECTOR_STRING, while deprecated, will always be represented the
226   // same way, the fix lies on the reading side.
227   slb.EndVector(start, true, false);
228   slb.Finish();
229   // Verify because why not.
230   TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
231                                     slb.GetBuffer().size(), nullptr),
232           true);
233   // So now lets read this data back.
234   // For existing data, since we have no way of knowing what the actual
235   // bit-width of the size field of the string is, we are going to ignore this
236   // field, and instead treat these strings as FBT_KEY (null-terminated), so we
237   // can deal with strings of arbitrary length. This of course truncates strings
238   // with embedded nulls, but we think that that is preferrable over truncating
239   // strings >= 256 bytes.
240   auto vec = flexbuffers::GetRoot(slb.GetBuffer()).AsTypedVector();
241   // Even though this was serialized as FBT_VECTOR_STRING, it is read as
242   // FBT_VECTOR_KEY:
243   TEST_EQ(vec.ElementType(), flexbuffers::FBT_KEY);
244   // Access the long string. Previously, this would return a string of size 1,
245   // since it would read the high-byte of the 16-bit length.
246   // This should now correctly test the full 300 bytes, using AsKey():
247   TEST_EQ_STR(vec[0].AsKey(), test_data.c_str());
248   // Old code that called AsString will continue to work, as the String
249   // accessor objects now use a cached size that can come from a key as well.
250   TEST_EQ_STR(vec[0].AsString().c_str(), test_data.c_str());
251   // Short strings work as before:
252   TEST_EQ_STR(vec[1].AsKey(), "hello");
253   TEST_EQ_STR(vec[1].AsString().c_str(), "hello");
254   // So, while existing code and data mostly "just work" with the fixes applied
255   // to AsTypedVector and AsString, what do you do going forward?
256   // Code accessing existing data doesn't necessarily need to change, though
257   // you could consider using AsKey instead of AsString for a) documenting
258   // that you are accessing keys, or b) a speedup if you don't actually use
259   // the string size.
260   // For new data, or data that doesn't need to be backwards compatible,
261   // instead serialize as FBT_VECTOR (call EndVector with typed = false, then
262   // read elements with AsString), or, for maximum compactness, use
263   // FBT_VECTOR_KEY (call slb.Key above instead, read with AsKey or AsString).
264 }
265 
ParseFlexbuffersFromJsonWithNullTest()266 void ParseFlexbuffersFromJsonWithNullTest() {
267   // Test nulls are handled appropriately through flexbuffers to exercise other
268   // code paths of ParseSingleValue in the optional scalars change.
269   // TODO(cneo): Json -> Flatbuffers test once some language can generate code
270   // with optional scalars.
271   {
272     char json[] = "{\"opt_field\": 123 }";
273     flatbuffers::Parser parser;
274     flexbuffers::Builder flexbuild;
275     parser.ParseFlexBuffer(json, nullptr, &flexbuild);
276     auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
277     TEST_EQ(root.AsMap()["opt_field"].AsInt64(), 123);
278   }
279   {
280     char json[] = "{\"opt_field\": 123.4 }";
281     flatbuffers::Parser parser;
282     flexbuffers::Builder flexbuild;
283     parser.ParseFlexBuffer(json, nullptr, &flexbuild);
284     auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
285     TEST_EQ(root.AsMap()["opt_field"].AsDouble(), 123.4);
286   }
287   {
288     char json[] = "{\"opt_field\": null }";
289     flatbuffers::Parser parser;
290     flexbuffers::Builder flexbuild;
291     parser.ParseFlexBuffer(json, nullptr, &flexbuild);
292     auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
293     TEST_ASSERT(!root.AsMap().IsTheEmptyMap());
294     TEST_ASSERT(root.AsMap()["opt_field"].IsNull());
295     TEST_EQ(root.ToString(), std::string("{ opt_field: null }"));
296   }
297 }
298 
299 }  // namespace tests
300 }  // namespace flatbuffers
301