Add a FlatbufferType implementation for a Schema

Change-Id: Ia58d86edc7bcfd1835087d8d8674b790dcbfaa67
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