Add a wrapper around flatbuffers::TypeTable

So I can implement it on top of a Schema too.

Change-Id: I56502593e302c186cf48d3e2b42233a48202176d
diff --git a/aos/BUILD b/aos/BUILD
index 790f845..96c5b3d 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -337,6 +337,7 @@
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         "@com_github_google_flatbuffers//:flatbuffers",
+        "@com_github_google_glog//:glog",
     ],
 )
 
diff --git a/aos/flatbuffer_utils.cc b/aos/flatbuffer_utils.cc
index 1060fa8..b0615df 100644
--- a/aos/flatbuffer_utils.cc
+++ b/aos/flatbuffer_utils.cc
@@ -1 +1,147 @@
 #include "aos/flatbuffer_utils.h"
+
+#include "flatbuffers/minireflect.h"
+#include "glog/logging.h"
+
+namespace aos {
+
+bool FlatbufferType::IsSequence() const {
+  if (type_table_) {
+    return type_table_->st != flatbuffers::ST_ENUM;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+bool FlatbufferType::IsEnum() const {
+  if (type_table_) {
+    return type_table_->st == flatbuffers::ST_ENUM;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+bool FlatbufferType::FieldIsSequence(int index) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    DCHECK_LT(static_cast<size_t>(index), type_table_->num_elems);
+    const flatbuffers::TypeCode &type_code = type_table_->type_codes[index];
+    if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
+      return false;
+    }
+    DCHECK(FieldType(index).IsSequence());
+    return true;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+bool FlatbufferType::FieldIsEnum(int index) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    DCHECK_LT(static_cast<size_t>(index), type_table_->num_elems);
+    const flatbuffers::TypeCode &type_code = type_table_->type_codes[index];
+    if (type_code.base_type == flatbuffers::ET_SEQUENCE) {
+      return false;
+    }
+    if (type_code.sequence_ref == -1) {
+      // Not an enum.
+      return false;
+    }
+    DCHECK(FieldType(index).IsEnum());
+    return true;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+std::optional<int64_t> FlatbufferType::EnumValue(std::string_view name) const {
+  DCHECK(IsEnum());
+  if (type_table_) {
+    for (size_t i = 0; i < type_table_->num_elems; ++i) {
+      if (name == type_table_->names[i]) {
+        if (type_table_->values) {
+          return type_table_->values[i];
+        } else {
+          return i;
+        }
+      }
+    }
+    return std::nullopt;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+bool FlatbufferType::FieldIsRepeating(int index) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    DCHECK_LT(static_cast<size_t>(index), type_table_->num_elems);
+    const flatbuffers::TypeCode &type_code = type_table_->type_codes[index];
+    return type_code.is_repeating;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+int FlatbufferType::FieldIndex(std::string_view field_name) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    for (size_t i = 0; i < type_table_->num_elems; ++i) {
+      if (field_name == std::string_view(type_table_->names[i])) {
+        return i;
+      }
+    }
+    return -1;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+std::string_view FlatbufferType::FieldName(int index) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    DCHECK_LT(static_cast<size_t>(index), type_table_->num_elems);
+    return type_table_->names[index];
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+flatbuffers::ElementaryType FlatbufferType::FieldElementaryType(
+    int index) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    DCHECK_LT(static_cast<size_t>(index), type_table_->num_elems);
+    const flatbuffers::TypeCode &type_code = type_table_->type_codes[index];
+    return static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+size_t FlatbufferType::FieldInlineSize(int index) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    return flatbuffers::InlineSize(FieldElementaryType(index), type_table_);
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+int FlatbufferType::NumberFields() const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    return type_table_->num_elems;
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+FlatbufferType FlatbufferType::FieldType(int index) const {
+  DCHECK(IsSequence());
+  if (type_table_) {
+    DCHECK_LT(static_cast<size_t>(index), type_table_->num_elems);
+    const flatbuffers::TypeCode &type_code = type_table_->type_codes[index];
+    CHECK_GE(type_code.sequence_ref, 0);
+    // type_refs can be shorter than num_elems, but not longer, so this is still
+    // a valid sanity check.
+    DCHECK_LT(static_cast<size_t>(type_code.sequence_ref),
+              type_table_->num_elems);
+    const flatbuffers::TypeFunction type_function =
+        type_table_->type_refs[type_code.sequence_ref];
+    return FlatbufferType(type_function());
+  }
+  LOG(FATAL) << "Unimplemented";
+}
+
+}  // namespace aos
diff --git a/aos/flatbuffer_utils.h b/aos/flatbuffer_utils.h
index 2b1c1ab..2118fd6 100644
--- a/aos/flatbuffer_utils.h
+++ b/aos/flatbuffer_utils.h
@@ -1,7 +1,11 @@
 #ifndef AOS_FLATBUFFER_UTILS_
 #define AOS_FLATBUFFER_UTILS_
 
+#include <optional>
+#include <string_view>
+
 #include "flatbuffers/flatbuffers.h"
+#include "glog/logging.h"
 
 namespace aos {
 
@@ -11,6 +15,86 @@
   return flatbuffers::ElementaryTypeNames()[elementary_type] + 3;
 }
 
+// A least-common-denominator API for a TypeTable or a Schema.
+//
+// An instance may represent an enum or a sequence (table, struct, or union).
+// Schemas have objects for the individual fields, but these are not exposed as
+// FlatbufferType instances.
+class FlatbufferType final {
+ public:
+  // Implicit on purpose, to allow freely creating a FlatbufferType.
+  FlatbufferType(const flatbuffers::TypeTable *type_table)
+      : type_table_(CHECK_NOTNULL(type_table)) {}
+
+  // This is deliberately copyable, for ease of memory management. It is cheap
+  // to pass by value.
+  FlatbufferType(const FlatbufferType &) = default;
+  FlatbufferType(FlatbufferType &&) = default;
+  FlatbufferType &operator=(const FlatbufferType &) = default;
+  FlatbufferType &operator=(FlatbufferType &&) = default;
+
+  // Returns whether this type is a sequence (table, struct, or union).
+  bool IsSequence() const;
+
+  // Returns whether this type is an enum.
+  bool IsEnum() const;
+
+  // Returns whether the given field is a sequence (table, struct, or union).
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  bool FieldIsSequence(int index) const;
+
+  // Returns whether the given field is an enum.
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  bool FieldIsEnum(int index) const;
+
+  // Returns the value for a given enumerator.
+  //
+  // Only valid for enums.
+  std::optional<int64_t> EnumValue(std::string_view name) const;
+
+  // Returns whether the given field is either a vector (in a table) or an array
+  // (in a struct).
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  bool FieldIsRepeating(int index) const;
+
+  // Returns the field index in a table given the name, or -1 if the name is not
+  // found.
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  int FieldIndex(std::string_view field_name) const;
+
+  // Returns the name for a field.
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  std::string_view FieldName(int index) const;
+
+  // Returns the type of a field.
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  flatbuffers::ElementaryType FieldElementaryType(int index) const;
+
+  // See flatbuffers::InlineSize for details.
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  size_t FieldInlineSize(int index) const;
+
+  // Returns the total number of fields.
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  int NumberFields() const;
+
+  // Returns the type for a field.
+  //
+  // Only valid for sequences (tables, structs, or unions).
+  FlatbufferType FieldType(int index) const;
+
+ private:
+  const flatbuffers::TypeTable *type_table_ = nullptr;
+};
+
 }  // namespace aos
 
 #endif  // AOS_FLATBUFFER_UTILS_
diff --git a/aos/json_to_flatbuffer.cc b/aos/json_to_flatbuffer.cc
index 036ae01..f64991d 100644
--- a/aos/json_to_flatbuffer.cc
+++ b/aos/json_to_flatbuffer.cc
@@ -17,19 +17,6 @@
 // one is and how to test it.  So everything rejects it.
 
 namespace aos {
-
-// Finds the field index in the table given the name.
-int FieldIndex(const flatbuffers::TypeTable *typetable,
-               const char *field_name) {
-  CHECK(typetable->values == nullptr);
-  for (size_t i = 0; i < typetable->num_elems; ++i) {
-    if (strcmp(field_name, typetable->names[i]) == 0) {
-      return i;
-    }
-  }
-  return -1;
-}
-
 namespace {
 
 // Class to hold one of the 3 json types for an array.
@@ -77,30 +64,29 @@
 
 // Adds a single element.  This assumes that vectors have been dealt with
 // already.  Returns true on success.
-bool AddSingleElement(const flatbuffers::TypeTable *typetable,
-                      const FieldElement &field_element,
+bool AddSingleElement(FlatbufferType type, const FieldElement &field_element,
                       ::std::vector<bool> *fields_in_use,
                       flatbuffers::FlatBufferBuilder *fbb);
-bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
-                      int64_t int_value, flatbuffers::FlatBufferBuilder *fbb);
-bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
-                      double double_value, flatbuffers::FlatBufferBuilder *fbb);
-bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
+bool AddSingleElement(FlatbufferType type, int field_index, int64_t int_value,
+                      flatbuffers::FlatBufferBuilder *fbb);
+bool AddSingleElement(FlatbufferType type, int field_index, double double_value,
+                      flatbuffers::FlatBufferBuilder *fbb);
+bool AddSingleElement(FlatbufferType type, int field_index,
                       flatbuffers::Offset<flatbuffers::String> offset_element,
                       flatbuffers::FlatBufferBuilder *fbb);
 
-// Writes an array of FieldElement (with the definition in the type
-// table) to the builder.  Returns the offset of the table.
-flatbuffers::uoffset_t WriteTable(const flatbuffers::TypeTable *typetable,
+// Writes an array of FieldElement (with the definition in "type") to the
+// builder.  Returns the offset of the resulting table.
+flatbuffers::uoffset_t WriteTable(FlatbufferType type,
                                   const ::std::vector<FieldElement> &elements,
                                   flatbuffers::FlatBufferBuilder *fbb) {
   // End of a nested struct!  Add it.
   const flatbuffers::uoffset_t start = fbb->StartTable();
 
-  ::std::vector<bool> fields_in_use(typetable->num_elems, false);
+  ::std::vector<bool> fields_in_use(type.NumberFields(), false);
 
   for (const FieldElement &field_element : elements) {
-    AddSingleElement(typetable, field_element, &fields_in_use, fbb);
+    AddSingleElement(type, field_element, &fields_in_use, fbb);
   }
 
   return fbb->EndTable(start);
@@ -123,10 +109,10 @@
 
   // Parses the json into a flatbuffer.  Returns either an empty vector on
   // error, or a vector with the flatbuffer data in it.
-  flatbuffers::Offset<flatbuffers::Table> Parse(
-      const std::string_view data, const flatbuffers::TypeTable *typetable) {
+  flatbuffers::Offset<flatbuffers::Table> Parse(const std::string_view data,
+                                                FlatbufferType type) {
     flatbuffers::uoffset_t end = 0;
-    bool result = DoParse(typetable, data, &end);
+    bool result = DoParse(type, data, &end);
 
     if (result) {
       // On success, finish the table and build the vector.
@@ -143,8 +129,8 @@
 
   // Parses the flatbuffer.  This is a second method so we can do easier
   // cleanup at the top level.  Returns true on success.
-  bool DoParse(const flatbuffers::TypeTable *typetable,
-               const std::string_view data, flatbuffers::uoffset_t *table_end);
+  bool DoParse(FlatbufferType type, const std::string_view data,
+               flatbuffers::uoffset_t *table_end);
 
   // Adds *_value for the provided field.  If we are in a vector, queues the
   // data up in vector_elements.  Returns true on success.
@@ -169,7 +155,7 @@
   // nested structures.
   struct FlatBufferContext {
     // Type of the current type.
-    const flatbuffers::TypeTable *typetable;
+    FlatbufferType type;
     // If true, we are parsing a vector.
     bool in_vector;
     // The field index of the current field.
@@ -194,10 +180,9 @@
   ::std::vector<FlatBufferContext> stack_;
 };
 
-bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable,
-                         const std::string_view data,
+bool JsonParser::DoParse(FlatbufferType type, const std::string_view data,
                          flatbuffers::uoffset_t *table_end) {
-  ::std::vector<const flatbuffers::TypeTable *> stack;
+  ::std::vector<FlatbufferType> stack;
 
   Tokenizer t(data);
 
@@ -220,23 +205,22 @@
 
       case Tokenizer::TokenType::kStartObject:  // {
         if (stack_.size() == 0) {
-          stack_.push_back({typetable, false, -1, "", {}, {}});
+          stack_.push_back({type, false, -1, "", {}, {}});
         } else {
           int field_index = stack_.back().field_index;
 
-          const flatbuffers::TypeCode &type_code =
-              stack_.back().typetable->type_codes[field_index];
-
-          if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
+          if (!stack_.back().type.FieldIsSequence(field_index)) {
             fprintf(stderr, "Field '%s' is not a sequence\n",
                     stack_.back().field_name.c_str());
             return false;
           }
 
-          flatbuffers::TypeFunction type_function =
-              stack_.back().typetable->type_refs[type_code.sequence_ref];
-
-          stack_.push_back({type_function(), false, -1, "", {}, {}});
+          stack_.push_back({stack_.back().type.FieldType(field_index),
+                            false,
+                            -1,
+                            "",
+                            {},
+                            {}});
         }
         break;
       case Tokenizer::TokenType::kEndObject:  // }
@@ -247,7 +231,7 @@
         } else {
           // End of a nested struct!  Add it.
           const flatbuffers::uoffset_t end =
-              WriteTable(stack_.back().typetable, stack_.back().elements, fbb_);
+              WriteTable(stack_.back().type, stack_.back().elements, fbb_);
 
           // We now want to talk about the parent structure.  Pop the child.
           stack_.pop_back();
@@ -335,8 +319,8 @@
       case Tokenizer::TokenType::kField:  // field name
       {
         stack_.back().field_name = t.field_name();
-        stack_.back().field_index = FieldIndex(
-            stack_.back().typetable, stack_.back().field_name.c_str());
+        stack_.back().field_index =
+            stack_.back().type.FieldIndex(stack_.back().field_name.c_str());
 
         if (stack_.back().field_index == -1) {
           fprintf(stderr, "Invalid field name '%s'\n",
@@ -350,10 +334,7 @@
 }
 
 bool JsonParser::AddElement(int field_index, int64_t int_value) {
-  flatbuffers::TypeCode type_code =
-      stack_.back().typetable->type_codes[field_index];
-
-  if (type_code.is_repeating != in_vector()) {
+  if (stack_.back().type.FieldIsRepeating(field_index) != in_vector()) {
     fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
     return false;
   }
@@ -367,10 +348,7 @@
 }
 
 bool JsonParser::AddElement(int field_index, double double_value) {
-  flatbuffers::TypeCode type_code =
-      stack_.back().typetable->type_codes[field_index];
-
-  if (type_code.is_repeating != in_vector()) {
+  if (stack_.back().type.FieldIsRepeating(field_index) != in_vector()) {
     fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
     return false;
   }
@@ -384,16 +362,13 @@
 }
 
 bool JsonParser::AddElement(int field_index, const ::std::string &data) {
-  flatbuffers::TypeCode type_code =
-      stack_.back().typetable->type_codes[field_index];
-
-  if (type_code.is_repeating != in_vector()) {
+  if (stack_.back().type.FieldIsRepeating(field_index) != in_vector()) {
     fprintf(stderr, "Type and json disagree on if we are in a vector or not\n");
     return false;
   }
 
   const flatbuffers::ElementaryType elementary_type =
-      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+      stack_.back().type.FieldElementaryType(field_index);
   switch (elementary_type) {
     case flatbuffers::ET_CHAR:
     case flatbuffers::ET_UCHAR:
@@ -403,40 +378,25 @@
     case flatbuffers::ET_UINT:
     case flatbuffers::ET_LONG:
     case flatbuffers::ET_ULONG:
-      if (type_code.sequence_ref != -1) {
+      if (stack_.back().type.FieldIsEnum(field_index)) {
         // We have an enum.
-        const flatbuffers::TypeTable *type_table = stack_.back().typetable;
-        flatbuffers::TypeFunction type_function =
-            type_table->type_refs[type_code.sequence_ref];
+        const FlatbufferType type = stack_.back().type;
+        const FlatbufferType enum_type = type.FieldType(field_index);
+        CHECK(enum_type.IsEnum());
 
-        const flatbuffers::TypeTable *enum_type_table = type_function();
+        const std::optional<int64_t> int_value = enum_type.EnumValue(data);
 
-        CHECK_EQ(enum_type_table->st, flatbuffers::ST_ENUM);
-
-        int64_t int_value = 0;
-        bool found = false;
-        for (size_t i = 0; i < enum_type_table->num_elems; ++i) {
-          if (data == enum_type_table->names[i]) {
-            if (enum_type_table->values) {
-              int_value = enum_type_table->values[i];
-            } else {
-              int_value = i;
-            }
-            found = true;
-            break;
-          }
-        }
-
-        if (!found) {
-          fprintf(stderr, "Enum value '%s' not found for field '%s'\n",
-                  data.c_str(), type_table->names[field_index]);
+        if (!int_value) {
+          const std::string_view name = type.FieldName(field_index);
+          fprintf(stderr, "Enum value '%s' not found for field '%.*s'\n",
+                  data.c_str(), static_cast<int>(name.size()), name.data());
           return false;
         }
 
         if (in_vector()) {
-          stack_.back().vector_elements.emplace_back(int_value);
+          stack_.back().vector_elements.emplace_back(*int_value);
         } else {
-          stack_.back().elements.emplace_back(field_index, int_value);
+          stack_.back().elements.emplace_back(field_index, *int_value);
         }
         return true;
       }
@@ -457,13 +417,13 @@
   return true;
 }
 
-bool AddSingleElement(const flatbuffers::TypeTable *typetable,
-                      const FieldElement &field_element,
+bool AddSingleElement(FlatbufferType type, const FieldElement &field_element,
                       ::std::vector<bool> *fields_in_use,
                       flatbuffers::FlatBufferBuilder *fbb) {
   if ((*fields_in_use)[field_element.field_index]) {
-    fprintf(stderr, "Duplicate field: '%s'\n",
-            typetable->names[field_element.field_index]);
+    const std::string_view name = type.FieldName(field_element.field_index);
+    fprintf(stderr, "Duplicate field: '%.*s'\n", static_cast<int>(name.size()),
+            name.data());
     return false;
   }
 
@@ -471,29 +431,27 @@
 
   switch (field_element.element.type) {
     case Element::ElementType::INT:
-      return AddSingleElement(typetable, field_element.field_index,
+      return AddSingleElement(type, field_element.field_index,
                               field_element.element.int_element, fbb);
     case Element::ElementType::DOUBLE:
-      return AddSingleElement(typetable, field_element.field_index,
+      return AddSingleElement(type, field_element.field_index,
                               field_element.element.double_element, fbb);
     case Element::ElementType::OFFSET:
-      return AddSingleElement(typetable, field_element.field_index,
+      return AddSingleElement(type, field_element.field_index,
                               field_element.element.offset_element, fbb);
   }
   return false;
 }
 
-bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
-                      int64_t int_value, flatbuffers::FlatBufferBuilder *fbb
+bool AddSingleElement(FlatbufferType type, int field_index, int64_t int_value,
+                      flatbuffers::FlatBufferBuilder *fbb
 
 ) {
   flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
       static_cast<flatbuffers::voffset_t>(field_index));
 
-  flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
-
   const flatbuffers::ElementaryType elementary_type =
-      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+      type.FieldElementaryType(field_index);
   switch (elementary_type) {
     case flatbuffers::ET_BOOL:
       fbb->AddElement<bool>(field_offset, int_value, 0);
@@ -530,25 +488,25 @@
       return true;
     case flatbuffers::ET_STRING:
     case flatbuffers::ET_UTYPE:
-    case flatbuffers::ET_SEQUENCE:
-      fprintf(
-          stderr, "Mismatched type for field '%s'. Got: integer, expected %s\n",
-          typetable->names[field_index], ElementaryTypeName(elementary_type));
+    case flatbuffers::ET_SEQUENCE: {
+      const std::string_view name = type.FieldName(field_index);
+      fprintf(stderr,
+              "Mismatched type for field '%.*s'. Got: integer, expected %s\n",
+              static_cast<int>(name.size()), name.data(),
+              ElementaryTypeName(elementary_type));
       return false;
+    }
   };
   return false;
 }
 
-bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
-                      double double_value,
+bool AddSingleElement(FlatbufferType type, int field_index, double double_value,
                       flatbuffers::FlatBufferBuilder *fbb) {
   flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
       static_cast<flatbuffers::voffset_t>(field_index));
 
-  flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
-
   const flatbuffers::ElementaryType elementary_type =
-      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+      type.FieldElementaryType(field_index);
   switch (elementary_type) {
     case flatbuffers::ET_UTYPE:
     case flatbuffers::ET_BOOL:
@@ -561,11 +519,14 @@
     case flatbuffers::ET_LONG:
     case flatbuffers::ET_ULONG:
     case flatbuffers::ET_STRING:
-    case flatbuffers::ET_SEQUENCE:
-      fprintf(
-          stderr, "Mismatched type for field '%s'. Got: double, expected %s\n",
-          typetable->names[field_index], ElementaryTypeName(elementary_type));
+    case flatbuffers::ET_SEQUENCE: {
+      const std::string_view name = type.FieldName(field_index);
+      fprintf(stderr,
+              "Mismatched type for field '%.*s'. Got: double, expected %s\n",
+              static_cast<int>(name.size()), name.data(),
+              ElementaryTypeName(elementary_type));
       return false;
+    }
     case flatbuffers::ET_FLOAT:
       fbb->AddElement<float>(field_offset, double_value, 0);
       return true;
@@ -575,22 +536,20 @@
   }
   return false;
 }
-bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
+bool AddSingleElement(FlatbufferType type, int field_index,
                       flatbuffers::Offset<flatbuffers::String> offset_element,
                       flatbuffers::FlatBufferBuilder *fbb) {
-  flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
-
   flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
       static_cast<flatbuffers::voffset_t>(field_index));
 
   // Vectors will always be Offset<>'s.
-  if (type_code.is_repeating) {
+  if (type.FieldIsRepeating(field_index)) {
     fbb->AddOffset(field_offset, offset_element);
     return true;
   }
 
   const flatbuffers::ElementaryType elementary_type =
-      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
+      type.FieldElementaryType(field_index);
   switch (elementary_type) {
     case flatbuffers::ET_CHAR:
     case flatbuffers::ET_UCHAR:
@@ -603,16 +562,15 @@
     case flatbuffers::ET_UTYPE:
     case flatbuffers::ET_BOOL:
     case flatbuffers::ET_FLOAT:
-    case flatbuffers::ET_DOUBLE:
-      fprintf(
-          stderr, "Mismatched type for field '%s'. Got: string, expected %s\n",
-          typetable->names[field_index], ElementaryTypeName(elementary_type));
-      CHECK_EQ(type_code.sequence_ref, -1)
-          << ": Field name " << typetable->names[field_index]
-          << " Got string expected " << ElementaryTypeName(elementary_type);
+    case flatbuffers::ET_DOUBLE: {
+      const std::string_view name = type.FieldName(field_index);
+      fprintf(stderr,
+              "Mismatched type for field '%.*s'. Got: string, expected %s\n",
+              static_cast<int>(name.size()), name.data(),
+              ElementaryTypeName(elementary_type));
       return false;
+    }
     case flatbuffers::ET_STRING:
-      CHECK_EQ(type_code.sequence_ref, -1);
     case flatbuffers::ET_SEQUENCE:
       fbb->AddOffset(field_offset, offset_element);
       return true;
@@ -621,16 +579,12 @@
 }
 
 bool JsonParser::FinishVector(int field_index) {
-  flatbuffers::TypeCode type_code =
-      stack_.back().typetable->type_codes[field_index];
+  // Vectors have a start (unfortunately which needs to know the size)
+  fbb_->StartVector(stack_.back().vector_elements.size(),
+                    stack_.back().type.FieldInlineSize(field_index));
 
   const flatbuffers::ElementaryType elementary_type =
-      static_cast<flatbuffers::ElementaryType>(type_code.base_type);
-
-  // Vectors have a start (unfortunately which needs to know the size)
-  fbb_->StartVector(
-      stack_.back().vector_elements.size(),
-      flatbuffers::InlineSize(elementary_type, stack_.back().typetable));
+      stack_.back().type.FieldElementaryType(field_index);
 
   // Then the data (in reverse order for some reason...)
   for (size_t i = stack_.back().vector_elements.size(); i > 0;) {
@@ -640,8 +594,6 @@
         if (!PushElement(elementary_type, element.int_element)) return false;
         break;
       case Element::ElementType::DOUBLE:
-        CHECK_EQ(type_code.sequence_ref, -1)
-            << ": Field index is " << field_index;
         if (!PushElement(elementary_type, element.double_element)) return false;
         break;
       case Element::ElementType::OFFSET:
@@ -768,19 +720,19 @@
 }  // namespace
 
 flatbuffers::Offset<flatbuffers::Table> JsonToFlatbuffer(
-    const std::string_view data, const flatbuffers::TypeTable *typetable,
+    const std::string_view data, FlatbufferType type,
     flatbuffers::FlatBufferBuilder *fbb) {
   JsonParser p(fbb);
-  return p.Parse(data, typetable);
+  return p.Parse(data, type);
 }
 
-flatbuffers::DetachedBuffer JsonToFlatbuffer(
-    const std::string_view data, const flatbuffers::TypeTable *typetable) {
+flatbuffers::DetachedBuffer JsonToFlatbuffer(const std::string_view data,
+                                             FlatbufferType type) {
   flatbuffers::FlatBufferBuilder fbb;
   fbb.ForceDefaults(true);
 
   const flatbuffers::Offset<flatbuffers::Table> result =
-      JsonToFlatbuffer(data, typetable, &fbb);
+      JsonToFlatbuffer(data, type, &fbb);
   if (result.o != 0) {
     fbb.Finish(result);
 
diff --git a/aos/json_to_flatbuffer.h b/aos/json_to_flatbuffer.h
index c1aa736..052fa9c 100644
--- a/aos/json_to_flatbuffer.h
+++ b/aos/json_to_flatbuffer.h
@@ -7,6 +7,7 @@
 #include <string_view>
 
 #include "aos/fast_string_builder.h"
+#include "aos/flatbuffer_utils.h"
 #include "aos/flatbuffers.h"
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/reflection.h"
@@ -14,25 +15,25 @@
 namespace aos {
 
 // Parses the flatbuffer into the buffer, or returns an empty buffer.
-flatbuffers::DetachedBuffer JsonToFlatbuffer(
-    const std::string_view data, const flatbuffers::TypeTable *typetable);
+flatbuffers::DetachedBuffer JsonToFlatbuffer(std::string_view data,
+                                             FlatbufferType type);
 
 // Parses the flatbuffer into the builder, and returns the offset.
 flatbuffers::Offset<flatbuffers::Table> JsonToFlatbuffer(
-    const std::string_view data, const flatbuffers::TypeTable *typetable,
+    std::string_view data, FlatbufferType type,
     flatbuffers::FlatBufferBuilder *fbb);
 
 // Typed versions of the above methods.
 template <typename T>
 inline flatbuffers::DetachedBuffer JsonToFlatbuffer(
     const std::string_view data) {
-  return JsonToFlatbuffer(data, T::MiniReflectTypeTable());
+  return JsonToFlatbuffer(data, FlatbufferType(T::MiniReflectTypeTable()));
 }
 template <typename T>
 inline flatbuffers::Offset<T> JsonToFlatbuffer(
     const std::string_view data, flatbuffers::FlatBufferBuilder *fbb) {
   return flatbuffers::Offset<T>(
-      JsonToFlatbuffer(data, T::MiniReflectTypeTable(), fbb).o);
+      JsonToFlatbuffer(data, FlatbufferType(T::MiniReflectTypeTable()), fbb).o);
 }
 
 struct JsonOptions {
@@ -68,17 +69,17 @@
       Flatbuffer<T>::MiniReflectTypeTable(), json_options);
 }
 
-std::string FlatbufferToJson(const reflection::Schema *const schema,
-                             const uint8_t *const data,
+std::string FlatbufferToJson(const reflection::Schema *schema,
+                             const uint8_t *data,
                              JsonOptions json_options = {});
 
 void FlatbufferToJson(FastStringBuilder *builder,
-                      const reflection::Schema *const schema,
-                      const uint8_t *const data, JsonOptions json_options = {});
+                      const reflection::Schema *schema, const uint8_t *data,
+                      JsonOptions json_options = {});
 
 // Writes a Flatbuffer to a file, or dies.
 template <typename T>
-inline void WriteFlatbufferToJson(const std::string_view filename,
+inline void WriteFlatbufferToJson(std::string_view filename,
                                   const Flatbuffer<T> &msg) {
   std::ofstream json_file(std::string(filename), std::ios::out);
   CHECK(json_file) << ": Couldn't open " << filename;
@@ -88,7 +89,7 @@
 
 // Writes a NonSizePrefixedFlatbuffer to a binary file, or dies.
 template <typename T>
-inline void WriteFlatbufferToFile(const std::string_view filename,
+inline void WriteFlatbufferToFile(std::string_view filename,
                                   const NonSizePrefixedFlatbuffer<T> &msg) {
   std::ofstream file(std::string(filename),
                      std::ios::out | std::ofstream::binary);