Add a Flatbuffers containing object, and more variants

We now have DetachedBuffers which represent flatbuffers, the new
Flatbuffer object, and Table objects.  It should be easy to merge and
copy these various objects now.  (And print).

Change-Id: Ie8fcff1e97f0ae5a7ef8a3becb1be467010460dc
diff --git a/aos/BUILD b/aos/BUILD
index 46aa1b9..eba1b45 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -440,9 +440,11 @@
     hdrs = ["json_to_flatbuffer.h"],
     deps = [
         ":flatbuffer_utils",
+        ":flatbuffers",
         ":json_tokenizer",
         "//aos/logging",
         "@com_github_google_flatbuffers//:flatbuffers",
+        "@com_github_google_glog//:glog",
         "@com_google_absl//absl/strings",
     ],
 )
@@ -466,6 +468,7 @@
     copts = ["-Wno-cast-align"],
     deps = [
         ":flatbuffer_utils",
+        ":flatbuffers",
         "@com_github_google_flatbuffers//:flatbuffers",
     ],
 )
@@ -482,3 +485,13 @@
         "//aos/testing:googletest",
     ],
 )
+
+cc_library(
+    name = "flatbuffers",
+    hdrs = [
+        "flatbuffers.h",
+    ],
+    deps = [
+        "@com_github_google_flatbuffers//:flatbuffers",
+    ],
+)
diff --git a/aos/flatbuffer_merge.cc b/aos/flatbuffer_merge.cc
index 3f72bb9..c8cc742 100644
--- a/aos/flatbuffer_merge.cc
+++ b/aos/flatbuffer_merge.cc
@@ -15,20 +15,14 @@
   OffsetAndFieldOffset(flatbuffers::voffset_t new_field_offset,
                        flatbuffers::Offset<flatbuffers::String> new_element)
       : field_offset(new_field_offset), element(new_element) {}
+  OffsetAndFieldOffset(flatbuffers::voffset_t new_field_offset,
+                       flatbuffers::Offset<flatbuffers::Table> new_element)
+      : field_offset(new_field_offset), element(new_element.o) {}
 
   flatbuffers::voffset_t field_offset;
   flatbuffers::Offset<flatbuffers::String> element;
 };
 
-// Merges 2 flat buffers with the provided type table into the builder.  Returns
-// the offset to the flatbuffers.
-// One or both of t1 and t2 must be non-null.  If one is null, this method
-// coppies instead of merging.
-flatbuffers::uoffset_t MergeFlatBuffers(const flatbuffers::TypeTable *typetable,
-                                        const flatbuffers::Table *t1,
-                                        const flatbuffers::Table *t2,
-                                        flatbuffers::FlatBufferBuilder *fbb);
-
 // Merges a single element to a builder for the provided field.
 // One or both of t1 and t2 must be non-null.  If one is null, this method
 // copies instead of merging.
@@ -291,11 +285,10 @@
       for (auto i = vec2->rbegin(); i != vec2->rend(); ++i) {
         const flatbuffers::Table *t = *i;
 
-        flatbuffers::uoffset_t end =
+        flatbuffers::Offset<flatbuffers::Table> end =
             MergeFlatBuffers(sub_typetable, t, nullptr, fbb);
 
-        object_elements.emplace_back(
-            flatbuffers::Offset<flatbuffers::Table>(end));
+        object_elements.emplace_back(end);
       }
     }
     if (t1_has) {
@@ -305,11 +298,10 @@
       for (auto i = vec1->rbegin(); i != vec1->rend(); ++i) {
         const flatbuffers::Table *t = *i;
 
-        flatbuffers::uoffset_t end =
+        flatbuffers::Offset<flatbuffers::Table> end =
             MergeFlatBuffers(sub_typetable, t, nullptr, fbb);
 
-        object_elements.emplace_back(
-            flatbuffers::Offset<flatbuffers::Table>(end));
+        object_elements.emplace_back(end);
       }
     }
 
@@ -329,10 +321,11 @@
   }
 }
 
-flatbuffers::uoffset_t MergeFlatBuffers(const flatbuffers::TypeTable *typetable,
-                                        const flatbuffers::Table *t1,
-                                        const flatbuffers::Table *t2,
-                                        flatbuffers::FlatBufferBuilder *fbb) {
+}  // namespace
+
+flatbuffers::Offset<flatbuffers::Table> MergeFlatBuffers(
+    const flatbuffers::TypeTable *typetable, const flatbuffers::Table *t1,
+    const flatbuffers::Table *t2, flatbuffers::FlatBufferBuilder *fbb) {
   ::std::vector<OffsetAndFieldOffset> elements;
 
   // We need to do this in 2 passes
@@ -503,15 +496,9 @@
   return fbb->EndTable(start);
 }
 
-}  // namespace
-
-::std::vector<uint8_t> MergeFlatBuffers(const flatbuffers::TypeTable *typetable,
-                                        const uint8_t *data1,
-                                        const uint8_t *data2) {
-  // Build up a builder.
-  flatbuffers::FlatBufferBuilder fbb;
-  fbb.ForceDefaults(1);
-
+flatbuffers::Offset<flatbuffers::Table> MergeFlatBuffers(
+    const flatbuffers::TypeTable *typetable, const uint8_t *data1,
+    const uint8_t *data2, flatbuffers::FlatBufferBuilder *fbb) {
   // Grab the 2 tables.
   const flatbuffers::Table *t1 =
       data1 != nullptr ? flatbuffers::GetRoot<flatbuffers::Table>(data1)
@@ -522,14 +509,21 @@
 
   // And then do the actual build.  This doesn't contain finish so we can nest
   // them nicely.
-  flatbuffers::uoffset_t end = MergeFlatBuffers(typetable, t1, t2, &fbb);
+  return flatbuffers::Offset<flatbuffers::Table>(
+      MergeFlatBuffers(typetable, t1, t2, fbb));
+}
+
+flatbuffers::DetachedBuffer MergeFlatBuffers(
+    const flatbuffers::TypeTable *typetable, const uint8_t *data1,
+    const uint8_t *data2) {
+  // Build up a builder.
+  flatbuffers::FlatBufferBuilder fbb;
+  fbb.ForceDefaults(1);
 
   // Finish up the buffer and return it.
-  fbb.Finish(flatbuffers::Offset<flatbuffers::Table>(end));
+  fbb.Finish(MergeFlatBuffers(typetable, data1, data2, &fbb));
 
-  const uint8_t *buf = fbb.GetBufferPointer();
-  const int size = fbb.GetSize();
-  return ::std::vector<uint8_t>(buf, buf + size);
+  return fbb.Release();
 }
 
 }  // namespace aos
diff --git a/aos/flatbuffer_merge.h b/aos/flatbuffer_merge.h
index c63b143..16372ed 100644
--- a/aos/flatbuffer_merge.h
+++ b/aos/flatbuffer_merge.h
@@ -4,20 +4,73 @@
 #include <cstddef>
 #include <string>
 
+#include "aos/flatbuffers.h"
 #include "flatbuffers/flatbuffers.h"
 
 namespace aos {
 
-::std::vector<uint8_t> MergeFlatBuffers(const flatbuffers::TypeTable *typetable,
-                                        const uint8_t *data1,
-                                        const uint8_t *data2);
+flatbuffers::DetachedBuffer MergeFlatBuffers(
+    const flatbuffers::TypeTable *typetable, const uint8_t *data1,
+    const uint8_t *data2);
+
+// Merges 2 flat buffers with the provided type table into the builder.  Returns
+// the offset to the flatbuffers.
+// One or both of t1 and t2 must be non-null.  If one is null, this method
+// coppies instead of merging.
+flatbuffers::Offset<flatbuffers::Table> MergeFlatBuffers(
+    const flatbuffers::TypeTable *typetable, const flatbuffers::Table *t1,
+    const flatbuffers::Table *t2, flatbuffers::FlatBufferBuilder *fbb);
 
 template <class T>
-::std::vector<uint8_t> MergeFlatBuffers(const uint8_t *data1,
-                                        const uint8_t *data2) {
+inline flatbuffers::Offset<T> MergeFlatBuffers(
+    const flatbuffers::Table *t1,
+    const flatbuffers::Table *t2, flatbuffers::FlatBufferBuilder *fbb) {
+  return MergeFlatBuffers(T::MiniReflectTypeTable(), t1, t2, fbb).o;
+}
+
+template <class T>
+inline flatbuffers::DetachedBuffer MergeFlatBuffers(const uint8_t *data1,
+                                                    const uint8_t *data2) {
   return MergeFlatBuffers(T::MiniReflectTypeTable(), data1, data2);
 }
 
+template <class T>
+inline flatbuffers::DetachedBuffer MergeFlatBuffers(
+    const flatbuffers::DetachedBuffer &data1,
+    const flatbuffers::DetachedBuffer &data2) {
+  return MergeFlatBuffers(T::MiniReflectTypeTable(), data1.data(),
+                          data2.data());
+}
+
+template <class T>
+inline aos::Flatbuffer<T> MergeFlatBuffers(const aos::Flatbuffer<T> &fb1,
+                                           const aos::Flatbuffer<T> &fb2) {
+  return aos::Flatbuffer<T>(
+      MergeFlatBuffers(T::MiniReflectTypeTable(), fb1.data(), fb2.data()));
+}
+
+template <class T>
+inline aos::Flatbuffer<T> MergeFlatBuffers(const T *fb1, const T *fb2) {
+  return aos::Flatbuffer<T>(MergeFlatBuffers(
+      T::MiniReflectTypeTable(), reinterpret_cast<const uint8_t *>(fb1),
+      reinterpret_cast<const uint8_t *>(fb2)));
+}
+
+template <class T>
+inline flatbuffers::Offset<T> CopyFlatBuffer(
+    const T *t1, flatbuffers::FlatBufferBuilder *fbb) {
+  return MergeFlatBuffers<T>(reinterpret_cast<const flatbuffers::Table *>(t1),
+                             nullptr, fbb);
+}
+
+template <class T>
+inline Flatbuffer<T> CopyFlatBuffer(const T *t) {
+  flatbuffers::FlatBufferBuilder fbb;
+  fbb.ForceDefaults(1);
+  fbb.Finish(CopyFlatBuffer<T>(t, &fbb));
+  return Flatbuffer<T>(fbb.Release());
+}
+
 }  // namespace aos
 
 #endif  // AOS_FLATBUFFER_MERGE_H_
diff --git a/aos/flatbuffer_merge_test.cc b/aos/flatbuffer_merge_test.cc
index f102db9..ca63249 100644
--- a/aos/flatbuffer_merge_test.cc
+++ b/aos/flatbuffer_merge_test.cc
@@ -17,25 +17,25 @@
                  const ::std::string out) {
     printf("Merging: %s\n", in1.c_str());
     printf("Merging: %s\n", in2.c_str());
-    const ::std::vector<uint8_t> fb1 = JsonToFlatbuffer(
+    const flatbuffers::DetachedBuffer fb1 = JsonToFlatbuffer(
         static_cast<const char *>(in1.c_str()), ConfigurationTypeTable());
 
     const ::std::string in1_nested = "{ \"nested_config\": " + in1 + " }";
-    const ::std::vector<uint8_t> fb1_nested = JsonToFlatbuffer(
-        static_cast<const char *>(in1_nested.c_str()),
-        ConfigurationTypeTable());
+    const flatbuffers::DetachedBuffer fb1_nested =
+        JsonToFlatbuffer(static_cast<const char *>(in1_nested.c_str()),
+                         ConfigurationTypeTable());
 
-    const ::std::vector<uint8_t> fb2 = JsonToFlatbuffer(
+    const flatbuffers::DetachedBuffer fb2 = JsonToFlatbuffer(
         static_cast<const char *>(in2.c_str()), ConfigurationTypeTable());
 
     const ::std::string in2_nested = "{ \"nested_config\": " + in2 + " }";
-    const ::std::vector<uint8_t> fb2_nested =
+    const flatbuffers::DetachedBuffer fb2_nested =
         JsonToFlatbuffer(static_cast<const char *>(in2_nested.c_str()),
                          ConfigurationTypeTable());
 
     const ::std::string out_nested = "{ \"nested_config\": " + out + " }";
 
-    const ::std::vector<uint8_t> empty =
+    const flatbuffers::DetachedBuffer empty =
         JsonToFlatbuffer("{ }", ConfigurationTypeTable());
 
     ASSERT_NE(fb1.size(), 0u);
@@ -54,70 +54,66 @@
 
     {
       // in1 merged with "" => in1.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(fb1.data(), empty.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
-      EXPECT_EQ(in1,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+      EXPECT_EQ(in1, FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // in2 merged with "" => in2.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(fb2.data(), empty.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
-      EXPECT_EQ(in2,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+      EXPECT_EQ(in2, FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // "" merged with in1 => in1.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(empty.data(), fb1.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
-      EXPECT_EQ(in1,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+      EXPECT_EQ(in1, FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // "" merged with in2 => in2.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(empty.data(), fb2.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
-      EXPECT_EQ(in2,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+      EXPECT_EQ(in2, FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // nullptr merged with in1 => in1.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(nullptr, fb1.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
-      EXPECT_EQ(in1, FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+      EXPECT_EQ(in1, FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // in1 merged with nullptr => in1.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(fb1.data(), nullptr);
       ASSERT_NE(fb_merged.size(), 0u);
 
-      EXPECT_EQ(in1, FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+      EXPECT_EQ(in1, FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // in1 merged with in2 => out.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(fb1.data(), fb2.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
       const ::std::string merged_output =
-          FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable());
+          FlatbufferToJson(fb_merged, ConfigurationTypeTable());
       EXPECT_EQ(out, merged_output);
 
       printf("Merged to: %s\n", merged_output.c_str());
@@ -128,72 +124,72 @@
 
     {
       // in1_nested merged with "" => in1.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(fb1_nested.data(), empty.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
       EXPECT_EQ(in1_nested,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+                FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // in2_nested merged with "" => in2_nested.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(fb2_nested.data(), empty.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
       EXPECT_EQ(in2_nested,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+                FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // "" merged with in1_nested => in1_nested.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(empty.data(), fb1_nested.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
       EXPECT_EQ(in1_nested,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+                FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // "" merged with in2_nested => in2_nested.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(empty.data(), fb2_nested.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
       EXPECT_EQ(in2_nested,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+                FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // nullptr merged with in1_nested => in1_nested.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(nullptr, fb1_nested.data());
       ASSERT_NE(fb_merged.size(), 0u);
 
       EXPECT_EQ(in1_nested,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+                FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // nullptr merged with in1_nested => in1_nested.
-      ::std::vector<uint8_t> fb_merged =
+      flatbuffers::DetachedBuffer fb_merged =
           MergeFlatBuffers<Configuration>(fb1_nested.data(), nullptr);
       ASSERT_NE(fb_merged.size(), 0u);
 
       EXPECT_EQ(in1_nested,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+                FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
 
     {
       // in1_nested merged with in2_nested => out_nested.
-      ::std::vector<uint8_t> fb_merged =
-          MergeFlatBuffers<Configuration>(fb1_nested.data(), fb2_nested.data());
+      flatbuffers::DetachedBuffer fb_merged =
+          MergeFlatBuffers<Configuration>(fb1_nested, fb2_nested);
       ASSERT_NE(fb_merged.size(), 0u);
 
       EXPECT_EQ(out_nested,
-                FlatbufferToJson(fb_merged.data(), ConfigurationTypeTable()));
+                FlatbufferToJson(fb_merged, ConfigurationTypeTable()));
     }
   }
 };
@@ -279,9 +275,10 @@
             "{ \"vector_foo_float\": [ -3.0, 1.3, 3.0, 2.0 ] }",
             "{ \"vector_foo_float\": [ 9.0, 7.0, 1.0, -3.0, 1.3, 3.0, 2.0 ] }");
 
-  JsonMerge("{ \"vector_foo_string\": [ \"9\", \"7\", \"1 \" ] }",
-            "{ \"vector_foo_string\": [ \"31\", \"32\" ] }",
-            "{ \"vector_foo_string\": [ \"9\", \"7\", \"1 \", \"31\", \"32\" ] }");
+  JsonMerge(
+      "{ \"vector_foo_string\": [ \"9\", \"7\", \"1 \" ] }",
+      "{ \"vector_foo_string\": [ \"31\", \"32\" ] }",
+      "{ \"vector_foo_string\": [ \"9\", \"7\", \"1 \", \"31\", \"32\" ] }");
 }
 
 // Test nested messages, and arrays of nested messages.
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
new file mode 100644
index 0000000..6f6fc11
--- /dev/null
+++ b/aos/flatbuffers.h
@@ -0,0 +1,66 @@
+#ifndef AOS_FLATBUFFERS_H_
+#define AOS_FLATBUFFERS_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+namespace aos {
+
+// TODO(austin): FlatbufferBase ?  We can associate the type table with it, and
+// make it generic.
+
+// This object associates the message type with the memory storing the
+// flatbuffer.  This only stores root tables.
+//
+// From a usage point of view, pointers to the data are very different than
+// pointers to the tables.
+template <typename T>
+class Flatbuffer {
+ public:
+  // Builds a Flatbuffer by taking ownership of the buffer.
+  Flatbuffer(flatbuffers::DetachedBuffer &&buffer)
+      : buffer_(::std::move(buffer)) {}
+
+  // Builds a flatbuffer by taking ownership of the buffer from the other
+  // flatbuffer.
+  Flatbuffer(Flatbuffer &&fb) : buffer_(::std::move(fb.buffer_)) {}
+  Flatbuffer &operator=(Flatbuffer &&fb) {
+    ::std::swap(buffer_, fb.buffer_);
+    return *this;
+  }
+
+  // Constructs an empty flatbuffer of type T.
+  static Flatbuffer<T> Empty() {
+    flatbuffers::FlatBufferBuilder fbb;
+    fbb.ForceDefaults(1);
+    const auto end = fbb.EndTable(fbb.StartTable());
+    fbb.Finish(flatbuffers::Offset<flatbuffers::Table>(end));
+    return Flatbuffer<T>(fbb.Release());
+  }
+
+  // Returns the MiniReflectTypeTable for T.
+  static const flatbuffers::TypeTable *MiniReflectTypeTable() {
+    return T::MiniReflectTypeTable();
+  }
+
+  // Returns a message from the buffer.
+  const T &message() const { return *flatbuffers::GetRoot<T>(buffer_.data()); }
+  // Returns a mutable message.  It can be mutated via the flatbuffer rules.
+  T *mutable_message() {
+    return flatbuffers::GetMutableRoot<T>(buffer_.data());
+  }
+
+  // Returns references to the buffer, and the data.
+  const flatbuffers::DetachedBuffer &buffer() const { return buffer_; }
+  const uint8_t *data() const { return buffer_.data(); }
+
+ private:
+  flatbuffers::DetachedBuffer buffer_;
+};
+
+// TODO(austin): Need a fixed buffer version of Flatbuffer.
+// TODO(austin): Need a way to get our hands on the max size.  Can start with
+// "large" for now.
+
+}  // namespace aos
+
+#endif  // AOS_FLATBUFFERS_H_
diff --git a/aos/json_to_flatbuffer.cc b/aos/json_to_flatbuffer.cc
index 939dca7..13a3a38 100644
--- a/aos/json_to_flatbuffer.cc
+++ b/aos/json_to_flatbuffer.cc
@@ -5,10 +5,10 @@
 
 #include "absl/strings/string_view.h"
 #include "aos/flatbuffer_utils.h"
-#include "aos/logging/logging.h"
 #include "aos/json_tokenizer.h"
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/minireflect.h"
+#include "glog/logging.h"
 
 // TODO(austin): Can we just do an Offset<void> ?  It doesn't matter, so maybe
 // just say that.
@@ -124,8 +124,8 @@
 
   // Parses the json into a flatbuffer.  Returns either an empty vector on
   // error, or a vector with the flatbuffer data in it.
-  ::std::vector<uint8_t> Parse(const absl::string_view data,
-                               const flatbuffers::TypeTable *typetable) {
+  flatbuffers::DetachedBuffer Parse(const absl::string_view data,
+                                    const flatbuffers::TypeTable *typetable) {
     flatbuffers::uoffset_t end = 0;
     bool result = DoParse(typetable, data, &end);
 
@@ -134,12 +134,10 @@
       auto o = flatbuffers::Offset<flatbuffers::Table>(end);
       fbb_.Finish(o);
 
-      const uint8_t *buf = fbb_.GetBufferPointer();
-      const int size = fbb_.GetSize();
-      return ::std::vector<uint8_t>(buf, buf + size);
+      return fbb_.Release();
     } else {
       // Otherwise return an empty vector.
-      return ::std::vector<uint8_t>();
+      return flatbuffers::DetachedBuffer();
     }
   }
 
@@ -708,18 +706,37 @@
 
 }  // namespace
 
-::std::vector<uint8_t> JsonToFlatbuffer(
+flatbuffers::DetachedBuffer JsonToFlatbuffer(
     const absl::string_view data, const flatbuffers::TypeTable *typetable) {
   JsonParser p;
   return p.Parse(data, typetable);
 }
 
-::std::string FlatbufferToJson(const uint8_t *buffer,
-                               const ::flatbuffers::TypeTable *typetable,
-                               bool multi_line) {
+::std::string BufferFlatbufferToJson(const uint8_t *buffer,
+                                     const ::flatbuffers::TypeTable *typetable,
+                                     bool multi_line) {
+  // It is pretty common to get passed in a nullptr when a test fails.  Rather
+  // than CHECK, return a more user friendly result.
+  if (buffer == nullptr) {
+    return "null";
+  }
+  return TableFlatbufferToJson(reinterpret_cast<const flatbuffers::Table *>(
+                                   flatbuffers::GetRoot<uint8_t>(buffer)),
+                               typetable, multi_line);
+}
+
+::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
+                                    const ::flatbuffers::TypeTable *typetable,
+                                    bool multi_line) {
+  // It is pretty common to get passed in a nullptr when a test fails.  Rather
+  // than CHECK, return a more user friendly result.
+  if (t == nullptr) {
+    return "null";
+  }
   ::flatbuffers::ToStringVisitor tostring_visitor(
       multi_line ? "\n" : " ", true, multi_line ? " " : "", multi_line);
-  IterateFlatBuffer(buffer, typetable, &tostring_visitor);
+  flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable,
+                             &tostring_visitor);
   return tostring_visitor.s;
 }
 
diff --git a/aos/json_to_flatbuffer.h b/aos/json_to_flatbuffer.h
index 78d0703..183e6b2 100644
--- a/aos/json_to_flatbuffer.h
+++ b/aos/json_to_flatbuffer.h
@@ -5,20 +5,52 @@
 #include <string>
 
 #include "absl/strings/string_view.h"
+#include "aos/flatbuffers.h"
 #include "flatbuffers/flatbuffers.h"
 
 namespace aos {
 
 // Parses the flatbuffer into the vector, or returns an empty vector.
-::std::vector<uint8_t> JsonToFlatbuffer(
+flatbuffers::DetachedBuffer JsonToFlatbuffer(
     const absl::string_view data, const flatbuffers::TypeTable *typetable);
 
 // Converts a flatbuffer into a Json string.
-//
 // multi_line controls if the Json is written out on multiple lines or one.
-::std::string FlatbufferToJson(const uint8_t *buffer,
-                               const flatbuffers::TypeTable *typetable,
-                               bool multi_line = false);
+// The methods below are generally more useful than BufferFlatbufferToJson and
+// TableFlatbufferToJson.
+::std::string BufferFlatbufferToJson(const uint8_t *buffer,
+                                     const flatbuffers::TypeTable *typetable,
+                                     bool multi_line = false);
+
+::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
+                                    const ::flatbuffers::TypeTable *typetable,
+                                    bool multi_line);
+
+// Converts a DetachedBuffer holding a flatbuffer to JSON.
+inline ::std::string FlatbufferToJson(const flatbuffers::DetachedBuffer &buffer,
+                                      const flatbuffers::TypeTable *typetable,
+                                      bool multi_line = false) {
+  return BufferFlatbufferToJson(buffer.data(), typetable, multi_line);
+}
+
+// Converts a Flatbuffer<T> holding a flatbuffer to JSON.
+template <typename T>
+inline ::std::string FlatbufferToJson(const Flatbuffer<T> &flatbuffer,
+                                      bool multi_line = false) {
+  return BufferFlatbufferToJson(
+      flatbuffer.data(), Flatbuffer<T>::MiniReflectTypeTable(), multi_line);
+}
+
+// Converts a flatbuffer::Table to JSON.
+template <typename T>
+typename std::enable_if<
+    std::is_base_of<flatbuffers::Table, T>::value,
+    std::string>::type inline FlatbufferToJson(const T *flatbuffer,
+                                               bool multi_line = false) {
+  return TableFlatbufferToJson(
+      reinterpret_cast<const flatbuffers::Table *>(flatbuffer),
+      Flatbuffer<T>::MiniReflectTypeTable(), multi_line);
+}
 
 }  // namespace aos
 
diff --git a/aos/json_to_flatbuffer_test.cc b/aos/json_to_flatbuffer_test.cc
index 7325c0c..a048e0a 100644
--- a/aos/json_to_flatbuffer_test.cc
+++ b/aos/json_to_flatbuffer_test.cc
@@ -16,15 +16,14 @@
 
   bool JsonAndBack(const ::std::string in, const ::std::string out) {
     printf("Testing: %s\n", in.c_str());
-    const ::std::vector<uint8_t> fb =
+    const flatbuffers::DetachedBuffer fb =
         JsonToFlatbuffer(in.data(), ConfigurationTypeTable());
 
     if (fb.size() == 0) {
       return false;
     }
 
-    const ::std::string back =
-        FlatbufferToJson(fb.data(), ConfigurationTypeTable());
+    const ::std::string back = FlatbufferToJson(fb, ConfigurationTypeTable());
 
     printf("Back to string: %s\n", back.c_str());