Rename Location to Channel and add extra Flatbuffer helpers

This now follows the convention in the design doc and is more closely
aligned to classic pubsub terminology.

Also add a Flatbuffer<T> base type, along with both an array backed
version.  Rename the DetachedBuffer variant.

Change-Id: I257b5dbb5838120c479c4b98139657fc08e73150
diff --git a/aos/BUILD b/aos/BUILD
index 9662ee3..206eefb 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -501,11 +501,16 @@
 
 cc_library(
     name = "flatbuffers",
+    srcs = [
+        "flatbuffers.cc",
+    ],
     hdrs = [
         "flatbuffers.h",
     ],
+    visibility = ["//visibility:public"],
     deps = [
         "@com_github_google_flatbuffers//:flatbuffers",
+        "@com_github_google_glog//:glog",
     ],
 )
 
diff --git a/aos/configuration.cc b/aos/configuration.cc
index a1cc614..56c5b0e 100644
--- a/aos/configuration.cc
+++ b/aos/configuration.cc
@@ -20,15 +20,15 @@
 
 namespace aos {
 
-// Define the compare and equal operators for Location and Application so we can
+// 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 Flatbuffer<Location> &lhs,
-               const Flatbuffer<Location> &rhs) {
+bool operator<(const FlatbufferDetachedBuffer<Channel> &lhs,
+               const FlatbufferDetachedBuffer<Channel> &rhs) {
   int name_compare = lhs.message().name()->string_view().compare(
       rhs.message().name()->string_view());
   if (name_compare == 0) {
@@ -41,22 +41,22 @@
   }
 }
 
-bool operator==(const Flatbuffer<Location> &lhs,
-                const Flatbuffer<Location> &rhs) {
+bool operator==(const FlatbufferDetachedBuffer<Channel> &lhs,
+                const FlatbufferDetachedBuffer<Channel> &rhs) {
   return lhs.message().name()->string_view() ==
              rhs.message().name()->string_view() &&
          lhs.message().type()->string_view() ==
              rhs.message().type()->string_view();
 }
 
-bool operator==(const Flatbuffer<Application> &lhs,
-                const Flatbuffer<Application> &rhs) {
+bool operator==(const FlatbufferDetachedBuffer<Application> &lhs,
+                const FlatbufferDetachedBuffer<Application> &rhs) {
   return lhs.message().name()->string_view() ==
          rhs.message().name()->string_view();
 }
 
-bool operator<(const Flatbuffer<Application> &lhs,
-               const Flatbuffer<Application> &rhs) {
+bool operator<(const FlatbufferDetachedBuffer<Application> &lhs,
+               const FlatbufferDetachedBuffer<Application> &rhs) {
   return lhs.message().name()->string_view() <
          rhs.message().name()->string_view();
 }
@@ -154,15 +154,15 @@
              : filename.substr(0, last_slash_pos + 1);
 }
 
-Flatbuffer<Configuration> ReadConfig(
+FlatbufferDetachedBuffer<Configuration> ReadConfig(
     const absl::string_view path, absl::btree_set<std::string> *visited_paths) {
-  Flatbuffer<Configuration> config(JsonToFlatbuffer(
+  FlatbufferDetachedBuffer<Configuration> config(JsonToFlatbuffer(
       util::ReadFileToStringOrDie(path), ConfigurationTypeTable()));
   // Depth first.  Take the following example:
   //
   // config1.json:
   // {
-  //   "locations": [
+  //   "channels": [
   //     {
   //       "name": "/foo",
   //       "type": ".aos.bar",
@@ -176,7 +176,7 @@
   //
   // config2.json:
   // {
-  //   "locations": [
+  //   "channels": [
   //     {
   //       "name": "/foo",
   //       "type": ".aos.bar",
@@ -201,8 +201,8 @@
     config.mutable_message()->clear_imports();
 
     // Start with an empty configuration to merge into.
-    Flatbuffer<Configuration> merged_config =
-        Flatbuffer<Configuration>::Empty();
+    FlatbufferDetachedBuffer<Configuration> merged_config =
+        FlatbufferDetachedBuffer<Configuration>::Empty();
 
     const ::std::string folder(ExtractFolder(path));
 
@@ -225,33 +225,33 @@
 }
 
 // Remove duplicate entries, and handle overrides.
-Flatbuffer<Configuration> MergeConfiguration(
+FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
     const Flatbuffer<Configuration> &config) {
-  // Store all the locations in a sorted set.  This lets us track locations we
+  // Store all the channels in a sorted set.  This lets us track channels we
   // have seen before and merge the updates in.
-  absl::btree_set<Flatbuffer<Location>> locations;
+  absl::btree_set<FlatbufferDetachedBuffer<Channel>> channels;
 
-  if (config.message().has_locations()) {
-    for (const Location *l : *config.message().locations()) {
+  if (config.message().has_channels()) {
+    for (const Channel *c : *config.message().channels()) {
       // Ignore malformed entries.
-      if (!l->has_name()) {
+      if (!c->has_name()) {
         continue;
       }
-      if (!l->has_type()) {
+      if (!c->has_type()) {
         continue;
       }
 
-      // Attempt to insert the location.
-      auto result = locations.insert(CopyFlatBuffer(l));
+      // Attempt to insert the channel.
+      auto result = channels.insert(CopyFlatBuffer(c));
       if (!result.second) {
         // Already there, so merge the new table into the original.
-        *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(l));
+        *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(c));
       }
     }
   }
 
   // Now repeat this for the application list.
-  absl::btree_set<Flatbuffer<Application>> applications;
+  absl::btree_set<FlatbufferDetachedBuffer<Application>> applications;
   if (config.message().has_applications()) {
     for (const Application *a : *config.message().applications()) {
       if (!a->has_name()) {
@@ -269,16 +269,16 @@
   fbb.ForceDefaults(1);
 
   // Start by building the vectors.  They need to come before the final table.
-  // Locations
-  flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Location>>>
-      locations_offset;
+  // Channels
+  flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
+      channels_offset;
   {
-    ::std::vector<flatbuffers::Offset<Location>> location_offsets;
-    for (const Flatbuffer<Location> &l : locations) {
-      location_offsets.emplace_back(
-          CopyFlatBuffer<Location>(&l.message(), &fbb));
+    ::std::vector<flatbuffers::Offset<Channel>> channel_offsets;
+    for (const FlatbufferDetachedBuffer<Channel> &c : channels) {
+      channel_offsets.emplace_back(
+          CopyFlatBuffer<Channel>(&c.message(), &fbb));
     }
-    locations_offset = fbb.CreateVector(location_offsets);
+    channels_offset = fbb.CreateVector(channel_offsets);
   }
 
   // Applications
@@ -286,7 +286,7 @@
       applications_offset;
   {
     ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
-    for (const Flatbuffer<Application> &a : applications) {
+    for (const FlatbufferDetachedBuffer<Application> &a : applications) {
       applications_offsets.emplace_back(
           CopyFlatBuffer<Application>(&a.message(), &fbb));
     }
@@ -308,7 +308,7 @@
 
   // And then build a Configuration with them all.
   ConfigurationBuilder configuration_builder(fbb);
-  configuration_builder.add_locations(locations_offset);
+  configuration_builder.add_channels(channels_offset);
   if (config.message().has_maps()) {
     configuration_builder.add_maps(maps_offset);
   }
@@ -318,12 +318,12 @@
   return fbb.Release();
 }
 
-// Compares (l < p) a location, and a name, type tuple.
-bool CompareLocations(const Location *l,
-                      ::std::pair<absl::string_view, absl::string_view> p) {
-  int name_compare = l->name()->string_view().compare(p.first);
+// 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 l->type()->string_view() < p.second;
+    return c->type()->string_view() < p.second;
   } else if (name_compare < 0) {
     return true;
   } else {
@@ -331,19 +331,19 @@
   }
 };
 
-// Compares for equality (l == p) a location, and a name, type tuple.
-bool EqualsLocations(const Location *l,
+// 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 l->name()->string_view() == p.first &&
-         l->type()->string_view() == p.second;
+  return c->name()->string_view() == p.first &&
+         c->type()->string_view() == p.second;
 }
 
-// Compares (l < p) an application, and a name;
+// 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 (l == p) an application, and a 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;
 }
@@ -352,7 +352,7 @@
 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 locations from
+  // 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() &&
@@ -381,26 +381,26 @@
   return *once.Get();
 }
 
-Flatbuffer<Configuration> ReadConfig(const absl::string_view path) {
+FlatbufferDetachedBuffer<Configuration> ReadConfig(
+    const absl::string_view path) {
   // We only want to read a file once.  So track the visited files in a set.
   absl::btree_set<std::string> visited_paths;
   return MergeConfiguration(ReadConfig(path, &visited_paths));
 }
 
-const Location *GetLocation(const Flatbuffer<Configuration> &config,
-                            absl::string_view name, absl::string_view type,
-                            absl::string_view application_name) {
+const Channel *GetChannel(const Configuration *config, absl::string_view name,
+                          absl::string_view type,
+                          absl::string_view application_name) {
   VLOG(1) << "Looking up { \"name\": \"" << name << "\", \"type\": \"" << type
           << "\" }";
 
   // First handle application specific maps.  Only do this if we have a matching
   // application name, and it has maps.
-  if (config.message().has_applications()) {
-    auto application_iterator =
-        std::lower_bound(config.message().applications()->cbegin(),
-                         config.message().applications()->cend(),
-                         application_name, CompareApplications);
-    if (application_iterator != config.message().applications()->cend() &&
+  if (config->has_applications()) {
+    auto application_iterator = std::lower_bound(
+        config->applications()->cbegin(), config->applications()->cend(),
+        application_name, CompareApplications);
+    if (application_iterator != config->applications()->cend() &&
         EqualsApplications(*application_iterator, application_name)) {
       if (application_iterator->has_maps()) {
         HandleMaps(application_iterator->maps(), &name);
@@ -409,21 +409,21 @@
   }
 
   // Now do global maps.
-  if (config.message().has_maps()) {
-    HandleMaps(config.message().maps(), &name);
+  if (config->has_maps()) {
+    HandleMaps(config->maps(), &name);
   }
 
-  // Then look for the location.
-  auto location_iterator =
-      std::lower_bound(config.message().locations()->cbegin(),
-                       config.message().locations()->cend(),
-                       std::make_pair(name, type), CompareLocations);
+  // Then look for the channel.
+  auto channel_iterator =
+      std::lower_bound(config->channels()->cbegin(),
+                       config->channels()->cend(),
+                       std::make_pair(name, type), CompareChannels);
 
   // Make sure we actually found it, and it matches.
-  if (location_iterator != config.message().locations()->cend() &&
-      EqualsLocations(*location_iterator, std::make_pair(name, type))) {
-    VLOG(1) << "Found: " << FlatbufferToJson(*location_iterator);
-    return *location_iterator;
+  if (channel_iterator != config->channels()->cend() &&
+      EqualsChannels(*channel_iterator, std::make_pair(name, type))) {
+    VLOG(1) << "Found: " << FlatbufferToJson(*channel_iterator);
+    return *channel_iterator;
   } else {
     VLOG(1) << "No match for { \"name\": \"" << name << "\", \"type\": \""
             << type << "\" }";
diff --git a/aos/configuration.fbs b/aos/configuration.fbs
index bc0f40e..d23c4b9 100644
--- a/aos/configuration.fbs
+++ b/aos/configuration.fbs
@@ -1,26 +1,26 @@
 namespace aos;
 
-// Table representing a location.  Locations are where data is published and
+// Table representing a channel.  Channels are where data is published and
 // subscribed from.  The tuple of name, type is the identifying information.
-table Location {
-  // Name of the location.
+table Channel {
+  // Name of the channel.
   name:string;
   // Type name of the flatbuffer.
   type:string;
-  // Max frequency in messages/sec of the data published on this location.
+  // Max frequency in messages/sec of the data published on this channel.
   frequency:int = 100;
   // Max size of the data being published.  (This will be automatically
   // computed in the future.)
   max_size:int = 1000;
 }
 
-// Table to support renaming location names.
+// Table to support renaming channel names.
 table Map {
-  // Location to match with.  If the name in here matches, the name is replaced
+  // Channel to match with.  If the name in here matches, the name is replaced
   // with the name in rename.
-  match:Location;
-  // The location to merge in.
-  rename:Location;
+  match:Channel;
+  // The channel to merge in.
+  rename:Channel;
 }
 
 // Application specific information.
@@ -31,9 +31,9 @@
   // maps are applied in reverse order, and before the global maps.
   // For example
   //   "maps": [ { "match": { "name": "/foo" }, "rename": { "name": "/bar" } } ]
-  // will make it so any locations named "/foo" actually go to "/bar" for just
+  // will make it so any channels named "/foo" actually go to "/bar" for just
   // this application.  This is super handy for running an application twice
-  // publishing to different locations, or for injecting a little application
+  // publishing to different channels, or for injecting a little application
   // to modify messages live for testing.
   //
   //   "maps": [
@@ -47,8 +47,8 @@
 
 // Overall configuration datastructure for the pubsub.
 table Configuration {
-  // List of locations.
-  locations:[Location] (id: 0);
+  // List of channels.
+  channels:[Channel] (id: 0);
   // List of global maps.  These are applied in reverse order.
   maps:[Map] (id: 1);
   // List of applications.
diff --git a/aos/configuration.h b/aos/configuration.h
index b30a2b3..3cee8e7 100644
--- a/aos/configuration.h
+++ b/aos/configuration.h
@@ -18,16 +18,24 @@
 
 // Reads a json configuration.  This includes all imports and merges.  Note:
 // duplicate imports will result in a CHECK.
-Flatbuffer<Configuration> ReadConfig(const absl::string_view path);
+FlatbufferDetachedBuffer<Configuration> ReadConfig(
+    const absl::string_view path);
 
-// Returns the resolved location for a name, type, and application name.
+// Returns the resolved location for a name, type, and application name. Returns
+// nullptr if none is found.
 //
 // If the application name is empty, it is ignored.  Maps are processed in
 // reverse order, and application specific first.
-const Location *GetLocation(const Flatbuffer<Configuration> &config,
-                            const absl::string_view name,
-                            const absl::string_view type,
-                            const absl::string_view application_name);
+const Channel *GetChannel(const Configuration *config,
+                          const absl::string_view name,
+                          const absl::string_view type,
+                          const absl::string_view application_name);
+inline const Channel *GetChannel(const Flatbuffer<Configuration> &config,
+                          const absl::string_view name,
+                          const absl::string_view type,
+                          const absl::string_view application_name) {
+  return GetChannel(&config.message(), name, type, application_name);
+}
 
 // Returns "our" IP address.
 const in_addr &GetOwnIPAddress();
diff --git a/aos/configuration_test.cc b/aos/configuration_test.cc
index 3177265..0f13041 100644
--- a/aos/configuration_test.cc
+++ b/aos/configuration_test.cc
@@ -23,7 +23,8 @@
 
 // Tests that we can read and merge a configuration.
 TEST_F(ConfigurationTest, ConfigMerge) {
-  Flatbuffer<Configuration> config = ReadConfig("aos/testdata/config1.json");
+  FlatbufferDetachedBuffer<Configuration> config =
+      ReadConfig("aos/testdata/config1.json");
   printf("Read: %s\n", FlatbufferToJson(config, true).c_str());
 
   EXPECT_EQ(
@@ -36,7 +37,7 @@
 TEST_F(ConfigurationDeathTest, DuplicateFile) {
   EXPECT_DEATH(
       {
-        Flatbuffer<Configuration> config =
+        FlatbufferDetachedBuffer<Configuration> config =
             ReadConfig("aos/testdata/config1_bad.json");
       },
       "aos/testdata/config1_bad.json");
@@ -44,30 +45,31 @@
 
 // Tests that we can lookup a location, complete with maps, from a merged
 // config.
-TEST_F(ConfigurationTest, GetLocation) {
-  Flatbuffer<Configuration> config = ReadConfig("aos/testdata/config1.json");
+TEST_F(ConfigurationTest, GetChannel) {
+  FlatbufferDetachedBuffer<Configuration> config =
+      ReadConfig("aos/testdata/config1.json");
 
   // Test a basic lookup first.
-  EXPECT_EQ(FlatbufferToJson(GetLocation(config, "/foo", ".aos.bar", "app1")),
+  EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/foo", ".aos.bar", "app1")),
             kExpectedLocation);
 
   // Test that an invalid name results in nullptr back.
-  EXPECT_EQ(GetLocation(config, "/invalid_name", ".aos.bar", "app1"), nullptr);
+  EXPECT_EQ(GetChannel(config, "/invalid_name", ".aos.bar", "app1"), nullptr);
 
   // Tests that a root map/rename works. And that they get processed from the
   // bottom up.
   EXPECT_EQ(
-      FlatbufferToJson(GetLocation(config, "/batman", ".aos.bar", "app1")),
+      FlatbufferToJson(GetChannel(config, "/batman", ".aos.bar", "app1")),
       kExpectedLocation);
 
   // And then test that an application specific map/rename works.
-  EXPECT_EQ(FlatbufferToJson(GetLocation(config, "/bar", ".aos.bar", "app1")),
+  EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/bar", ".aos.bar", "app1")),
             kExpectedLocation);
-  EXPECT_EQ(FlatbufferToJson(GetLocation(config, "/baz", ".aos.bar", "app2")),
+  EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/baz", ".aos.bar", "app2")),
             kExpectedLocation);
 
   // And then test that an invalid application name gets properly ignored.
-  EXPECT_EQ(FlatbufferToJson(GetLocation(config, "/foo", ".aos.bar", "app3")),
+  EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/foo", ".aos.bar", "app3")),
             kExpectedLocation);
 }
 
diff --git a/aos/flatbuffer_merge.h b/aos/flatbuffer_merge.h
index 16372ed..afb4f69 100644
--- a/aos/flatbuffer_merge.h
+++ b/aos/flatbuffer_merge.h
@@ -43,15 +43,18 @@
 }
 
 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()));
+inline aos::FlatbufferDetachedBuffer<T> MergeFlatBuffers(
+    const aos::Flatbuffer<T> &fb1, const aos::Flatbuffer<T> &fb2) {
+const uint8_t *data1 = fb1.data();
+const uint8_t *data2 = fb2.data();
+  return aos::FlatbufferDetachedBuffer<T>(
+      MergeFlatBuffers(T::MiniReflectTypeTable(), data1, data2));
 }
 
 template <class T>
-inline aos::Flatbuffer<T> MergeFlatBuffers(const T *fb1, const T *fb2) {
-  return aos::Flatbuffer<T>(MergeFlatBuffers(
+inline aos::FlatbufferDetachedBuffer<T> MergeFlatBuffers(const T *fb1,
+                                                         const T *fb2) {
+  return aos::FlatbufferDetachedBuffer<T>(MergeFlatBuffers(
       T::MiniReflectTypeTable(), reinterpret_cast<const uint8_t *>(fb1),
       reinterpret_cast<const uint8_t *>(fb2)));
 }
@@ -64,11 +67,11 @@
 }
 
 template <class T>
-inline Flatbuffer<T> CopyFlatBuffer(const T *t) {
+inline FlatbufferDetachedBuffer<T> CopyFlatBuffer(const T *t) {
   flatbuffers::FlatBufferBuilder fbb;
   fbb.ForceDefaults(1);
   fbb.Finish(CopyFlatBuffer<T>(t, &fbb));
-  return Flatbuffer<T>(fbb.Release());
+  return FlatbufferDetachedBuffer<T>(fbb.Release());
 }
 
 }  // namespace aos
diff --git a/aos/flatbuffers.cc b/aos/flatbuffers.cc
new file mode 100644
index 0000000..3fe22c0
--- /dev/null
+++ b/aos/flatbuffers.cc
@@ -0,0 +1,22 @@
+#include "aos/flatbuffers.h"
+
+#include "glog/logging.h"
+
+namespace aos {
+
+uint8_t *FixedAllocatorBase::allocate(size_t) {
+  if (is_allocated_) {
+    LOG(FATAL) << "Tried to allocate already allocated flatbuffer";
+  }
+
+  is_allocated_ = true;
+  return data();
+}
+
+uint8_t *FixedAllocatorBase::reallocate_downward(uint8_t *, size_t, size_t,
+                                                 size_t, size_t) {
+  LOG(FATAL) << "Tried to reallocate a flatbuffer";
+  return nullptr;
+}
+
+}  // namespace aos
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index 6f6fc11..448f34b 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -5,8 +5,99 @@
 
 namespace aos {
 
-// TODO(austin): FlatbufferBase ?  We can associate the type table with it, and
-// make it generic.
+// This class is a base class for all sizes of array backed allocators.
+class FixedAllocatorBase : public flatbuffers::Allocator {
+ public:
+  // TODO(austin): Read the contract for these.
+  uint8_t *allocate(size_t) override;
+
+  void deallocate(uint8_t *, size_t) override { is_allocated_ = false; }
+
+  uint8_t *reallocate_downward(uint8_t *, size_t, size_t, size_t,
+                               size_t) override;
+
+  virtual const uint8_t *data() const = 0;
+  virtual uint8_t *data() = 0;
+  virtual size_t size() const = 0;
+
+ private:
+  bool is_allocated_ = false;
+};
+
+// This class is a fixed memory allocator which holds the data for a flatbuffer
+// in an array.
+template <size_t S>
+class FixedAllocator : public FixedAllocatorBase {
+ public:
+  uint8_t *data() override { return &buffer_[0]; }
+  const uint8_t *data() const override { return &buffer_[0]; }
+  size_t size() const override { return buffer_.size(); }
+
+ private:
+  std::array<uint8_t, S> buffer_;
+};
+
+// Base class representing an object which holds the memory representing a root
+// flatbuffer.
+template <typename T>
+class Flatbuffer {
+ public:
+  // 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>(reinterpret_cast<const void *>(data()));
+  }
+  // Returns a mutable message.  It can be mutated via the flatbuffer rules.
+  T *mutable_message() {
+    return flatbuffers::GetMutableRoot<T>(reinterpret_cast<void *>(data()));
+  }
+
+  virtual const uint8_t *data() const = 0;
+  virtual uint8_t *data() = 0;
+  virtual size_t size() const = 0;
+};
+
+// Array backed flatbuffer.
+template <typename T>
+class FlatbufferArray : public Flatbuffer<T> {
+ public:
+  // Builds a Flatbuffer by copying the data from the other flatbuffer.
+  FlatbufferArray(const Flatbuffer<T> &other) {
+    CHECK_LE(other.size(), data_.size());
+
+    memcpy(data_.data(), other.data(), other.size());
+    size_ = other.size();
+  }
+
+  // Coppies the data from the other flatbuffer.
+  FlatbufferArray &operator=(const Flatbuffer<T> &other) {
+    CHECK_LE(other.size(), data_.size());
+
+    memcpy(data_.data(), other.data(), other.size());
+    size_ = other.size();
+    return *this;
+  }
+
+  // Creates a builder wrapping the underlying data.
+  flatbuffers::FlatBufferBuilder FlatBufferBuilder() {
+    data_.deallocate(data_.data(), data_.size());
+    flatbuffers::FlatBufferBuilder fbb(data_.size(), &data_);
+    fbb.ForceDefaults(1);
+    return fbb;
+  }
+
+  const uint8_t *data() const override { return data_.data(); }
+  uint8_t *data() override { return data_.data(); }
+  size_t size() const override { return size_; }
+
+ private:
+  FixedAllocator<8 * 1024> data_;
+  size_t size_ = data_.size();
+};
 
 // This object associates the message type with the memory storing the
 // flatbuffer.  This only stores root tables.
@@ -14,50 +105,40 @@
 // From a usage point of view, pointers to the data are very different than
 // pointers to the tables.
 template <typename T>
-class Flatbuffer {
+class FlatbufferDetachedBuffer : public Flatbuffer<T> {
  public:
   // Builds a Flatbuffer by taking ownership of the buffer.
-  Flatbuffer(flatbuffers::DetachedBuffer &&buffer)
+  FlatbufferDetachedBuffer(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) {
+  FlatbufferDetachedBuffer(FlatbufferDetachedBuffer &&fb)
+      : buffer_(::std::move(fb.buffer_)) {}
+  FlatbufferDetachedBuffer &operator=(FlatbufferDetachedBuffer &&fb) {
     ::std::swap(buffer_, fb.buffer_);
     return *this;
   }
 
   // Constructs an empty flatbuffer of type T.
-  static Flatbuffer<T> Empty() {
+  static FlatbufferDetachedBuffer<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());
+    return FlatbufferDetachedBuffer<T>(fbb.Release());
   }
 
   // Returns references to the buffer, and the data.
   const flatbuffers::DetachedBuffer &buffer() const { return buffer_; }
-  const uint8_t *data() const { return buffer_.data(); }
+  const uint8_t *data() const override { return buffer_.data(); }
+  uint8_t *data() override { return buffer_.data(); }
+  size_t size() const override { return buffer_.size(); }
 
  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.
 
diff --git a/aos/testdata/config1.json b/aos/testdata/config1.json
index 9f5fcb6..6bbf1a4 100644
--- a/aos/testdata/config1.json
+++ b/aos/testdata/config1.json
@@ -1,5 +1,5 @@
 {
-  "locations": [
+  "channels": [
     {
       "name": "/foo",
       "type": ".aos.bar",
diff --git a/aos/testdata/config1_bad.json b/aos/testdata/config1_bad.json
index 738d46e..ff53a14 100644
--- a/aos/testdata/config1_bad.json
+++ b/aos/testdata/config1_bad.json
@@ -1,5 +1,5 @@
 {
-  "locations": [
+  "channels": [
     {
       "name": "/foo",
       "type": ".aos.bar",
diff --git a/aos/testdata/config2.json b/aos/testdata/config2.json
index 0e6e451..2b8c67e 100644
--- a/aos/testdata/config2.json
+++ b/aos/testdata/config2.json
@@ -1,5 +1,5 @@
 {
-  "locations": [
+  "channels": [
     {
       "name": "/foo",
       "type": ".aos.bar",
diff --git a/aos/testdata/config3.json b/aos/testdata/config3.json
index bb6c580..b29cc77 100644
--- a/aos/testdata/config3.json
+++ b/aos/testdata/config3.json
@@ -1,5 +1,5 @@
 {
-  "locations": [
+  "channels": [
     {
       "name": "/foo3",
       "type": ".aos.bar"
diff --git a/aos/testdata/expected.json b/aos/testdata/expected.json
index 62c0bad..4227c91 100644
--- a/aos/testdata/expected.json
+++ b/aos/testdata/expected.json
@@ -1,5 +1,5 @@
 {
- "locations": [
+ "channels": [
   {
    "name": "/foo",
    "type": ".aos.bar",