FlexBuffers: allow any values to be shared.
authorWouter van Oortmerssen <aardappel@gmail.com>
Mon, 26 Aug 2019 18:54:38 +0000 (11:54 -0700)
committerWouter van Oortmerssen <aardappel@gmail.com>
Mon, 26 Aug 2019 21:17:52 +0000 (14:17 -0700)
(see comments in the code).

Change-Id: I5603abb0db436145739653692644bbcfd3c946e3

include/flatbuffers/flexbuffers.h
tests/test.cpp

index 3425150..35c3e0b 100644 (file)
@@ -914,7 +914,9 @@ class Builder FLATBUFFERS_FINAL_CLASS {
     Bool(b);
   }
 
-  void IndirectInt(int64_t i) { PushIndirect(i, FBT_INDIRECT_INT, WidthI(i)); }
+  void IndirectInt(int64_t i) {
+    PushIndirect(i, FBT_INDIRECT_INT, WidthI(i));
+  }
   void IndirectInt(const char *key, int64_t i) {
     Key(key);
     IndirectInt(i);
@@ -1194,6 +1196,26 @@ class Builder FLATBUFFERS_FINAL_CLASS {
     EndMap(start);
   }
 
+  // If you wish to share a value explicitly (a value not shared automatically
+  // through one of the BUILDER_FLAG_SHARE_* flags) you can do so with these
+  // functions. Or if you wish to turn those flags off for performance reasons
+  // and still do some explicit sharing. For example:
+  // builder.IndirectDouble(M_PI);
+  // auto id = builder.LastValue();  // Remember where we stored it.
+  // .. more code goes here ..
+  // builder.ReuseValue(id);  // Refers to same double by offset.
+  // LastValue works regardless of wether the value has a key or not.
+  // Works on any data type.
+  struct Value;
+  Value LastValue() { return stack_.back(); }
+  void ReuseValue(Value v) {
+    stack_.push_back(v);
+  }
+  void ReuseValue(const char *key, Value v) {
+    Key(key);
+    ReuseValue(v);
+  }
+
   // Overloaded Add that tries to call the correct function above.
   void Add(int8_t i) { Int(i); }
   void Add(int16_t i) { Int(i); }
@@ -1319,6 +1341,8 @@ class Builder FLATBUFFERS_FINAL_CLASS {
                                                            : FBT_INT);
   }
 
+ public:
+  // This was really intended to be private, except for LastValue/ReuseValue.
   struct Value {
     union {
       int64_t i_;
@@ -1388,6 +1412,7 @@ class Builder FLATBUFFERS_FINAL_CLASS {
     }
   };
 
+ private:
   void WriteAny(const Value &val, uint8_t byte_width) {
     switch (val.type_) {
       case FBT_NULL:
index 2efef43..a3474cb 100644 (file)
@@ -2514,9 +2514,11 @@ void FlexBuffersTest() {
         slb += -100;  // Equivalent to slb.Add(-100) or slb.Int(-100);
         slb += "Fred";
         slb.IndirectFloat(4.0f);
+        auto i_f = slb.LastValue();
         uint8_t blob[] = { 77 };
         slb.Blob(blob, 1);
         slb += false;
+        slb.ReuseValue(i_f);
       });
       int ints[] = { 1, 2, 3 };
       slb.Vector("bar", ints, 3);
@@ -2537,9 +2539,11 @@ void FlexBuffersTest() {
         slb3 += -100;  // Equivalent to slb.Add(-100) or slb.Int(-100);
         slb3 += "Fred";
         slb3.IndirectFloat(4.0f);
+        auto i_f = slb3.LastValue();
         uint8_t blob[] = { 77 };
         slb3.Blob(blob, 1);
         slb3 += false;
+        slb3.ReuseValue(i_f);
       }, slb2);
       int ints[] = { 1, 2, 3 };
       slb2.Vector("bar", ints, 3);
@@ -2563,7 +2567,7 @@ void FlexBuffersTest() {
   auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
   TEST_EQ(map.size(), 7);
   auto vec = map["vec"].AsVector();
-  TEST_EQ(vec.size(), 5);
+  TEST_EQ(vec.size(), 6);
   TEST_EQ(vec[0].AsInt64(), -100);
   TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
   TEST_EQ(vec[1].AsInt64(), 0);  // Number parsing failed.
@@ -2571,13 +2575,11 @@ void FlexBuffersTest() {
   TEST_EQ(vec[2].AsString().IsTheEmptyString(), true);  // Wrong Type.
   TEST_EQ_STR(vec[2].AsString().c_str(), "");     // This still works though.
   TEST_EQ_STR(vec[2].ToString().c_str(), "4.0");  // Or have it converted.
-
   // Few tests for templated version of As.
   TEST_EQ(vec[0].As<int64_t>(), -100);
   TEST_EQ_STR(vec[1].As<std::string>().c_str(), "Fred");
   TEST_EQ(vec[1].As<int64_t>(), 0);  // Number parsing failed.
   TEST_EQ(vec[2].As<double>(), 4.0);
-
   // Test that the blob can be accessed.
   TEST_EQ(vec[3].IsBlob(), true);
   auto blob = vec[3].AsBlob();
@@ -2585,6 +2587,7 @@ void FlexBuffersTest() {
   TEST_EQ(blob.data()[0], 77);
   TEST_EQ(vec[4].IsBool(), true);   // Check if type is a bool
   TEST_EQ(vec[4].AsBool(), false);  // Check if value is false
+  TEST_EQ(vec[5].AsDouble(), 4.0);  // This is shared with vec[2] !
   auto tvec = map["bar"].AsTypedVector();
   TEST_EQ(tvec.size(), 3);
   TEST_EQ(tvec[2].AsInt8(), 3);
@@ -2913,7 +2916,7 @@ void NativeTypeTest() {
   }
 }
 
-void FixedLengthArrayJsonTest(bool binary) {  
+void FixedLengthArrayJsonTest(bool binary) {
   // VS10 does not support typed enums, exclude from tests
 #if !defined(_MSC_VER) || _MSC_VER >= 1700
   // load FlatBuffer schema (.fbs) and JSON from disk