Add a FlatbufferType implementation for a Schema

Change-Id: Ia58d86edc7bcfd1835087d8d8674b790dcbfaa67
diff --git a/aos/BUILD b/aos/BUILD
index 96c5b3d..7c976b1 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -316,14 +316,14 @@
 )
 
 flatbuffer_ts_library(
-    name = "json_to_flatbuffer_flatbuffer_ts",
+    name = "json_to_flatbuffer_fbs_ts",
     srcs = ["json_to_flatbuffer.fbs"],
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//aos:__subpackages__"],
 )
 
 flatbuffer_cc_library(
-    name = "json_to_flatbuffer_flatbuffer",
+    name = "json_to_flatbuffer_fbs",
     srcs = ["json_to_flatbuffer.fbs"],
     gen_reflections = 1,
     target_compatible_with = ["@platforms//os:linux"],
@@ -377,10 +377,13 @@
     srcs = [
         "json_to_flatbuffer_test.cc",
     ],
+    data = [
+        ":json_to_flatbuffer_fbs_reflection_out",
+    ],
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":json_to_flatbuffer",
-        ":json_to_flatbuffer_flatbuffer",
+        ":json_to_flatbuffer_fbs",
         "//aos/testing:googletest",
     ],
 )
@@ -391,12 +394,12 @@
         "flatbuffer_introspection_test.cc",
     ],
     data = [
-        ":json_to_flatbuffer_flatbuffer_reflection_out",
+        ":json_to_flatbuffer_fbs_reflection_out",
     ],
     target_compatible_with = ["@platforms//os:linux"],
     deps = [
         ":json_to_flatbuffer",
-        ":json_to_flatbuffer_flatbuffer",
+        ":json_to_flatbuffer_fbs",
         "//aos/testing:googletest",
         "//aos/util:file",
         "@com_github_google_flatbuffers//:flatbuffers",
@@ -426,7 +429,7 @@
     deps = [
         ":flatbuffer_merge",
         ":json_to_flatbuffer",
-        ":json_to_flatbuffer_flatbuffer",
+        ":json_to_flatbuffer_fbs",
         "//aos/testing:googletest",
     ],
 )
@@ -578,7 +581,7 @@
     deps = [
         ":flatbuffers",
         ":json_to_flatbuffer",
-        ":json_to_flatbuffer_flatbuffer",
+        ":json_to_flatbuffer_fbs",
         "//aos/testing:googletest",
     ],
 )
diff --git a/aos/flatbuffer_utils.cc b/aos/flatbuffer_utils.cc
index b0615df..3429fe4 100644
--- a/aos/flatbuffer_utils.cc
+++ b/aos/flatbuffer_utils.cc
@@ -1,6 +1,7 @@
 #include "aos/flatbuffer_utils.h"
 
 #include "flatbuffers/minireflect.h"
+#include "flatbuffers/reflection_generated.h"
 #include "glog/logging.h"
 
 namespace aos {
@@ -9,6 +10,12 @@
   if (type_table_) {
     return type_table_->st != flatbuffers::ST_ENUM;
   }
+  if (object_) {
+    return true;
+  }
+  if (enum_) {
+    return enum_->is_union();
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -16,6 +23,12 @@
   if (type_table_) {
     return type_table_->st == flatbuffers::ST_ENUM;
   }
+  if (object_) {
+    return false;
+  }
+  if (enum_) {
+    return !enum_->is_union();
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -30,6 +43,11 @@
     DCHECK(FieldType(index).IsSequence());
     return true;
   }
+  if (object_ || enum_) {
+    const reflection::BaseType base_type = ReflectionElementBaseType(index);
+    return base_type == reflection::BaseType::Obj ||
+           base_type == reflection::BaseType::Union;
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -48,11 +66,20 @@
     DCHECK(FieldType(index).IsEnum());
     return true;
   }
+  if (object_ || enum_) {
+    const reflection::BaseType base_type = ReflectionElementBaseType(index);
+    if (base_type == reflection::BaseType::Obj ||
+        base_type == reflection::BaseType::Union) {
+      return false;
+    }
+    return ReflectionType(index)->index() >= 0;
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
 std::optional<int64_t> FlatbufferType::EnumValue(std::string_view name) const {
   DCHECK(IsEnum());
+  DCHECK(!object_);
   if (type_table_) {
     for (size_t i = 0; i < type_table_->num_elems; ++i) {
       if (name == type_table_->names[i]) {
@@ -65,6 +92,15 @@
     }
     return std::nullopt;
   }
+  if (enum_) {
+    for (size_t i = 0; i < enum_->values()->size(); ++i) {
+      const auto *const value = enum_->values()->Get(i);
+      if (name == value->name()->string_view()) {
+        return value->value();
+      }
+    }
+    return std::nullopt;
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -75,6 +111,12 @@
     const flatbuffers::TypeCode &type_code = type_table_->type_codes[index];
     return type_code.is_repeating;
   }
+  if (object_ || enum_) {
+    const reflection::BaseType type = ReflectionType(index)->base_type();
+    CHECK(type != reflection::BaseType::None);
+    return type == reflection::BaseType::Vector ||
+           type == reflection::BaseType::Array;
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -88,6 +130,24 @@
     }
     return -1;
   }
+  if (object_) {
+    for (size_t i = 0; i < object_->fields()->size(); ++i) {
+      const reflection::Field *const field = object_->fields()->Get(i);
+      if (field_name == field->name()->string_view()) {
+        return field->id();
+      }
+    }
+    return -1;
+  }
+  if (enum_) {
+    for (size_t i = 0; i < enum_->values()->size(); ++i) {
+      const reflection::EnumVal *const value = enum_->values()->Get(i);
+      if (field_name == value->name()->string_view()) {
+        return value->value();
+      }
+    }
+    return -1;
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -97,9 +157,59 @@
     DCHECK_LT(static_cast<size_t>(index), type_table_->num_elems);
     return type_table_->names[index];
   }
+  if (object_) {
+    return ReflectionObjectField(index)->name()->string_view();
+  }
+  if (enum_) {
+    return ReflectionEnumValue(index)->name()->string_view();
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
+namespace {
+
+flatbuffers::ElementaryType ElementaryTypeFromBaseType(
+    reflection::BaseType base_type) {
+  switch (base_type) {
+    case reflection::BaseType::None:
+      LOG(FATAL) << "Invalid schema";
+    case reflection::BaseType::UType:
+      return flatbuffers::ElementaryType::ET_UTYPE;
+    case reflection::BaseType::Bool:
+      return flatbuffers::ElementaryType::ET_BOOL;
+    case reflection::BaseType::Byte:
+      return flatbuffers::ElementaryType::ET_CHAR;
+    case reflection::BaseType::UByte:
+      return flatbuffers::ElementaryType::ET_UCHAR;
+    case reflection::BaseType::Short:
+      return flatbuffers::ElementaryType::ET_SHORT;
+    case reflection::BaseType::UShort:
+      return flatbuffers::ElementaryType::ET_USHORT;
+    case reflection::BaseType::Int:
+      return flatbuffers::ElementaryType::ET_INT;
+    case reflection::BaseType::UInt:
+      return flatbuffers::ElementaryType::ET_UINT;
+    case reflection::BaseType::Long:
+      return flatbuffers::ElementaryType::ET_LONG;
+    case reflection::BaseType::ULong:
+      return flatbuffers::ElementaryType::ET_ULONG;
+    case reflection::BaseType::Float:
+      return flatbuffers::ElementaryType::ET_FLOAT;
+    case reflection::BaseType::Double:
+      return flatbuffers::ElementaryType::ET_DOUBLE;
+    case reflection::BaseType::String:
+      return flatbuffers::ElementaryType::ET_STRING;
+    case reflection::BaseType::Obj:
+      return flatbuffers::ElementaryType::ET_SEQUENCE;
+    case reflection::BaseType::Union:
+      return flatbuffers::ElementaryType::ET_SEQUENCE;
+    default:
+      LOG(FATAL) << "Unknown BaseType";
+  }
+}
+
+}  // namespace
+
 flatbuffers::ElementaryType FlatbufferType::FieldElementaryType(
     int index) const {
   DCHECK(IsSequence());
@@ -108,14 +218,69 @@
     const flatbuffers::TypeCode &type_code = type_table_->type_codes[index];
     return static_cast<flatbuffers::ElementaryType>(type_code.base_type);
   }
+  if (object_ || enum_) {
+    return ElementaryTypeFromBaseType(ReflectionElementBaseType(index));
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
+namespace {
+
+size_t BaseTypeInlineSize(reflection::BaseType base_type) {
+  switch (base_type) {
+    case reflection::BaseType::None:
+      LOG(FATAL) << "Invalid schema";
+    case reflection::BaseType::UType:
+    case reflection::BaseType::Bool:
+    case reflection::BaseType::Byte:
+    case reflection::BaseType::UByte:
+      return 1;
+    case reflection::BaseType::Short:
+    case reflection::BaseType::UShort:
+      return 2;
+    case reflection::BaseType::Int:
+    case reflection::BaseType::UInt:
+    case reflection::BaseType::Float:
+    case reflection::BaseType::String:
+      return 4;
+    case reflection::BaseType::Long:
+    case reflection::BaseType::ULong:
+    case reflection::BaseType::Double:
+      return 8;
+    case reflection::BaseType::Union:
+      return 4;
+    default:
+      LOG(FATAL) << "Unknown BaseType";
+  }
+}
+
+}  // namespace
+
 size_t FlatbufferType::FieldInlineSize(int index) const {
   DCHECK(IsSequence());
   if (type_table_) {
     return flatbuffers::InlineSize(FieldElementaryType(index), type_table_);
   }
+  if (object_ || enum_) {
+    const reflection::Type *const type = ReflectionType(index);
+    const reflection::BaseType element_base_type =
+        ReflectionElementBaseType(index);
+    int element_size;
+    if (element_base_type == reflection::BaseType::Obj) {
+      const FlatbufferType field_type = FieldType(index);
+      if (field_type.object_ && field_type.object_->is_struct()) {
+        element_size = field_type.object_->bytesize();
+      } else {
+        element_size = 4;
+      }
+    } else {
+      element_size = BaseTypeInlineSize(element_base_type);
+    }
+    if (type->base_type() == reflection::BaseType::Array) {
+      return element_size * type->fixed_length();
+    }
+    return element_size;
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -124,6 +289,12 @@
   if (type_table_) {
     return type_table_->num_elems;
   }
+  if (object_) {
+    return object_->fields()->size();
+  }
+  if (enum_) {
+    return enum_->values()->size();
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
@@ -141,7 +312,62 @@
         type_table_->type_refs[type_code.sequence_ref];
     return FlatbufferType(type_function());
   }
+  if (object_ || enum_) {
+    const reflection::BaseType base_type = ReflectionElementBaseType(index);
+    const int object_index = ReflectionType(index)->index();
+    CHECK(object_index >= 0) << ": Invalid schema";
+    if (base_type == reflection::BaseType::Obj ||
+        base_type == reflection::BaseType::Union) {
+      DCHECK_LT(static_cast<size_t>(object_index), schema_->objects()->size());
+      return FlatbufferType(schema_, schema_->objects()->Get(object_index));
+    } else {
+      DCHECK_LT(static_cast<size_t>(object_index), schema_->enums()->size());
+      return FlatbufferType(schema_, schema_->enums()->Get(object_index));
+    }
+  }
   LOG(FATAL) << "Unimplemented";
 }
 
+const reflection::Type *FlatbufferType::ReflectionType(int index) const {
+  if (object_) {
+    return ReflectionObjectField(index)->type();
+  } else {
+    return ReflectionEnumValue(index)->union_type();
+  }
+}
+
+const reflection::Field *FlatbufferType::ReflectionObjectField(
+    int index) const {
+  DCHECK(object_);
+  const auto result = std::find_if(
+      object_->fields()->begin(), object_->fields()->end(),
+      [index](const reflection::Field *field) { return field->id() == index; });
+  DCHECK(result != object_->fields()->end());
+  return *result;
+}
+
+const reflection::EnumVal *FlatbufferType::ReflectionEnumValue(
+    int index) const {
+  DCHECK(enum_);
+  const auto result =
+      std::find_if(enum_->values()->begin(), enum_->values()->end(),
+                   [index](const reflection::EnumVal *value) {
+                     return value->value() == index;
+                   });
+  DCHECK(result != enum_->values()->end());
+  return *result;
+}
+
+reflection::BaseType FlatbufferType::ReflectionElementBaseType(
+    int index) const {
+  const reflection::Type *const type = ReflectionType(index);
+  reflection::BaseType base_type = type->base_type();
+  if (base_type == reflection::BaseType::Vector ||
+      base_type == reflection::BaseType::Array) {
+    base_type = type->element();
+  }
+  CHECK(base_type != reflection::BaseType::None);
+  return base_type;
+}
+
 }  // namespace aos
diff --git a/aos/flatbuffer_utils.h b/aos/flatbuffer_utils.h
index 2118fd6..0287b6d 100644
--- a/aos/flatbuffer_utils.h
+++ b/aos/flatbuffer_utils.h
@@ -5,6 +5,7 @@
 #include <string_view>
 
 #include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/reflection_generated.h"
 #include "glog/logging.h"
 
 namespace aos {
@@ -25,6 +26,9 @@
   // Implicit on purpose, to allow freely creating a FlatbufferType.
   FlatbufferType(const flatbuffers::TypeTable *type_table)
       : type_table_(CHECK_NOTNULL(type_table)) {}
+  FlatbufferType(const reflection::Schema *schema)
+      : schema_(CHECK_NOTNULL(schema)),
+        object_(DCHECK_NOTNULL(schema->root_table())) {}
 
   // This is deliberately copyable, for ease of memory management. It is cheap
   // to pass by value.
@@ -92,7 +96,22 @@
   FlatbufferType FieldType(int index) const;
 
  private:
+  explicit FlatbufferType(const reflection::Schema *schema,
+                          const reflection::Object *object)
+      : schema_(DCHECK_NOTNULL(schema)), object_(DCHECK_NOTNULL(object)) {}
+  explicit FlatbufferType(const reflection::Schema *schema,
+                          const reflection::Enum *fb_enum)
+      : schema_(DCHECK_NOTNULL(schema)), enum_(DCHECK_NOTNULL(fb_enum)) {}
+
+  const reflection::Type *ReflectionType(int index) const;
+  const reflection::Field *ReflectionObjectField(int index) const;
+  const reflection::EnumVal *ReflectionEnumValue(int index) const;
+  reflection::BaseType ReflectionElementBaseType(int index) const;
+
   const flatbuffers::TypeTable *type_table_ = nullptr;
+  const reflection::Schema *schema_ = nullptr;
+  const reflection::Object *object_ = nullptr;
+  const reflection::Enum *enum_ = nullptr;
 };
 
 }  // namespace aos
diff --git a/aos/json_to_flatbuffer_test.cc b/aos/json_to_flatbuffer_test.cc
index dd70908..9dc12d2 100644
--- a/aos/json_to_flatbuffer_test.cc
+++ b/aos/json_to_flatbuffer_test.cc
@@ -11,22 +11,33 @@
  public:
   JsonToFlatbufferTest() {}
 
+  FlatbufferVector<reflection::Schema> Schema() {
+    return FileToFlatbuffer<reflection::Schema>("aos/json_to_flatbuffer.bfbs");
+  }
+
   bool JsonAndBack(const ::std::string str) { return JsonAndBack(str, str); }
 
   bool JsonAndBack(const ::std::string in, const ::std::string out) {
     printf("Testing: %s\n", in.c_str());
-    FlatbufferDetachedBuffer<Configuration> fb =
+    FlatbufferDetachedBuffer<Configuration> fb_typetable =
         JsonToFlatbuffer<Configuration>(in.data());
+    FlatbufferDetachedBuffer<Configuration> fb_reflection =
+        JsonToFlatbuffer(in.data(), FlatbufferType(&Schema().message()));
 
-    if (fb.span().size() == 0) {
+    if (fb_typetable.span().size() == 0) {
+      return false;
+    }
+    if (fb_reflection.span().size() == 0) {
       return false;
     }
 
-    const ::std::string back = FlatbufferToJson(fb);
+    const ::std::string back_typetable = FlatbufferToJson(fb_typetable);
+    const ::std::string back_reflection = FlatbufferToJson(fb_reflection);
 
-    printf("Back to string: %s\n", back.c_str());
+    printf("Back to string via TypeTable: %s\n", back_typetable.c_str());
+    printf("Back to string via reflection: %s\n", back_reflection.c_str());
 
-    return back == out;
+    return back_typetable == out && back_reflection == out;
   }
 };
 
@@ -222,20 +233,35 @@
   json_short += " ] }";
   json_long += ", 101 ] }";
 
-  const FlatbufferDetachedBuffer<Configuration> fb_short(
+  const FlatbufferDetachedBuffer<Configuration> fb_short_typetable(
       JsonToFlatbuffer<Configuration>(json_short));
-  ASSERT_GT(fb_short.span().size(), 0);
-  const FlatbufferDetachedBuffer<Configuration> fb_long(
+  ASSERT_GT(fb_short_typetable.span().size(), 0);
+  const FlatbufferDetachedBuffer<Configuration> fb_long_typetable(
       JsonToFlatbuffer<Configuration>(json_long));
-  ASSERT_GT(fb_long.span().size(), 0);
+  ASSERT_GT(fb_long_typetable.span().size(), 0);
+  const FlatbufferDetachedBuffer<Configuration> fb_short_reflection(
+      JsonToFlatbuffer(json_short, FlatbufferType(&Schema().message())));
+  ASSERT_GT(fb_short_reflection.span().size(), 0);
+  const FlatbufferDetachedBuffer<Configuration> fb_long_reflection(
+      JsonToFlatbuffer(json_long, FlatbufferType(&Schema().message())));
+  ASSERT_GT(fb_long_reflection.span().size(), 0);
 
-  const std::string back_json_short = FlatbufferToJson<Configuration>(
-      fb_short, {.multi_line = false, .max_vector_size = 100});
-  const std::string back_json_long = FlatbufferToJson<Configuration>(
-      fb_long, {.multi_line = false, .max_vector_size = 100});
+  const std::string back_json_short_typetable = FlatbufferToJson<Configuration>(
+      fb_short_typetable, {.multi_line = false, .max_vector_size = 100});
+  const std::string back_json_long_typetable = FlatbufferToJson<Configuration>(
+      fb_long_typetable, {.multi_line = false, .max_vector_size = 100});
+  const std::string back_json_short_reflection =
+      FlatbufferToJson<Configuration>(
+          fb_short_reflection, {.multi_line = false, .max_vector_size = 100});
+  const std::string back_json_long_reflection = FlatbufferToJson<Configuration>(
+      fb_long_reflection, {.multi_line = false, .max_vector_size = 100});
 
-  EXPECT_EQ(json_short, back_json_short);
-  EXPECT_EQ("{ \"vector_foo_int\": [ ... 101 elements ... ] }", back_json_long);
+  EXPECT_EQ(json_short, back_json_short_typetable);
+  EXPECT_EQ(json_short, back_json_short_reflection);
+  EXPECT_EQ("{ \"vector_foo_int\": [ ... 101 elements ... ] }",
+            back_json_long_typetable);
+  EXPECT_EQ("{ \"vector_foo_int\": [ ... 101 elements ... ] }",
+            back_json_long_reflection);
 }
 
 // Tests that a nullptr buffer prints nullptr.
diff --git a/aos/network/www/BUILD b/aos/network/www/BUILD
index a153ff3..1caa57c 100644
--- a/aos/network/www/BUILD
+++ b/aos/network/www/BUILD
@@ -74,7 +74,7 @@
     deps = [
         ":reflection_ts",
         "//aos:configuration_ts_fbs",
-        "//aos:json_to_flatbuffer_flatbuffer_ts",
+        "//aos:json_to_flatbuffer_fbs_ts",
         "//aos/network/www:proxy",
         "@com_github_google_flatbuffers//ts:flatbuffers_ts",
     ],
@@ -87,7 +87,7 @@
     visibility = ["//visibility:public"],
     deps = [
         "//aos:configuration_ts_fbs",
-        "//aos:json_to_flatbuffer_flatbuffer_ts",
+        "//aos:json_to_flatbuffer_fbs_ts",
         "@com_github_google_flatbuffers//ts:flatbuffers_ts",
     ],
 )
@@ -144,7 +144,7 @@
     src = "test_config_file.json",
     flatbuffers = [
         "//aos:configuration_fbs",
-        "//aos:json_to_flatbuffer_flatbuffer",
+        "//aos:json_to_flatbuffer_fbs",
     ],
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//visibility:public"],