Convert aos over to flatbuffers

Everything builds, and all the tests pass.  I suspect that some entries
are missing from the config files, but those will be found pretty
quickly on startup.

There is no logging or live introspection of queue messages.

Change-Id: I496ee01ed68f202c7851bed7e8786cee30df29f5
diff --git a/aos/configuration.cc b/aos/configuration.cc
index cd92f20..bcb67ba 100644
--- a/aos/configuration.cc
+++ b/aos/configuration.cc
@@ -22,11 +22,6 @@
 
 // Define the compare and equal operators for Channel and Application so we can
 // insert them in the btree below.
-//
-// These are not in headers because they are only comparing part of the
-// flatbuffer, and it seems weird to expose that as *the* compare operator.  And
-// I can't put them in an anonymous namespace because they wouldn't be found
-// that way by the btree.
 bool operator<(const FlatbufferDetachedBuffer<Channel> &lhs,
                const FlatbufferDetachedBuffer<Channel> &rhs) {
   int name_compare = lhs.message().name()->string_view().compare(
@@ -154,8 +149,12 @@
 
 FlatbufferDetachedBuffer<Configuration> ReadConfig(
     const absl::string_view path, absl::btree_set<std::string> *visited_paths) {
-  FlatbufferDetachedBuffer<Configuration> config(JsonToFlatbuffer(
-      util::ReadFileToStringOrDie(path), ConfigurationTypeTable()));
+  flatbuffers::DetachedBuffer buffer = JsonToFlatbuffer(
+      util::ReadFileToStringOrDie(path), ConfigurationTypeTable());
+
+  CHECK_GT(buffer.size(), 0u) << ": Failed to parse JSON file";
+
+  FlatbufferDetachedBuffer<Configuration> config(std::move(buffer));
   // Depth first.  Take the following example:
   //
   // config1.json:
@@ -222,7 +221,54 @@
   return config;
 }
 
-// Remove duplicate entries, and handle overrides.
+// Compares (c < p) a channel, and a name, type tuple.
+bool CompareChannels(const Channel *c,
+                     ::std::pair<absl::string_view, absl::string_view> p) {
+  int name_compare = c->name()->string_view().compare(p.first);
+  if (name_compare == 0) {
+    return c->type()->string_view() < p.second;
+  } else if (name_compare < 0) {
+    return true;
+  } else {
+    return false;
+  }
+};
+
+// Compares for equality (c == p) a channel, and a name, type tuple.
+bool EqualsChannels(const Channel *c,
+                     ::std::pair<absl::string_view, absl::string_view> p) {
+  return c->name()->string_view() == p.first &&
+         c->type()->string_view() == p.second;
+}
+
+// Compares (c < p) an application, and a name;
+bool CompareApplications(const Application *a, absl::string_view name) {
+  return a->name()->string_view() < name;
+};
+
+// Compares for equality (c == p) an application, and a name;
+bool EqualsApplications(const Application *a, absl::string_view name) {
+  return a->name()->string_view() == name;
+}
+
+// Maps name for the provided maps.  Modifies name.
+void HandleMaps(const flatbuffers::Vector<flatbuffers::Offset<aos::Map>> *maps,
+                absl::string_view *name) {
+  // For the same reason we merge configs in reverse order, we want to process
+  // maps in reverse order.  That lets the outer config overwrite channels from
+  // the inner configs.
+  for (auto i = maps->rbegin(); i != maps->rend(); ++i) {
+    if (i->has_match() && i->match()->has_name() && i->has_rename() &&
+        i->rename()->has_name() && i->match()->name()->string_view() == *name) {
+      VLOG(1) << "Renamed \"" << *name << "\" to \""
+              << i->rename()->name()->string_view() << "\"";
+      *name = i->rename()->name()->string_view();
+    }
+  }
+}
+
+}  // namespace
+
 FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
     const Flatbuffer<Configuration> &config) {
   // Store all the channels in a sorted set.  This lets us track channels we
@@ -316,54 +362,6 @@
   return fbb.Release();
 }
 
-// Compares (c < p) a channel, and a name, type tuple.
-bool CompareChannels(const Channel *c,
-                     ::std::pair<absl::string_view, absl::string_view> p) {
-  int name_compare = c->name()->string_view().compare(p.first);
-  if (name_compare == 0) {
-    return c->type()->string_view() < p.second;
-  } else if (name_compare < 0) {
-    return true;
-  } else {
-    return false;
-  }
-};
-
-// Compares for equality (c == p) a channel, and a name, type tuple.
-bool EqualsChannels(const Channel *c,
-                     ::std::pair<absl::string_view, absl::string_view> p) {
-  return c->name()->string_view() == p.first &&
-         c->type()->string_view() == p.second;
-}
-
-// Compares (c < p) an application, and a name;
-bool CompareApplications(const Application *a, absl::string_view name) {
-  return a->name()->string_view() < name;
-};
-
-// Compares for equality (c == p) an application, and a name;
-bool EqualsApplications(const Application *a, absl::string_view name) {
-  return a->name()->string_view() == name;
-}
-
-// Maps name for the provided maps.  Modifies name.
-void HandleMaps(const flatbuffers::Vector<flatbuffers::Offset<aos::Map>> *maps,
-                absl::string_view *name) {
-  // For the same reason we merge configs in reverse order, we want to process
-  // maps in reverse order.  That lets the outer config overwrite channels from
-  // the inner configs.
-  for (auto i = maps->rbegin(); i != maps->rend(); ++i) {
-    if (i->has_match() && i->match()->has_name() && i->has_rename() &&
-        i->rename()->has_name() && i->match()->name()->string_view() == *name) {
-      VLOG(1) << "Renamed \"" << *name << "\" to \""
-              << i->rename()->name()->string_view() << "\"";
-      *name = i->rename()->name()->string_view();
-    }
-  }
-}
-
-}  // namespace
-
 const char *GetRootDirectory() {
   static  char* root_dir;// return value
   static absl::once_flag once_;
@@ -417,6 +415,9 @@
     HandleMaps(config->maps(), &name);
   }
 
+  VLOG(1) << "Acutally looking up { \"name\": \"" << name << "\", \"type\": \""
+          << type << "\" }";
+
   // Then look for the channel.
   auto channel_iterator =
       std::lower_bound(config->channels()->cbegin(),
@@ -435,5 +436,96 @@
   }
 }
 
+FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
+    const Flatbuffer<Configuration> &config,
+    const std::vector<aos::FlatbufferString<reflection::Schema>> &schemas) {
+  flatbuffers::FlatBufferBuilder fbb;
+  fbb.ForceDefaults(1);
+
+  flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
+      channels_offset;
+  if (config.message().has_channels()) {
+    std::vector<flatbuffers::Offset<Channel>> channel_offsets;
+    for (const Channel *c : *config.message().channels()) {
+      flatbuffers::FlatBufferBuilder channel_fbb;
+      channel_fbb.ForceDefaults(1);
+
+      // Search for a schema with a matching type.
+      const aos::FlatbufferString<reflection::Schema> *found_schema = nullptr;
+      for (const aos::FlatbufferString<reflection::Schema> &schema: schemas) {
+        if (schema.message().root_table() != nullptr) {
+          if (schema.message().root_table()->name()->string_view() ==
+              c->type()->string_view()) {
+            found_schema = &schema;
+          }
+        }
+      }
+
+      CHECK(found_schema != nullptr)
+          << ": Failed to find schema for " << FlatbufferToJson(c);
+
+      // The following is wasteful, but works.
+      //
+      // Copy it into a Channel object by creating an object with only the
+      // schema populated and merge that into the current channel.
+      flatbuffers::Offset<reflection::Schema> schema_offset =
+          CopyFlatBuffer<reflection::Schema>(&found_schema->message(),
+                                             &channel_fbb);
+      Channel::Builder channel_builder(channel_fbb);
+      channel_builder.add_schema(schema_offset);
+      channel_fbb.Finish(channel_builder.Finish());
+      FlatbufferDetachedBuffer<Channel> channel_schema_flatbuffer(
+          channel_fbb.Release());
+
+      FlatbufferDetachedBuffer<Channel> merged_channel(
+          MergeFlatBuffers(channel_schema_flatbuffer, CopyFlatBuffer(c)));
+
+      channel_offsets.emplace_back(
+          CopyFlatBuffer<Channel>(&merged_channel.message(), &fbb));
+    }
+    channels_offset = fbb.CreateVector(channel_offsets);
+  }
+
+  // Copy the applications and maps unmodified.
+  flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Application>>>
+      applications_offset;
+  {
+    ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
+    if (config.message().has_applications()) {
+      for (const Application *a : *config.message().applications()) {
+        applications_offsets.emplace_back(CopyFlatBuffer<Application>(a, &fbb));
+      }
+    }
+    applications_offset = fbb.CreateVector(applications_offsets);
+  }
+
+  flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Map>>>
+      maps_offset;
+  {
+    ::std::vector<flatbuffers::Offset<Map>> map_offsets;
+    if (config.message().has_maps()) {
+      for (const Map *m : *config.message().maps()) {
+        map_offsets.emplace_back(CopyFlatBuffer<Map>(m, &fbb));
+      }
+      maps_offset = fbb.CreateVector(map_offsets);
+    }
+  }
+
+  // Now insert eerything else in unmodified.
+  ConfigurationBuilder configuration_builder(fbb);
+  if (config.message().has_channels()) {
+    configuration_builder.add_channels(channels_offset);
+  }
+  if (config.message().has_maps()) {
+    configuration_builder.add_maps(maps_offset);
+  }
+  if (config.message().has_applications()) {
+    configuration_builder.add_applications(applications_offset);
+  }
+
+  fbb.Finish(configuration_builder.Finish());
+  return fbb.Release();
+}
+
 }  // namespace configuration
 }  // namespace aos