Reduce memory usage of the static flatbuffer API

This contains a couple of changes, which work together and are a bit
hard to separate out.

1) Instead of requiring the whole contents of a sub-message or vector to
   be aligned, split the alignment requirement up into an alignment
   requirement at an offset into the message.  This lets us leave the
   length field in a message, for example, at 4 byte alignment when the
   body requires 8 byte alignment.  This enables better packing of
   fields.
2) From James, don't pre-reserve space for vectors with 0 length.  They
   will trigger a re-allocation anyways when they are used since there
   is no space allocated, so pre-allocating doesn't help.
3) Remove padding at the end of messages and require the allocator to
   handle it instead.  We used to allocate kSize + kAlign and then
   manually align things, which resulted in wasted space.
4) Automatically add any extra padding after a vector to the vector.
   For some small vectors, this lets us use the padding for the vector
   rather than allocating more space.
5) Shrink the code generated for the object offsets by adding constexpr
   variables with the previous object size rather than inlining it.
   This results in a much faster build since clang-format was fighting
   the large fields at build time.
6) Sort fields in a flatbuffer by alignment to pack them better.

Change-Id: I5af440855e3425be31fa7f30c68af552fcf06cb2
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/flatbuffers/base.cc b/aos/flatbuffers/base.cc
index f22fce9..afd48a2 100644
--- a/aos/flatbuffers/base.cc
+++ b/aos/flatbuffers/base.cc
@@ -37,20 +37,17 @@
   }
 }
 
-bool ResizeableObject::InsertBytes(void *insertion_point, size_t bytes,
-                                   SetZero set_zero) {
+std::optional<std::span<uint8_t>> ResizeableObject::InsertBytes(
+    void *insertion_point, size_t bytes, SetZero set_zero) {
   // See comments on InsertBytes() declaration and in FixObjects()
   // implementation below.
-  CHECK_LT(buffer_.data(), reinterpret_cast<const uint8_t *>(insertion_point))
+  CHECK_LT(reinterpret_cast<const void *>(buffer_.data()),
+           reinterpret_cast<const void *>(insertion_point))
       << ": Insertion may not be prior to the start of the buffer.";
-  // Check that we started off with a properly aligned size.
-  // Doing this CHECK earlier is tricky because if done in the constructor then
-  // it executes prior to the Alignment() implementation being available.
-  CHECK_EQ(0u, buffer_.size() % Alignment());
   // Note that we will round up the size to the current alignment, so that we
   // ultimately end up only adjusting the buffer size by a multiple of its
   // alignment, to avoid having to do any more complicated bookkeeping.
-  const size_t aligned_bytes = PaddedSize(bytes, Alignment());
+  const size_t aligned_bytes = AlignOffset(bytes, Alignment());
   if (parent_ != nullptr) {
     return parent_->InsertBytes(insertion_point, aligned_bytes, set_zero);
   } else {
@@ -59,14 +56,16 @@
             ->InsertBytes(insertion_point, aligned_bytes, Alignment(),
                           set_zero);
     if (!new_buffer.has_value()) {
-      return false;
+      return std::nullopt;
     }
-    UpdateBuffer(new_buffer.value(),
-                 new_buffer.value().data() +
-                     (reinterpret_cast<const uint8_t *>(insertion_point) -
-                      buffer_.data()),
-                 aligned_bytes);
-    return true;
+    std::span<uint8_t> inserted_data(
+        new_buffer.value().data() +
+            (reinterpret_cast<const uint8_t *>(insertion_point) -
+             buffer_.data()),
+        aligned_bytes);
+    UpdateBuffer(new_buffer.value(), inserted_data.data(),
+                 inserted_data.size());
+    return inserted_data;
   }
 }
 
@@ -78,16 +77,9 @@
   ObserveBufferModification();
 }
 
-std::span<uint8_t> ResizeableObject::BufferForObject(
-    size_t absolute_offset, size_t size, size_t terminal_alignment) {
-  const size_t padded_size = PaddedSize(size, terminal_alignment);
-  std::span<uint8_t> padded_buffer =
-      internal::GetSubSpan(buffer_, absolute_offset, padded_size);
-  std::span<uint8_t> object_buffer =
-      internal::GetSubSpan(padded_buffer, 0, size);
-  std::span<uint8_t> padding = internal::GetSubSpan(padded_buffer, size);
-  internal::ClearSpan(padding);
-  return object_buffer;
+std::span<uint8_t> ResizeableObject::BufferForObject(size_t absolute_offset,
+                                                     size_t size) {
+  return internal::GetSubSpan(buffer_, absolute_offset, size);
 }
 
 void ResizeableObject::FixObjects(void *modification_point,
@@ -103,8 +95,7 @@
         object.inline_entry < modification_point) {
       if (*object.inline_entry != 0) {
         CHECK_EQ(static_cast<const void *>(
-                     static_cast<const uint8_t *>(absolute_offset) +
-                     CHECK_NOTNULL(object.object)->AbsoluteOffsetOffset()),
+                     static_cast<const uint8_t *>(absolute_offset)),
                  DereferenceOffset(object.inline_entry));
         *object.inline_entry += bytes_inserted;
         CHECK_GE(DereferenceOffset(object.inline_entry), modification_point)
@@ -119,8 +110,7 @@
     // We only need to update the object's buffer if it currently exists.
     if (object.object != nullptr) {
       std::span<uint8_t> subbuffer = BufferForObject(
-          *object.absolute_offset, object.object->buffer_.size(),
-          object.object->Alignment());
+          *object.absolute_offset, object.object->buffer_.size());
       // By convention (enforced in InsertBytes()), the modification_point shall
       // not be at the start of the subobjects data buffer; it may be the byte
       // just past the end of the buffer. This makes it so that is unambiguous
diff --git a/aos/flatbuffers/base.h b/aos/flatbuffers/base.h
index a98d730..045fce3 100644
--- a/aos/flatbuffers/base.h
+++ b/aos/flatbuffers/base.h
@@ -26,13 +26,17 @@
 using ::flatbuffers::uoffset_t;
 using ::flatbuffers::voffset_t;
 
-// Returns the smallest multiple of alignment that is greater than or equal to
-// size.
-constexpr size_t PaddedSize(size_t size, size_t alignment) {
+// Returns the offset into the buffer needed to provide 'alignment' alignment
+// 'aligned_offset' bytes after the returned offset.  This assumes that the
+// first 'starting_offset' bytes are spoken for.
+constexpr size_t AlignOffset(size_t starting_offset, size_t alignment,
+                             size_t aligned_offset = 0) {
   // We can be clever with bitwise operations by assuming that aligment is a
   // power of two. Or we can just be clearer about what we mean and eat a few
   // integer divides.
-  return (((size - 1) / alignment) + 1) * alignment;
+  return (((starting_offset + aligned_offset - 1) / alignment) + 1) *
+             alignment -
+         aligned_offset;
 }
 
 // Used as a parameter to methods where we are messing with memory and may or
@@ -116,11 +120,6 @@
   ResizeableObject(ResizeableObject &&other);
   // Required alignment of this object.
   virtual size_t Alignment() const = 0;
-  // Offset from the start of buffer() to the actual start of the object in
-  // question (this is important for vectors, where the vector itself cannot
-  // have internal padding, and so the start of the vector may be offset from
-  // the start of the buffer to handle alignment).
-  virtual size_t AbsoluteOffsetOffset() const = 0;
   // Causes bytes bytes to be inserted between insertion_point - 1 and
   // insertion_point.
   // If requested, the new bytes will be cleared to zero; otherwise they will be
@@ -130,9 +129,10 @@
   // implementation, and is merely a requirement that any buffer growth occur
   // only on the inside or past the end of the vector, and not prior to the
   // start of the vector.
-  // Returns true on success, false on failure (e.g., if the allocator has no
-  // memory available).
-  bool InsertBytes(void *insertion_point, size_t bytes, SetZero set_zero);
+  // Returns a span of the inserted bytes on success, nullopt on failure (e.g.,
+  // if the allocator has no memory available).
+  std::optional<std::span<uint8_t>> InsertBytes(void *insertion_point,
+                                                size_t bytes, SetZero set_zero);
   // Called *after* the internal buffer_ has been swapped out and *after* the
   // object tree has been traversed and fixed.
   virtual void ObserveBufferModification() {}
@@ -151,12 +151,9 @@
   const void *PointerForAbsoluteOffset(const size_t absolute_offset) {
     return buffer_.data() + absolute_offset;
   }
-  // Returns a span at the requested offset into the buffer. terminal_alignment
-  // does not align the start of the buffer; instead, it ensures that the memory
-  // from absolute_offset + size until the next multiple of terminal_alignment
-  // is set to all zeroes.
-  std::span<uint8_t> BufferForObject(size_t absolute_offset, size_t size,
-                                     size_t terminal_alignment);
+  // Returns a span at the requested offset into the buffer for the requested
+  // size.
+  std::span<uint8_t> BufferForObject(size_t absolute_offset, size_t size);
   // When memory has been inserted/removed, this iterates over the sub-objects
   // and notifies/adjusts them appropriately.
   // This will be called after buffer_ has been updated, and:
diff --git a/aos/flatbuffers/base_test.cc b/aos/flatbuffers/base_test.cc
index 43c9c5b..b77f352 100644
--- a/aos/flatbuffers/base_test.cc
+++ b/aos/flatbuffers/base_test.cc
@@ -7,13 +7,27 @@
 #include "gtest/gtest.h"
 
 namespace aos::fbs::testing {
-// Tests that PaddedSize() behaves as expected.
-TEST(BaseTest, PaddedSize) {
-  EXPECT_EQ(0, PaddedSize(0, 4));
-  EXPECT_EQ(4, PaddedSize(4, 4));
-  EXPECT_EQ(8, PaddedSize(5, 4));
-  EXPECT_EQ(8, PaddedSize(6, 4));
-  EXPECT_EQ(8, PaddedSize(7, 4));
+// Tests that AlignOffset() behaves as expected.
+TEST(BaseTest, AlignOffset) {
+  EXPECT_EQ(0, AlignOffset(0, 4));
+  EXPECT_EQ(4, AlignOffset(4, 4));
+  EXPECT_EQ(8, AlignOffset(5, 4));
+  EXPECT_EQ(8, AlignOffset(6, 4));
+  EXPECT_EQ(8, AlignOffset(7, 4));
+}
+
+// Tests that AlignOffset handles the alignment point being nonzero.  This shows
+// up when you want 8 byte alignment 4 bytes into the start of the buffer, and
+// don't want to pad out the front of the buffer.
+TEST(BaseTest, AlignOffsetWithOffset) {
+  EXPECT_EQ(4, AlignOffset(4, 4, 4));
+
+  EXPECT_EQ(4, AlignOffset(0, 8, 4));
+  EXPECT_EQ(4, AlignOffset(1, 8, 4));
+  EXPECT_EQ(4, AlignOffset(2, 8, 4));
+  EXPECT_EQ(4, AlignOffset(3, 8, 4));
+  EXPECT_EQ(4, AlignOffset(4, 8, 4));
+  EXPECT_EQ(12, AlignOffset(5, 8, 4));
 }
 
 inline constexpr size_t kDefaultSize = AlignedVectorAllocator::kAlignment * 2;
@@ -170,7 +184,8 @@
   virtual ~TestResizeableObject() {}
   using ResizeableObject::SubObject;
   bool InsertBytes(void *insertion_point, size_t bytes) {
-    return ResizeableObject::InsertBytes(insertion_point, bytes, SetZero::kYes);
+    return ResizeableObject::InsertBytes(insertion_point, bytes, SetZero::kYes)
+        .has_value();
   }
   TestResizeableObject(TestResizeableObject &&) = default;
 
@@ -204,7 +219,6 @@
   TestObject &GetObject(size_t index) { return objects_.at(index); }
 
   size_t Alignment() const override { return 64; }
-  size_t AbsoluteOffsetOffset() const override { return 0; }
 
  private:
   std::vector<TestObject> objects_;
diff --git a/aos/flatbuffers/builder.h b/aos/flatbuffers/builder.h
index b4aefbe..be41b63 100644
--- a/aos/flatbuffers/builder.h
+++ b/aos/flatbuffers/builder.h
@@ -94,7 +94,6 @@
 
  private:
   size_t Alignment() const override { return flatbuffer_.t.Alignment(); }
-  size_t AbsoluteOffsetOffset() const override { return 0; }
   size_t NumberOfSubObjects() const override { return 1; }
   void SetPrefix() {
     // We can't do much if the provided buffer isn't at least 4-byte aligned,
@@ -103,13 +102,16 @@
     CHECK_EQ(reinterpret_cast<size_t>(buffer_.data()) % alignof(uoffset_t), 0u);
     *reinterpret_cast<uoffset_t *>(buffer_.data()) = flatbuffer_start_;
   }
-  // Because the allocator API doesn't provide a way for us to request a
-  // strictly aligned buffer, manually align the start of the actual flatbuffer
-  // data if needed.
+  // Manually aligns the start of the actual flatbuffer to handle the alignment
+  // offset.
   static size_t BufferStart(std::span<uint8_t> buffer) {
-    return aos::fbs::PaddedSize(
+    CHECK_EQ(reinterpret_cast<size_t>(buffer.data()) % T::kAlign, 0u)
+        << "Failed to allocate data of length " << buffer.size()
+        << " with alignment " << T::kAlign;
+
+    return aos::fbs::AlignOffset(
                reinterpret_cast<size_t>(buffer.data()) + sizeof(uoffset_t),
-               T::kAlign) -
+               T::kAlign, T::kAlignOffset) -
            reinterpret_cast<size_t>(buffer.data());
   }
 
diff --git a/aos/flatbuffers/static_flatbuffers.cc b/aos/flatbuffers/static_flatbuffers.cc
index c2e0454..4c013a2 100644
--- a/aos/flatbuffers/static_flatbuffers.cc
+++ b/aos/flatbuffers/static_flatbuffers.cc
@@ -15,6 +15,7 @@
 #include "absl/strings/str_format.h"
 #include "absl/strings/str_join.h"
 #include "absl/strings/str_replace.h"
+#include "absl/strings/substitute.h"
 #include "flatbuffers/base.h"
 #include "flatbuffers/string.h"
 #include "flatbuffers/vector.h"
@@ -32,6 +33,8 @@
   std::string name;
   // Whether it is an inline data type (scalar/struct vs vector/table/string).
   bool is_inline = true;
+  // Whether the elements are inline (vector of ints vs vector of strings).
+  bool elements_are_inline = true;
   // Whether this is a struct or not.
   bool is_struct = false;
   // Whether this is a repeated type (vector or string).
@@ -48,6 +51,8 @@
   size_t inline_alignment = 0u;
   // vtable offset of the field.
   size_t vtable_offset = 0u;
+  // Size of the elements in the vector, if this is a vector.
+  size_t element_size = 0u;
 };
 
 const reflection::Object *GetObject(const reflection::Schema *schema,
@@ -168,6 +173,7 @@
   const reflection::Type *type = field_fbs->type();
   field->inline_size = type->base_size();
   field->inline_alignment = type->base_size();
+  field->element_size = type->element_size();
   switch (type->base_type()) {
     case reflection::BaseType::Bool:
     case reflection::BaseType::Byte:
@@ -183,6 +189,7 @@
       // We have a scalar field, so things are relatively
       // straightforwards.
       field->is_inline = true;
+      field->elements_are_inline = true;
       field->is_struct = false;
       field->is_repeated = false;
       field->full_type =
@@ -190,6 +197,7 @@
       return;
     case reflection::BaseType::String: {
       field->is_inline = false;
+      field->elements_are_inline = true;
       field->is_struct = false;
       field->is_repeated = true;
       field->full_type =
@@ -231,6 +239,7 @@
         };
       }
       field->is_inline = false;
+      field->elements_are_inline = elements_are_inline;
       field->is_struct = false;
       field->full_type =
           absl::StrFormat("::aos::fbs::Vector<%s, %d, %s, %s>", element_type,
@@ -242,6 +251,7 @@
     case reflection::BaseType::Obj: {
       const reflection::Object *object = GetObject(schema, type->index());
       field->is_inline = object->is_struct();
+      field->elements_are_inline = field->is_inline;
       field->is_struct = object->is_struct();
       field->is_repeated = false;
       const std::string flatbuffer_name =
@@ -281,7 +291,7 @@
   const std::string constructor_body =
       R"code(
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
 )code";
   return absl::StrFormat(R"code(
@@ -351,10 +361,14 @@
 // table (scalars, structs, and enums) .
 std::string MakeInlineAccessors(const FieldData &field,
                                 const size_t inline_absolute_offset) {
-  CHECK_EQ(inline_absolute_offset % field.inline_alignment, 0u)
+  constexpr size_t kVtablePointerSize = sizeof(soffset_t);
+  CHECK_EQ(
+      (inline_absolute_offset - kVtablePointerSize) % field.inline_alignment,
+      0u)
       << ": Unaligned field " << field.name << " on " << field.full_type
       << " with inline offset of " << inline_absolute_offset
-      << " and alignment of " << field.inline_alignment;
+      << " and alignment of " << field.inline_alignment
+      << " and an alignment offset of " << kVtablePointerSize;
   const std::string setter =
       absl::StrFormat(R"code(
   // Sets the %s field, causing it to be populated if it is not already.
@@ -369,7 +383,7 @@
       R"code(
   // Returns the value of %s if set; nullopt otherwise.
   std::optional<%s> %s() const {
-    return has_%s() ? std::make_optional(Get<%s>(%s)) : std::nullopt;;
+    return has_%s() ? std::make_optional(Get<%s>(%s)) : std::nullopt;
   }
   // Returns a pointer to modify the %s field.
   // The pointer may be invalidated by mutations/movements of the underlying buffer.
@@ -388,39 +402,82 @@
 // Generates the accessors for fields which are not inline fields and have an
 // offset to the actual field content stored inline in the flatbuffer table.
 std::string MakeOffsetDataAccessors(const FieldData &field) {
-  const std::string setter = absl::StrFormat(
+  const std::string setter = absl::Substitute(
       R"code(
-  // Creates an empty object for the %s field, which you can
+  // Creates an empty object for the $0 field, which you can
   // then populate/modify as desired.
   // The field must not be populated yet.
-  %s* add_%s() {
-    CHECK(!%s.has_value());
-    constexpr size_t kVtableIndex = %d;
-    // Construct the *Static object that we will use for managing this subtable.
-    %s.emplace(BufferForObject(%s, %s::kSize, kAlign), this);
+  $1* add_$0() {
+    CHECK(!$2.has_value());
+    constexpr size_t kVtableIndex = $3;
+    // If this object does not normally have its initial memory statically allocated,
+    // allocate it now (this is used for zero-length vectors).
+    if constexpr ($1::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset = $4;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset, $1::kSize, ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for any other objects
+      // that may have been "sharing" this location. The effect of this logic
+      // is that the first object that gets populated at any given location will
+      // bump all other objects to later. This is fine, although it does mean
+      // that the order in which objects appear in memory may vary depending
+      // on the order in which they are constructed (if they start out sharing a start pointer).
+      $4 = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this subtable.
+      $5.emplace(BufferForObject($4, $1::$7), this);
+    } else {
+      // Construct the *Static object that we will use for managing this subtable.
+      $5.emplace(BufferForObject($4, $1::kSize), this);
+    }
     // Actually set the appropriate fields in the flatbuffer memory itself.
-    SetField<::flatbuffers::uoffset_t>(%s, kVtableIndex, %s + %s::kOffset - %s);
-    return &%s.value().t;
+    SetField<::flatbuffers::uoffset_t>($6, kVtableIndex, $4 - $6);
+    return &$5.value().t;
   }
   )code",
-      field.name, field.full_type, field.name, MemberName(field),
-      field.vtable_offset, MemberName(field), ObjectAbsoluteOffsetName(field),
-      field.full_type, InlineAbsoluteOffsetName(field),
-      ObjectAbsoluteOffsetName(field), field.full_type,
-      InlineAbsoluteOffsetName(field), MemberName(field));
-  const std::string getters = absl::StrFormat(
+      field.name,                       // $0
+      field.full_type,                  // $1
+      MemberName(field),                // $2
+      field.vtable_offset,              // $3
+      ObjectAbsoluteOffsetName(field),  // $4
+      MemberName(field),                // $5
+      InlineAbsoluteOffsetName(field),  // $6
+      (field.elements_are_inline
+           // When building vectors of inline elements, we want this object to
+           // consume as much of the memory that was allocated as possible. This
+           // lets the vector use the padding for storage, saving space.  Round
+           // this down to the size of memory that the max number of elements
+           // fit in perfectly so the padding after that isn't owned.
+           ? "RoundedLength(inserted_bytes.value().size())"
+           // For vectors of elements, we need to pad out the inline portion of
+           // the vector storing offsets to the alignment of the actual elements
+           // so we can insert elements at the end without having to allocate
+           // padding.  This saves space in the long run, and lets us consume
+           // the padding for offsets if needed.
+           : "kSize")  // $7
+  );
+  const std::string getters = absl::Substitute(
       R"code(
-  // Returns a pointer to the %s field, if set. nullptr otherwise.
-  const %s* %s() const {
-    return %s.has_value() ? &%s.value().t : nullptr;
+  // Returns a pointer to the $0 field, if set. nullptr otherwise.
+  const $1* $0() const {
+    return $2.has_value() ? &$2.value().t : nullptr;
   }
-  %s* mutable_%s() {
-    return %s.has_value() ? &%s.value().t : nullptr;
+  $1* mutable_$0() {
+    return $2.has_value() ? &$2.value().t : nullptr;
   }
   )code",
-      field.name, field.full_type, field.name, MemberName(field),
-      MemberName(field), field.full_type, field.name, MemberName(field),
-      MemberName(field));
+      field.name,        // $0
+      field.full_type,   // $1
+      MemberName(field)  // $2
+  );
   return setter + getters + MakeClearer(field) + MakeHaser(field);
 }
 
@@ -452,14 +509,15 @@
     // Offset from the start of the buffer to the start of the actual
     // data for this field. Will be updated even when the table is not
     // populated, so that we know where to construct it when requested.
-    size_t %s = %s;
+    static constexpr size_t kDefaultObjectAbsoluteOffset%s = %s;
+    size_t %s = kDefaultObjectAbsoluteOffset%s;
     // Offset from the start of the buffer to the offset in the inline data for
     // this field.
     static constexpr size_t %s = %d;
     )code",
-        field.name, field.full_type, MemberName(field),
-        ObjectAbsoluteOffsetName(field), offset_data_absolute_offset,
-        InlineAbsoluteOffsetName(field), inline_absolute_offset);
+        field.name, field.full_type, MemberName(field), field.name,
+        offset_data_absolute_offset, ObjectAbsoluteOffsetName(field),
+        field.name, InlineAbsoluteOffsetName(field), inline_absolute_offset);
   }
 }
 
@@ -651,9 +709,10 @@
 }
 
 std::string AlignCppString(const std::string_view expression,
-                           const std::string_view alignment) {
-  return absl::StrFormat("::aos::fbs::PaddedSize(%s, %s)", expression,
-                         alignment);
+                           const std::string_view alignment,
+                           const std::string_view offset) {
+  return absl::StrCat("::aos::fbs::AlignOffset(", expression, ", ", alignment,
+                      ", ", offset, ")");
 }
 
 std::string MakeInclude(std::string_view path, bool system = false) {
@@ -679,11 +738,19 @@
     PopulateTypeData(schema, field_fbs, &field);
     fields.push_back(field);
   }
+  std::sort(fields.begin(), fields.end(),
+            [](const FieldData &f1, const FieldData &f2) {
+              return std::make_tuple(f1.inline_alignment, f1.element_size,
+                                     f1.vtable_offset) >
+                     std::make_tuple(f2.inline_alignment, f2.element_size,
+                                     f2.vtable_offset);
+            });
   const size_t nominal_min_align = object->minalign();
   std::string out_of_line_member_size = "";
   // inline_absolute_offset tracks the current position of the inline table
   // contents so that we can assign static offsets to each field.
-  size_t inline_absolute_offset = sizeof(soffset_t);
+  constexpr size_t kVtablePointerSize = sizeof(soffset_t);
+  size_t inline_absolute_offset = kVtablePointerSize;
   // offset_data_relative_offset tracks the current size of the various
   // sub-tables/vectors/strings that get stored at the end of the buffer.
   // For simplicity, the offset data will start at a fully aligned offset
@@ -691,11 +758,11 @@
   // Note that this is a string because it's irritating to actually pipe the
   // numbers for size/alignment up here, so we just accumulate them here and
   // then write the expression directly into the C++.
-  std::string offset_data_relative_offset = "0";
   const std::string offset_data_start_expression =
-      "::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign)";
-  std::string accessors;
-  std::string members;
+      "(kVtableStart + kVtableSize)";
+  std::string offset_data_relative_offset = offset_data_start_expression;
+  std::vector<std::string> accessors;
+  std::vector<std::string> members;
   std::set<std::string> includes = {
       MakeInclude("optional", true),
       MakeInclude("aos/flatbuffers/static_table.h"),
@@ -712,28 +779,32 @@
   std::vector<std::string> alignments;
   std::set<std::string> subobject_names;
   for (const FieldData &field : fields) {
-    inline_absolute_offset =
-        PaddedSize(inline_absolute_offset, field.inline_alignment);
+    inline_absolute_offset = AlignOffset(
+        inline_absolute_offset, field.inline_alignment, kVtablePointerSize);
     if (!field.is_inline) {
-      // All sub-fields will get aligned to the parent alignment. This makes
-      // some book-keeping a bit easier, at the expense of some gratuitous
-      // padding.
-      offset_data_relative_offset =
-          AlignCppString(offset_data_relative_offset, "kAlign");
       alignments.push_back(field.full_type + "::kAlign");
+      // We care about aligning each field relative to the alignment point in
+      // this flatbuffer (which is kAlignOffset into the block of memory).  We
+      // then need to report out the offset relative to the start, not the
+      // alignment point.
+      offset_data_relative_offset =
+          AlignCppString(offset_data_relative_offset + " - kAlignOffset",
+                         alignments.back(),
+                         field.full_type + "::kAlignOffset") +
+          " + kAlignOffset";
     } else {
       alignments.push_back(std::to_string(field.inline_alignment));
     }
-    const std::string offset_data_absolute_offset =
-        offset_data_start_expression + " + " + offset_data_relative_offset;
-    accessors += MakeAccessors(field, inline_absolute_offset);
-    members +=
-        MakeMembers(field, offset_data_absolute_offset, inline_absolute_offset);
+    const std::string offset_data_absolute_offset = offset_data_relative_offset;
+    accessors.emplace_back(MakeAccessors(field, inline_absolute_offset));
+    members.emplace_back(MakeMembers(field, offset_data_absolute_offset,
+                                     inline_absolute_offset));
 
     inline_absolute_offset += field.inline_size;
     if (!field.is_inline) {
-      offset_data_relative_offset +=
-          absl::StrFormat(" + %s::kSize", field.full_type);
+      offset_data_relative_offset = absl::StrFormat(
+          "kDefaultObjectAbsoluteOffset%s + %s::kPreallocatedSize", field.name,
+          field.full_type);
     }
     if (field.fbs_type.has_value()) {
       // Is this not getting populate for the root schema?
@@ -744,39 +815,37 @@
   const std::string alignment = absl::StrCat(
       "static constexpr size_t kAlign = std::max<size_t>({kMinAlign, ",
       absl::StrJoin(alignments, ", "), "});\n");
-  const std::string size =
-      absl::StrCat("static constexpr size_t kSize = ",
-                   AlignCppString(offset_data_start_expression + " + " +
-                                      offset_data_relative_offset,
-                                  "kAlign"),
-                   ";");
+  // Same here, we want to align the end relative to the alignment point, but
+  // then we want to report out the size including the offset.
+  const std::string size = absl::StrCat(
+      "static constexpr size_t kSize = ",
+      AlignCppString(offset_data_relative_offset + " - kAlignOffset", "kAlign",
+                     "kAlignOffset"),
+      " + kAlignOffset;");
   const size_t inline_data_size = inline_absolute_offset;
   const std::string constants = absl::StrFormat(
       R"code(
-  // Space taken up by the inline portion of the flatbuffer table data, in bytes.
+  // Space taken up by the inline portion of the flatbuffer table data, in
+  // bytes.
   static constexpr size_t kInlineDataSize = %d;
   // Space taken up by the vtable for this object, in bytes.
-  static constexpr size_t kVtableSize = sizeof(::flatbuffers::voffset_t) * (2 + %d);
-  // Offset from the start of the internal memory buffer to the start of the vtable.
-  static constexpr size_t kVtableStart = ::aos::fbs::PaddedSize(kInlineDataSize, alignof(::flatbuffers::voffset_t));
-  // Required alignment of this object. The buffer that this object gets constructed
-  // into must be aligned to this value.
+  static constexpr size_t kVtableSize =
+     sizeof(::flatbuffers::voffset_t) * (2 + %d);
+  // Offset from the start of the internal memory buffer to the start of the
+  // vtable.
+  static constexpr size_t kVtableStart = ::aos::fbs::AlignOffset(
+      kInlineDataSize, alignof(::flatbuffers::voffset_t));
+  // Required alignment of this object. The buffer that this object gets
+  // constructed into must be aligned to this value.
   %s
-  // Nominal size of this object, in bytes. The object may grow beyond this size,
-  // but will always start at this size and so the initial buffer must match
-  // this size.
-  %s
-  static_assert(%d <= kAlign, "Flatbuffer schema minalign should not exceed our required alignment.");
-  // Offset from the start of the memory buffer to the start of any out-of-line data (subtables,
-  // vectors, strings).
+  // Offset into this object to measure the alignment at.
+  static constexpr size_t kAlignOffset = sizeof(::flatbuffers::soffset_t);
+  static_assert(
+      %d <= kAlign,
+      "Flatbuffer schema minalign should not exceed our required alignment.");
+  // Offset from the start of the memory buffer to the start of any out-of-line
+  // data (subtables, vectors, strings).
   static constexpr size_t kOffsetDataStart = %s;
-  // Size required for a buffer that includes a root table offset at the start.
-  static constexpr size_t kRootSize = ::aos::fbs::PaddedSize(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
-  // Minimum size required to build this flatbuffer in an entirely unaligned buffer
-  // (including the root table offset). Made to be a multiple of kAlign for convenience.
-  static constexpr size_t kUnalignedBufferSize = kRootSize + kAlign;
-  // Offset at which the table vtable offset occurs. This is only needed for vectors.
-  static constexpr size_t kOffset = 0;
   // Various overrides to support the Table parent class.
   size_t FixedVtableOffset() const final { return kVtableStart; }
   size_t VtableSize() const final { return kVtableSize; }
@@ -785,10 +854,12 @@
   size_t Alignment() const final { return kAlign; }
   // Exposes the name of the flatbuffer type to allow interchangeable use
   // of the Flatbuffer and FlatbufferStatic types in various AOS methods.
-  static const char *GetFullyQualifiedName() { return Flatbuffer::GetFullyQualifiedName(); }
+  static const char *GetFullyQualifiedName() {
+    return Flatbuffer::GetFullyQualifiedName();
+  }
 )code",
-      inline_data_size, object->fields()->size(), alignment, size,
-      nominal_min_align, offset_data_start_expression);
+      inline_data_size, object->fields()->size(), alignment, nominal_min_align,
+      offset_data_start_expression);
   const std::string_view fbs_type_name = object->name()->string_view();
   const std::string type_namespace = FlatbufferNameToCppName(
       fbs_type_name.substr(0, fbs_type_name.find_last_of(".")));
@@ -817,13 +888,25 @@
 %s
 %s
 %s
+ public:
+  // Nominal size of this object, in bytes. The object may grow beyond this
+  // size, but will always start at this size and so the initial buffer must
+  // match this size.
+  %s
+  // Always statically allocate memory for tables (set for consistency with
+  // static_vector.h).
+  static constexpr size_t kPreallocatedSize = kSize;
+  // Size required for a buffer that includes a root table offset at the start.
+  static constexpr size_t kRootSize =
+      ::aos::fbs::AlignOffset(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
 };
 }
   )code",
       type_namespace, type_name, FlatbufferNameToCppName(fbs_type_name),
-      constants, MakeConstructor(type_name), type_name, accessors,
-      MakeFullClearer(fields), MakeCopier(fields), MakeObjectCopier(fields),
-      MakeMoveConstructor(type_name), members, MakeSubObjectList(fields));
+      constants, MakeConstructor(type_name), type_name,
+      absl::StrJoin(accessors, ""), MakeFullClearer(fields), MakeCopier(fields),
+      MakeObjectCopier(fields), MakeMoveConstructor(type_name),
+      absl::StrJoin(members, ""), MakeSubObjectList(fields), size);
 
   GeneratedObject result;
   result.name = fbs_type_name;
diff --git a/aos/flatbuffers/static_flatbuffers_test.cc b/aos/flatbuffers/static_flatbuffers_test.cc
index 0929d17..4a8b9e9 100644
--- a/aos/flatbuffers/static_flatbuffers_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_test.cc
@@ -591,7 +591,12 @@
     {
       auto aligned_vector = object->mutable_vector_aligned();
       ASSERT_TRUE(aligned_vector->reserve(100));
-      EXPECT_EQ(100, aligned_vector->capacity());
+
+      VLOG(1) << AnnotateBinaries(test_schema_, builder.buffer());
+      // Since the allocator is going to allocate in blocks of 64, we end up
+      // with more capacity than we asked for.  Better to have it than to leave
+      // it as unusable padding.
+      EXPECT_EQ(115, aligned_vector->capacity());
       ASSERT_TRUE(builder.AsFlatbufferSpan().Verify())
           << aligned_vector->SerializationDebugString();
       EXPECT_EQ(expected_contents,
@@ -657,11 +662,16 @@
     {
       auto unspecified_vector = object->add_unspecified_length_vector();
       ASSERT_NE(nullptr, unspecified_vector);
-      ASSERT_EQ(0, unspecified_vector->capacity());
+      ASSERT_EQ(60, unspecified_vector->capacity());
+      for (size_t i = 0; i < 60; ++i) {
+        ASSERT_TRUE(unspecified_vector->emplace_back(0));
+      }
       ASSERT_FALSE(unspecified_vector->emplace_back(0));
-      ASSERT_TRUE(unspecified_vector->reserve(2));
-      ASSERT_TRUE(unspecified_vector->emplace_back(1));
-      ASSERT_TRUE(unspecified_vector->emplace_back(2));
+      ASSERT_TRUE(unspecified_vector->reserve(64));
+      ASSERT_EQ(124, unspecified_vector->capacity());
+      for (size_t i = 0; i < 64; ++i) {
+        ASSERT_TRUE(unspecified_vector->emplace_back(1));
+      }
       ASSERT_FALSE(unspecified_vector->emplace_back(3));
       ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
     }
@@ -927,10 +937,14 @@
   {
     auto vector = object->add_unspecified_length_vector();
     // Confirm that the vector does indeed start out at zero length.
+    ASSERT_EQ(vector->capacity(), 60);
+    for (size_t i = 0; i < 60; ++i) {
+      ASSERT_TRUE(vector->emplace_back(i));
+    }
     ASSERT_FALSE(vector->emplace_back(4));
     ASSERT_TRUE(vector->reserve(9000));
     vector->resize(256);
-    for (size_t index = 0; index < 256; ++index) {
+    for (size_t index = 60; index < 256; ++index) {
       vector->at(index) = static_cast<uint8_t>(index);
     }
   }
diff --git a/aos/flatbuffers/static_table.h b/aos/flatbuffers/static_table.h
index e9619a7..a7f96d1 100644
--- a/aos/flatbuffers/static_table.h
+++ b/aos/flatbuffers/static_table.h
@@ -17,7 +17,13 @@
 // Every table will be aligned to the greatest alignment of all of its members
 // and its size will be equal to a multiple of the alignment. Each table shall
 // have the following layout: [vtable offset; inline data with padding; vtable;
-// padding; table/vector data with padding]
+// table/vector data with padding]
+//
+// Within the table/vector data with padding section, there will be a chunk of
+// memory for the data associated with each sub-table/vector of the table.
+// That memory will start with some padding and then the memory actually
+// allocated to the table/vector in question will start at said
+// sub-table/vector's individual alignment.
 class Table : public ResizeableObject {
  public:
   // Prints out a debug string of the raw flatbuffer memory. Does not currently
@@ -57,7 +63,6 @@
   virtual size_t VtableSize() const = 0;
   virtual size_t InlineTableSize() const = 0;
   virtual size_t OffsetDataStart() const = 0;
-  size_t AbsoluteOffsetOffset() const override { return 0; }
   void PopulateVtable() {
     // Zero out everything up to the start of the sub-messages/tables, which are
     // responsible for doing their own memory initialization.
diff --git a/aos/flatbuffers/static_vector.h b/aos/flatbuffers/static_vector.h
index bf706e0..da250e2 100644
--- a/aos/flatbuffers/static_vector.h
+++ b/aos/flatbuffers/static_vector.h
@@ -26,10 +26,11 @@
 //   FlatbufferType: The type used by flatbuffers::Vector to store this type.
 //   ConstFlatbufferType: The type used by a const flatbuffers::Vector to store
 //     this type.
-//   kDataAlign: Alignment required by the stored type.
-//   kDataSize: Nominal size required by each non-inline data member. This is
-//     what will be initially allocated; once created, individual members may
-//     grow to accommodate dynamically lengthed vectors.
+//   kDataElementAlign: Alignment required by the stored type.
+//   kDataElementSize: Nominal size required by each non-inline data member.
+//     This is what will be initially allocated; once created, individual
+//     members may grow to accommodate dynamically lengthed vectors.
+//   kDataElementAlignOffset: Alignment offset required by the stored type.
 template <typename T, bool kInline, class Enable = void>
 struct InlineWrapper;
 }  // namespace internal
@@ -92,15 +93,14 @@
 //   To maintain general simplicity, we will use the second condition and eat
 //   the cost of the potential extra few bytes of padding.
 // * The layout of the buffer will thus be:
-//   [padding; element_count; inline_data; padding; offset_data]
-//   The first padding will be of size max(0, kAlign - 4).
+//   [element_count; inline_data; padding; offset_data]
 //   The element_count is of size 4.
 //   The inline_data is of size sizeof(InlineType) * kStaticLength.
-//   The second padding is of size
-//       (kAlign - ((sizeof(InlineType) * kStaticLength) % kAlign)).
+//   The padding is sized such that the sum of the size of inline_data and the
+//   padding adds up to the alignment if we have offset_data.
 //   The remaining data is only present if kInline is false.
-//   The offset data is of size T::kSize * kStaticLength. T::kSize % T::kAlign
-//   must be zero.
+//   The offset data is of size T::kSize * kStaticLength. T::kSize is rounded
+//   up to a multiple of T::kAlign.
 //   Note that no padding is required on the end because T::kAlign will always
 //   end up being equal to the alignment (this can only be violated if
 //   kForceAlign is used, but we do not allow that).
@@ -196,7 +196,7 @@
   // Type stored inline in the serialized vector (offsets for tables/strings; T
   // otherwise).
   using InlineType = typename internal::InlineWrapper<T, kInline>::Type;
-  // OUt-of-line type for out-of-line T.
+  // Out-of-line type for out-of-line T.
   using ObjectType = typename internal::InlineWrapper<T, kInline>::ObjectType;
   // Type used as the template parameter to flatbuffers::Vector<>.
   using FlatbufferType =
@@ -216,52 +216,96 @@
       std::max(kForceAlign, alignof(InlineType));
   // Type used for serializing the length of the vector.
   typedef uint32_t LengthType;
+  static constexpr size_t kDataElementAlign =
+      internal::InlineWrapper<T, kInline>::kDataElementAlign;
+  static constexpr size_t kDataElementAlignOffset =
+      internal::InlineWrapper<T, kInline>::kDataElementAlignOffset;
+  // Per-element size of any out-of-line data.
+  static constexpr size_t kDataElementSize =
+      internal::InlineWrapper<T, kInline>::kDataElementSize;
   // Overall alignment of this type, and required alignment of the buffer that
   // must be provided to the Vector.
   static constexpr size_t kAlign =
-      std::max({alignof(LengthType), kInlineAlign,
-                internal::InlineWrapper<T, kInline>::kDataAlign});
-  // Padding inserted prior to the length element of the vector (to manage
-  // alignment of the data properly; see class comment)
-  static constexpr size_t kPadding1 =
-      std::max<size_t>(0, kAlign - sizeof(LengthType));
+      std::max({alignof(LengthType), kInlineAlign, kDataElementAlign});
+  // Offset into the buffer of where things must be aligned to the specified
+  // alignment.
+  static constexpr size_t kAlignOffset = sizeof(LengthType);
+
   // Size of the vector length field.
   static constexpr size_t kLengthSize = sizeof(LengthType);
   // Size of all the inline vector data, including null termination (prior to
   // any dynamic increases in size).
   static constexpr size_t kInlineSize =
       sizeof(InlineType) * (kStaticLength + (kNullTerminate ? 1 : 0));
-  // Per-element size of any out-of-line data.
-  static constexpr size_t kDataElementSize =
-      internal::InlineWrapper<T, kInline>::kDataSize;
+
   // Padding between the inline data and any out-of-line data, to manage
   // mismatches in alignment between the two.
-  static constexpr size_t kPadding2 = kAlign - (kInlineSize % kAlign);
+  //
+  // For inline vectors, we don't want to add any extra padding.  The allocator
+  // will add extra padding if needed and communicate it to our constructor.
+  //
+  // For non-inline vectors, we need to pad out the offsets so that their end
+  // ends up kDataElementAlignOffset before the aligned start of the elements.
+  //
+  // This pads kInlineSize out to
+  static constexpr size_t kPadding1 =
+      kInline
+          ? 0
+          : ((kAlign - ((kInlineSize + kAlign /* Add kAlign to guarentee we
+                                                 don't mod a negative number */
+                         - kDataElementAlignOffset) %
+                        kAlign)) %
+             kAlign);
   // Total statically allocated space for any out-of-line data ("offset data")
   // (prior to any dynamic increases in size).
   static constexpr size_t kOffsetOffsetDataSize =
       kInline ? 0 : (kStaticLength * kDataElementSize);
   // Total nominal size of the Vector.
   static constexpr size_t kSize =
-      kPadding1 + kLengthSize + kInlineSize + kPadding2 + kOffsetOffsetDataSize;
-  // Offset from the start of the provided buffer to where the actual start of
-  // the vector is.
-  static constexpr size_t kOffset = kPadding1;
-  // Constructors; the provided buffer must be aligned to kAlign and be kSize in
-  // length. parent must be non-null.
+      kLengthSize + kInlineSize + kPadding1 + kOffsetOffsetDataSize;
+  // If this is 0, then the parent object will not plan to statically
+  // reserve any memory for this object and will only reserve memory when the
+  // user requests creation of this object. This makes it so that zero-length
+  // vectors (which would require dynamic allocation *anyways* to actually be
+  // helpful) do not use up memory when unpopulated.
+  static constexpr size_t kPreallocatedSize = (kStaticLength > 0) ? kSize : 0;
+
+  // Returns the buffer size (in bytes) needed to hold the largest number of
+  // elements that can fit fully in the provided length (in bytes).  This lets
+  // us compute how much of the padding we can fill with elements.
+  static constexpr size_t RoundedLength(size_t length) {
+    constexpr size_t overall_element_size =
+        sizeof(InlineType) + (kInline ? 0 : kDataElementSize);
+    return ((length - kLengthSize) / overall_element_size) *
+               overall_element_size +
+           kLengthSize;
+  }
+
+  // Constructors; the provided buffer must be aligned to kAlign and be kSize
+  // in length. parent must be non-null.
   Vector(std::span<uint8_t> buffer, ResizeableObject *parent)
       : ResizeableObject(buffer, parent) {
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
-    CHECK_EQ(kSize, buffer.size());
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
+    CHECK_LE(kSize, buffer.size());
+    if constexpr (kInline) {
+      // If everything is inline, it costs us nothing to consume the padding and
+      // use it for holding elements.  For something like a short string in 8
+      // byte aligned space, this saves a second 8 byte allocation for the data.
+      allocated_length_ = (buffer.size() - kLengthSize) / sizeof(InlineType) -
+                          (kNullTerminate ? 1 : 0);
+    }
     SetLength(0u);
     if (!kInline) {
       // Initialize the offsets for any sub-tables. These are used to track
       // where each table will get serialized in memory as memory gets
       // resized/moved around.
+      //
+      // We don't want to expand allocated_length_ here because that would then
+      // imply we have more memory for elements too, which we don't.
       for (size_t index = 0; index < kStaticLength; ++index) {
-        object_absolute_offsets_.emplace_back(kPadding1 + kLengthSize +
-                                              kInlineSize + kPadding2 +
-                                              index * kDataElementSize);
+        object_absolute_offsets_.emplace_back(
+            kLengthSize + kInlineSize + kPadding1 + index * kDataElementSize);
       }
     }
   }
@@ -298,9 +342,28 @@
     if (new_length > allocated_length_) {
       const size_t new_elements = new_length - allocated_length_;
       // First, we must add space for our new inline elements.
-      if (!InsertBytes(
-              inline_data() + allocated_length_ + (kNullTerminate ? 1 : 0),
-              new_elements * sizeof(InlineType), SetZero::kYes)) {
+      std::optional<std::span<uint8_t>> inserted_bytes;
+
+      if (allocated_length_ == 0) {
+        // If we have padding and the padding is enough to hold the buffer, use
+        // it.  This only consumes the padding in the case where we have a
+        // non-inline object, but are allocating small enough data that the
+        // padding is big enough.
+        //
+        // TODO(austin): Use the padding when we are adding large numbers of
+        // elements too.
+        if (new_elements * sizeof(InlineType) <= kPadding1) {
+          inserted_bytes = internal::GetSubSpan(vector_buffer(), kLengthSize,
+                                                kPadding1 / sizeof(InlineType));
+        }
+      }
+
+      if (!inserted_bytes.has_value()) {
+        inserted_bytes = InsertBytes(
+            inline_data() + allocated_length_ + (kNullTerminate ? 1 : 0),
+            new_elements * sizeof(InlineType), SetZero::kYes);
+      }
+      if (!inserted_bytes.has_value()) {
         return false;
       }
       if (!kInline) {
@@ -319,6 +382,14 @@
                                                 index * kDataElementSize);
         }
         objects_.reserve(new_length);
+      } else {
+        // If we allocated memory, and the elements are inline (so we don't have
+        // to deal with allocating elements too), consume any extra space
+        // allocated as extra elements.
+        if (new_elements * sizeof(InlineType) < inserted_bytes->size()) {
+          new_length +=
+              inserted_bytes->size() / sizeof(InlineType) - new_elements;
+        }
       }
       allocated_length_ = new_length;
     }
@@ -545,7 +616,7 @@
     std::stringstream str;
     str << "Raw Size: " << kSize << " alignment: " << kAlign
         << " allocated length: " << allocated_length_ << " inline alignment "
-        << kInlineAlign << " kPadding1 " << kPadding1 << "\n";
+        << kInlineAlign << " \n";
     str << "Observed length " << GetLength() << " (expected " << length_
         << ")\n";
     str << "Inline Size " << kInlineSize << " Inline bytes/value:\n";
@@ -555,7 +626,7 @@
         internal::GetSubSpan(vector_buffer(), kLengthSize,
                              sizeof(InlineType) * allocated_length_),
         str);
-    str << "kPadding2 " << kPadding2 << " offset data size "
+    str << "kPadding1 " << kPadding1 << " offset data size "
         << kOffsetOffsetDataSize << "\n";
     return str.str();
   }
@@ -567,17 +638,12 @@
   Vector(Vector &&) = default;
 
  private:
-  // See kAlign and kOffset.
+  // See kAlign.
   size_t Alignment() const final { return kAlign; }
-  size_t AbsoluteOffsetOffset() const override { return kOffset; }
   // Returns a buffer that starts at the start of the vector itself (past any
   // padding).
-  std::span<uint8_t> vector_buffer() {
-    return internal::GetSubSpan(buffer(), kPadding1);
-  }
-  std::span<const uint8_t> vector_buffer() const {
-    return internal::GetSubSpan(buffer(), kPadding1);
-  }
+  std::span<uint8_t> vector_buffer() { return buffer(); }
+  std::span<const uint8_t> vector_buffer() const { return buffer(); }
 
   bool AddInlineElement(InlineType e) {
     if (length_ == allocated_length_) {
@@ -767,9 +833,11 @@
   typedef flatbuffers::Offset<typename T::Flatbuffer> FlatbufferType;
   typedef flatbuffers::Offset<typename T::Flatbuffer> ConstFlatbufferType;
   typedef T::FlatbufferObjectType FlatbufferObjectType;
-  static_assert((T::kSize % T::kAlign) == 0);
-  static constexpr size_t kDataAlign = T::kAlign;
-  static constexpr size_t kDataSize = T::kSize;
+  static constexpr size_t kDataElementAlign = T::kAlign;
+  static constexpr size_t kDataElementAlignOffset = T::kAlignOffset;
+  static constexpr size_t kDataElementSize =
+      ((T::kSize + T::kAlign - 1) / T::kAlign) * T::kAlign;
+  static_assert((kDataElementSize % kDataElementAlign) == 0);
   template <typename StaticVector>
   static bool FromFlatbuffer(
       StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
@@ -790,8 +858,9 @@
   typedef T FlatbufferType;
   typedef T ConstFlatbufferType;
   typedef T *FlatbufferObjectType;
-  static constexpr size_t kDataAlign = alignof(T);
-  static constexpr size_t kDataSize = sizeof(T);
+  static constexpr size_t kDataElementAlign = alignof(T);
+  static constexpr size_t kDataElementAlignOffset = 0;
+  static constexpr size_t kDataElementSize = sizeof(T);
   template <typename StaticVector>
   static bool FromFlatbuffer(
       StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
@@ -810,8 +879,9 @@
   typedef uint8_t FlatbufferType;
   typedef uint8_t ConstFlatbufferType;
   typedef uint8_t *FlatbufferObjectType;
-  static constexpr size_t kDataAlign = 1u;
-  static constexpr size_t kDataSize = 1u;
+  static constexpr size_t kDataElementAlign = 1u;
+  static constexpr size_t kDataElementAlignOffset = 0;
+  static constexpr size_t kDataElementSize = 1u;
   template <typename StaticVector>
   static bool FromFlatbuffer(
       StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
@@ -833,8 +903,9 @@
   typedef T *FlatbufferType;
   typedef const T *ConstFlatbufferType;
   typedef T *FlatbufferObjectType;
-  static constexpr size_t kDataAlign = alignof(T);
-  static constexpr size_t kDataSize = sizeof(T);
+  static constexpr size_t kDataElementAlign = alignof(T);
+  static constexpr size_t kDataElementAlignOffset = 0;
+  static constexpr size_t kDataElementSize = sizeof(T);
   template <typename StaticVector>
   static bool FromFlatbuffer(
       StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
diff --git a/aos/flatbuffers/test_dir/sample_test_static.h b/aos/flatbuffers/test_dir/sample_test_static.h
index 57ac57a..de88c92 100644
--- a/aos/flatbuffers/test_dir/sample_test_static.h
+++ b/aos/flatbuffers/test_dir/sample_test_static.h
@@ -31,34 +31,20 @@
       sizeof(::flatbuffers::voffset_t) * (2 + 1);
   // Offset from the start of the internal memory buffer to the start of the
   // vtable.
-  static constexpr size_t kVtableStart = ::aos::fbs::PaddedSize(
+  static constexpr size_t kVtableStart = ::aos::fbs::AlignOffset(
       kInlineDataSize, alignof(::flatbuffers::voffset_t));
   // Required alignment of this object. The buffer that this object gets
   // constructed into must be aligned to this value.
   static constexpr size_t kAlign = std::max<size_t>({kMinAlign, 1});
 
-  // Nominal size of this object, in bytes. The object may grow beyond this
-  // size, but will always start at this size and so the initial buffer must
-  // match this size.
-  static constexpr size_t kSize = ::aos::fbs::PaddedSize(
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) + 0, kAlign);
+  // Offset into this object to measure the alignment at.
+  static constexpr size_t kAlignOffset = sizeof(::flatbuffers::soffset_t);
   static_assert(
       1 <= kAlign,
       "Flatbuffer schema minalign should not exceed our required alignment.");
   // Offset from the start of the memory buffer to the start of any out-of-line
   // data (subtables, vectors, strings).
-  static constexpr size_t kOffsetDataStart =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign);
-  // Size required for a buffer that includes a root table offset at the start.
-  static constexpr size_t kRootSize =
-      ::aos::fbs::PaddedSize(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
-  // Minimum size required to build this flatbuffer in an entirely unaligned
-  // buffer (including the root table offset). Made to be a multiple of kAlign
-  // for convenience.
-  static constexpr size_t kUnalignedBufferSize = kRootSize + kAlign;
-  // Offset at which the table vtable offset occurs. This is only needed for
-  // vectors.
-  static constexpr size_t kOffset = 0;
+  static constexpr size_t kOffsetDataStart = (kVtableStart + kVtableSize);
   // Various overrides to support the Table parent class.
   size_t FixedVtableOffset() const final { return kVtableStart; }
   size_t VtableSize() const final { return kVtableSize; }
@@ -82,14 +68,16 @@
                               ::aos::fbs::ResizeableObject *parent)
       : Table(buffer, parent) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
   MinimallyAlignedTableStatic(std::span<uint8_t> buffer,
                               ::aos::fbs::Allocator *allocator)
       : Table(buffer, allocator) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
   MinimallyAlignedTableStatic(
@@ -97,7 +85,8 @@
       ::std::unique_ptr<::aos::fbs::Allocator> allocator)
       : Table(buffer, ::std::move(allocator)) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
 
@@ -114,7 +103,6 @@
     return has_field()
                ? std::make_optional(Get<uint8_t>(kInlineAbsoluteOffset_field))
                : std::nullopt;
-    ;
   }
   // Returns a pointer to modify the field field.
   // The pointer may be invalidated by mutations/movements of the underlying
@@ -186,6 +174,21 @@
   size_t NumberOfSubObjects() const final { return 0; }
   using ::aos::fbs::ResizeableObject::SubObject;
   SubObject GetSubObject(size_t) final { LOG(FATAL) << "No subobjects."; }
+
+ public:
+  // Nominal size of this object, in bytes. The object may grow beyond this
+  // size, but will always start at this size and so the initial buffer must
+  // match this size.
+  static constexpr size_t kSize =
+      ::aos::fbs::AlignOffset((kVtableStart + kVtableSize) - kAlignOffset,
+                              kAlign, kAlignOffset) +
+      kAlignOffset;
+  // Always statically allocate memory for tables (set for consistency with
+  // static_vector.h).
+  static constexpr size_t kPreallocatedSize = kSize;
+  // Size required for a buffer that includes a root table offset at the start.
+  static constexpr size_t kRootSize =
+      ::aos::fbs::AlignOffset(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
 };
 }  // namespace aos::fbs::testing
 
@@ -211,34 +214,20 @@
       sizeof(::flatbuffers::voffset_t) * (2 + 3);
   // Offset from the start of the internal memory buffer to the start of the
   // vtable.
-  static constexpr size_t kVtableStart = ::aos::fbs::PaddedSize(
+  static constexpr size_t kVtableStart = ::aos::fbs::AlignOffset(
       kInlineDataSize, alignof(::flatbuffers::voffset_t));
   // Required alignment of this object. The buffer that this object gets
   // constructed into must be aligned to this value.
   static constexpr size_t kAlign = std::max<size_t>({kMinAlign, 4, 2});
 
-  // Nominal size of this object, in bytes. The object may grow beyond this
-  // size, but will always start at this size and so the initial buffer must
-  // match this size.
-  static constexpr size_t kSize = ::aos::fbs::PaddedSize(
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) + 0, kAlign);
+  // Offset into this object to measure the alignment at.
+  static constexpr size_t kAlignOffset = sizeof(::flatbuffers::soffset_t);
   static_assert(
       1 <= kAlign,
       "Flatbuffer schema minalign should not exceed our required alignment.");
   // Offset from the start of the memory buffer to the start of any out-of-line
   // data (subtables, vectors, strings).
-  static constexpr size_t kOffsetDataStart =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign);
-  // Size required for a buffer that includes a root table offset at the start.
-  static constexpr size_t kRootSize =
-      ::aos::fbs::PaddedSize(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
-  // Minimum size required to build this flatbuffer in an entirely unaligned
-  // buffer (including the root table offset). Made to be a multiple of kAlign
-  // for convenience.
-  static constexpr size_t kUnalignedBufferSize = kRootSize + kAlign;
-  // Offset at which the table vtable offset occurs. This is only needed for
-  // vectors.
-  static constexpr size_t kOffset = 0;
+  static constexpr size_t kOffsetDataStart = (kVtableStart + kVtableSize);
   // Various overrides to support the Table parent class.
   size_t FixedVtableOffset() const final { return kVtableStart; }
   size_t VtableSize() const final { return kVtableSize; }
@@ -262,20 +251,23 @@
                  ::aos::fbs::ResizeableObject *parent)
       : Table(buffer, parent) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
   SubTableStatic(std::span<uint8_t> buffer, ::aos::fbs::Allocator *allocator)
       : Table(buffer, allocator) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
   SubTableStatic(std::span<uint8_t> buffer,
                  ::std::unique_ptr<::aos::fbs::Allocator> allocator)
       : Table(buffer, ::std::move(allocator)) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
 
@@ -291,7 +283,6 @@
   std::optional<float> baz() const {
     return has_baz() ? std::make_optional(Get<float>(kInlineAbsoluteOffset_baz))
                      : std::nullopt;
-    ;
   }
   // Returns a pointer to modify the baz field.
   // The pointer may be invalidated by mutations/movements of the underlying
@@ -317,7 +308,6 @@
     return has_foo()
                ? std::make_optional(Get<int16_t>(kInlineAbsoluteOffset_foo))
                : std::nullopt;
-    ;
   }
   // Returns a pointer to modify the foo field.
   // The pointer may be invalidated by mutations/movements of the underlying
@@ -400,6 +390,21 @@
   size_t NumberOfSubObjects() const final { return 0; }
   using ::aos::fbs::ResizeableObject::SubObject;
   SubObject GetSubObject(size_t) final { LOG(FATAL) << "No subobjects."; }
+
+ public:
+  // Nominal size of this object, in bytes. The object may grow beyond this
+  // size, but will always start at this size and so the initial buffer must
+  // match this size.
+  static constexpr size_t kSize =
+      ::aos::fbs::AlignOffset((kVtableStart + kVtableSize) - kAlignOffset,
+                              kAlign, kAlignOffset) +
+      kAlignOffset;
+  // Always statically allocate memory for tables (set for consistency with
+  // static_vector.h).
+  static constexpr size_t kPreallocatedSize = kSize;
+  // Size required for a buffer that includes a root table offset at the start.
+  static constexpr size_t kRootSize =
+      ::aos::fbs::AlignOffset(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
 };
 }  // namespace aos::fbs::testing
 
@@ -425,87 +430,33 @@
       sizeof(::flatbuffers::voffset_t) * (2 + 13);
   // Offset from the start of the internal memory buffer to the start of the
   // vtable.
-  static constexpr size_t kVtableStart = ::aos::fbs::PaddedSize(
+  static constexpr size_t kVtableStart = ::aos::fbs::AlignOffset(
       kInlineDataSize, alignof(::flatbuffers::voffset_t));
   // Required alignment of this object. The buffer that this object gets
   // constructed into must be aligned to this value.
   static constexpr size_t kAlign = std::max<size_t>(
-      {kMinAlign, aos::fbs::testing::included::IncludedTableStatic::kAlign, 4,
-       ::aos::fbs::String<20>::kAlign, 8,
-       aos::fbs::testing::SubTableStatic::kAlign, ::aos::fbs::String<0>::kAlign,
-       ::aos::fbs::Vector<uint8_t, 0, true, 0>::kAlign,
-       ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kAlign,
-       ::aos::fbs::Vector<int32_t, 3, true, 64>::kAlign,
-       ::aos::fbs::Vector<int32_t, 3, true, 0>::kAlign,
-       ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kAlign,
+      {kMinAlign, 8,
        ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0>::kAlign,
+       ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kAlign,
        ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false,
-                          0>::kAlign});
+                          0>::kAlign,
+       ::aos::fbs::Vector<int32_t, 3, true, 64>::kAlign,
+       ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kAlign,
+       ::aos::fbs::Vector<int32_t, 3, true, 0>::kAlign,
+       ::aos::fbs::String<0>::kAlign,
+       ::aos::fbs::Vector<uint8_t, 0, true, 0>::kAlign,
+       aos::fbs::testing::included::IncludedTableStatic::kAlign,
+       aos::fbs::testing::SubTableStatic::kAlign,
+       ::aos::fbs::String<20>::kAlign, 4});
 
-  // Nominal size of this object, in bytes. The object may grow beyond this
-  // size, but will always start at this size and so the initial buffer must
-  // match this size.
-  static constexpr size_t kSize = ::aos::fbs::PaddedSize(
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(
-                      ::aos::fbs::PaddedSize(
-                          ::aos::fbs::PaddedSize(
-                              ::aos::fbs::PaddedSize(
-                                  ::aos::fbs::PaddedSize(
-                                      ::aos::fbs::PaddedSize(
-                                          ::aos::fbs::PaddedSize(
-                                              ::aos::fbs::PaddedSize(
-                                                  ::aos::fbs::PaddedSize(
-                                                      0, kAlign) +
-                                                      aos::fbs::testing::included::
-                                                          IncludedTableStatic::
-                                                              kSize,
-                                                  kAlign) +
-                                                  ::aos::fbs::String<20>::kSize,
-                                              kAlign) +
-                                              aos::fbs::testing::
-                                                  SubTableStatic::kSize,
-                                          kAlign) +
-                                          ::aos::fbs::String<0>::kSize,
-                                      kAlign) +
-                                      ::aos::fbs::Vector<uint8_t, 0, true,
-                                                         0>::kSize,
-                                  kAlign) +
-                                  ::aos::fbs::Vector<::aos::fbs::String<0>, 0,
-                                                     false, 0>::kSize,
-                              kAlign) +
-                              ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize,
-                          kAlign) +
-                          ::aos::fbs::Vector<int32_t, 3, true, 0>::kSize,
-                      kAlign) +
-                      ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false,
-                                         0>::kSize,
-                  kAlign) +
-                  ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true,
-                                     0>::kSize,
-              kAlign) +
-          ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false,
-                             0>::kSize,
-      kAlign);
+  // Offset into this object to measure the alignment at.
+  static constexpr size_t kAlignOffset = sizeof(::flatbuffers::soffset_t);
   static_assert(
       1 <= kAlign,
       "Flatbuffer schema minalign should not exceed our required alignment.");
   // Offset from the start of the memory buffer to the start of any out-of-line
   // data (subtables, vectors, strings).
-  static constexpr size_t kOffsetDataStart =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign);
-  // Size required for a buffer that includes a root table offset at the start.
-  static constexpr size_t kRootSize =
-      ::aos::fbs::PaddedSize(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
-  // Minimum size required to build this flatbuffer in an entirely unaligned
-  // buffer (including the root table offset). Made to be a multiple of kAlign
-  // for convenience.
-  static constexpr size_t kUnalignedBufferSize = kRootSize + kAlign;
-  // Offset at which the table vtable offset occurs. This is only needed for
-  // vectors.
-  static constexpr size_t kOffset = 0;
+  static constexpr size_t kOffsetDataStart = (kVtableStart + kVtableSize);
   // Various overrides to support the Table parent class.
   size_t FixedVtableOffset() const final { return kVtableStart; }
   size_t VtableSize() const final { return kVtableSize; }
@@ -529,129 +480,28 @@
                   ::aos::fbs::ResizeableObject *parent)
       : Table(buffer, parent) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
   TestTableStatic(std::span<uint8_t> buffer, ::aos::fbs::Allocator *allocator)
       : Table(buffer, allocator) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
   TestTableStatic(std::span<uint8_t> buffer,
                   ::std::unique_ptr<::aos::fbs::Allocator> allocator)
       : Table(buffer, ::std::move(allocator)) {
     CHECK_EQ(buffer.size(), kSize);
-    CHECK_EQ(0u, reinterpret_cast<size_t>(buffer.data()) % kAlign);
+    CHECK_EQ(0u,
+             reinterpret_cast<size_t>(buffer.data() + kAlignOffset) % kAlign);
     PopulateVtable();
   }
 
   virtual ~TestTableStatic() {}
 
-  // Creates an empty object for the included_table field, which you can
-  // then populate/modify as desired.
-  // The field must not be populated yet.
-  aos::fbs::testing::included::IncludedTableStatic *add_included_table() {
-    CHECK(!included_table_.has_value());
-    constexpr size_t kVtableIndex = 22;
-    // Construct the *Static object that we will use for managing this subtable.
-    included_table_.emplace(
-        BufferForObject(object_absolute_offset_included_table,
-                        aos::fbs::testing::included::IncludedTableStatic::kSize,
-                        kAlign),
-        this);
-    // Actually set the appropriate fields in the flatbuffer memory itself.
-    SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_included_table, kVtableIndex,
-        object_absolute_offset_included_table +
-            aos::fbs::testing::included::IncludedTableStatic::kOffset -
-            kInlineAbsoluteOffset_included_table);
-    return &included_table_.value().t;
-  }
-
-  // Returns a pointer to the included_table field, if set. nullptr otherwise.
-  const aos::fbs::testing::included::IncludedTableStatic *included_table()
-      const {
-    return included_table_.has_value() ? &included_table_.value().t : nullptr;
-  }
-  aos::fbs::testing::included::IncludedTableStatic *mutable_included_table() {
-    return included_table_.has_value() ? &included_table_.value().t : nullptr;
-  }
-
-  // Clears the included_table field. This will cause has_included_table() to
-  // return false.
-  void clear_included_table() {
-    included_table_.reset();
-    ClearField(kInlineAbsoluteOffset_included_table, 4, 22);
-  }
-
-  // Returns true if the included_table field is set and can be accessed.
-  bool has_included_table() const {
-    return AsFlatbuffer().has_included_table();
-  }
-
-  // Sets the scalar field, causing it to be populated if it is not already.
-  // This will populate the field even if the specified value is the default.
-  void set_scalar(const int32_t &value) {
-    SetField<int32_t>(kInlineAbsoluteOffset_scalar, 4, value);
-  }
-
-  // Returns the value of scalar if set; nullopt otherwise.
-  std::optional<int32_t> scalar() const {
-    return has_scalar()
-               ? std::make_optional(Get<int32_t>(kInlineAbsoluteOffset_scalar))
-               : std::nullopt;
-    ;
-  }
-  // Returns a pointer to modify the scalar field.
-  // The pointer may be invalidated by mutations/movements of the underlying
-  // buffer. Returns nullptr if the field is not set.
-  int32_t *mutable_scalar() {
-    return has_scalar() ? MutableGet<int32_t>(kInlineAbsoluteOffset_scalar)
-                        : nullptr;
-  }
-
-  // Clears the scalar field. This will cause has_scalar() to return false.
-  void clear_scalar() { ClearField(kInlineAbsoluteOffset_scalar, 4, 4); }
-
-  // Returns true if the scalar field is set and can be accessed.
-  bool has_scalar() const { return AsFlatbuffer().has_scalar(); }
-
-  // Creates an empty object for the string field, which you can
-  // then populate/modify as desired.
-  // The field must not be populated yet.
-  ::aos::fbs::String<20> *add_string() {
-    CHECK(!string_.has_value());
-    constexpr size_t kVtableIndex = 8;
-    // Construct the *Static object that we will use for managing this subtable.
-    string_.emplace(BufferForObject(object_absolute_offset_string,
-                                    ::aos::fbs::String<20>::kSize, kAlign),
-                    this);
-    // Actually set the appropriate fields in the flatbuffer memory itself.
-    SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_string, kVtableIndex,
-        object_absolute_offset_string + ::aos::fbs::String<20>::kOffset -
-            kInlineAbsoluteOffset_string);
-    return &string_.value().t;
-  }
-
-  // Returns a pointer to the string field, if set. nullptr otherwise.
-  const ::aos::fbs::String<20> *string() const {
-    return string_.has_value() ? &string_.value().t : nullptr;
-  }
-  ::aos::fbs::String<20> *mutable_string() {
-    return string_.has_value() ? &string_.value().t : nullptr;
-  }
-
-  // Clears the string field. This will cause has_string() to return false.
-  void clear_string() {
-    string_.reset();
-    ClearField(kInlineAbsoluteOffset_string, 4, 8);
-  }
-
-  // Returns true if the string field is set and can be accessed.
-  bool has_string() const { return AsFlatbuffer().has_string(); }
-
   // Sets the substruct field, causing it to be populated if it is not already.
   // This will populate the field even if the specified value is the default.
   void set_substruct(const aos::fbs::testing::SubStruct &value) {
@@ -665,7 +515,6 @@
                ? std::make_optional(Get<aos::fbs::testing::SubStruct>(
                      kInlineAbsoluteOffset_substruct))
                : std::nullopt;
-    ;
   }
   // Returns a pointer to modify the substruct field.
   // The pointer may be invalidated by mutations/movements of the underlying
@@ -685,58 +534,551 @@
   // Returns true if the substruct field is set and can be accessed.
   bool has_substruct() const { return AsFlatbuffer().has_substruct(); }
 
-  // Creates an empty object for the subtable field, which you can
+  // Creates an empty object for the vector_of_structs field, which you can
   // then populate/modify as desired.
   // The field must not be populated yet.
-  aos::fbs::testing::SubTableStatic *add_subtable() {
-    CHECK(!subtable_.has_value());
-    constexpr size_t kVtableIndex = 14;
-    // Construct the *Static object that we will use for managing this subtable.
-    subtable_.emplace(
-        BufferForObject(object_absolute_offset_subtable,
-                        aos::fbs::testing::SubTableStatic::kSize, kAlign),
-        this);
+  ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0> *
+  add_vector_of_structs() {
+    CHECK(!vector_of_structs_.has_value());
+    constexpr size_t kVtableIndex = 18;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true,
+                                     0>::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_vector_of_structs;
+      std::optional<std::span<uint8_t>> inserted_bytes = InsertBytes(
+          buffer().data() + object_absolute_offset,
+          ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0>::kSize,
+          ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_vector_of_structs = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_structs_.emplace(
+          BufferForObject(
+              object_absolute_offset_vector_of_structs,
+              ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0>::
+                  RoundedLength(inserted_bytes.value().size())),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_structs_.emplace(
+          BufferForObject(object_absolute_offset_vector_of_structs,
+                          ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3,
+                                             true, 0>::kSize),
+          this);
+    }
     // Actually set the appropriate fields in the flatbuffer memory itself.
     SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_subtable, kVtableIndex,
-        object_absolute_offset_subtable +
-            aos::fbs::testing::SubTableStatic::kOffset -
-            kInlineAbsoluteOffset_subtable);
-    return &subtable_.value().t;
+        kInlineAbsoluteOffset_vector_of_structs, kVtableIndex,
+        object_absolute_offset_vector_of_structs -
+            kInlineAbsoluteOffset_vector_of_structs);
+    return &vector_of_structs_.value().t;
   }
 
-  // Returns a pointer to the subtable field, if set. nullptr otherwise.
-  const aos::fbs::testing::SubTableStatic *subtable() const {
-    return subtable_.has_value() ? &subtable_.value().t : nullptr;
+  // Returns a pointer to the vector_of_structs field, if set. nullptr
+  // otherwise.
+  const ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0> *
+  vector_of_structs() const {
+    return vector_of_structs_.has_value() ? &vector_of_structs_.value().t
+                                          : nullptr;
   }
-  aos::fbs::testing::SubTableStatic *mutable_subtable() {
-    return subtable_.has_value() ? &subtable_.value().t : nullptr;
+  ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0> *
+  mutable_vector_of_structs() {
+    return vector_of_structs_.has_value() ? &vector_of_structs_.value().t
+                                          : nullptr;
   }
 
-  // Clears the subtable field. This will cause has_subtable() to return false.
-  void clear_subtable() {
-    subtable_.reset();
-    ClearField(kInlineAbsoluteOffset_subtable, 4, 14);
+  // Clears the vector_of_structs field. This will cause has_vector_of_structs()
+  // to return false.
+  void clear_vector_of_structs() {
+    vector_of_structs_.reset();
+    ClearField(kInlineAbsoluteOffset_vector_of_structs, 4, 18);
   }
 
-  // Returns true if the subtable field is set and can be accessed.
-  bool has_subtable() const { return AsFlatbuffer().has_subtable(); }
+  // Returns true if the vector_of_structs field is set and can be accessed.
+  bool has_vector_of_structs() const {
+    return AsFlatbuffer().has_vector_of_structs();
+  }
+
+  // Creates an empty object for the unspecified_length_vector_of_strings field,
+  // which you can then populate/modify as desired. The field must not be
+  // populated yet.
+  ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0> *
+  add_unspecified_length_vector_of_strings() {
+    CHECK(!unspecified_length_vector_of_strings_.has_value());
+    constexpr size_t kVtableIndex = 28;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::Vector<::aos::fbs::String<0>, 0, false,
+                                     0>::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_unspecified_length_vector_of_strings;
+      std::optional<std::span<uint8_t>> inserted_bytes = InsertBytes(
+          buffer().data() + object_absolute_offset,
+          ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kSize,
+          ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_unspecified_length_vector_of_strings =
+          object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      unspecified_length_vector_of_strings_.emplace(
+          BufferForObject(
+              object_absolute_offset_unspecified_length_vector_of_strings,
+              ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kSize),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      unspecified_length_vector_of_strings_.emplace(
+          BufferForObject(
+              object_absolute_offset_unspecified_length_vector_of_strings,
+              ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kSize),
+          this);
+    }
+    // Actually set the appropriate fields in the flatbuffer memory itself.
+    SetField<::flatbuffers::uoffset_t>(
+        kInlineAbsoluteOffset_unspecified_length_vector_of_strings,
+        kVtableIndex,
+        object_absolute_offset_unspecified_length_vector_of_strings -
+            kInlineAbsoluteOffset_unspecified_length_vector_of_strings);
+    return &unspecified_length_vector_of_strings_.value().t;
+  }
+
+  // Returns a pointer to the unspecified_length_vector_of_strings field, if
+  // set. nullptr otherwise.
+  const ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0> *
+  unspecified_length_vector_of_strings() const {
+    return unspecified_length_vector_of_strings_.has_value()
+               ? &unspecified_length_vector_of_strings_.value().t
+               : nullptr;
+  }
+  ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0> *
+  mutable_unspecified_length_vector_of_strings() {
+    return unspecified_length_vector_of_strings_.has_value()
+               ? &unspecified_length_vector_of_strings_.value().t
+               : nullptr;
+  }
+
+  // Clears the unspecified_length_vector_of_strings field. This will cause
+  // has_unspecified_length_vector_of_strings() to return false.
+  void clear_unspecified_length_vector_of_strings() {
+    unspecified_length_vector_of_strings_.reset();
+    ClearField(kInlineAbsoluteOffset_unspecified_length_vector_of_strings, 4,
+               28);
+  }
+
+  // Returns true if the unspecified_length_vector_of_strings field is set and
+  // can be accessed.
+  bool has_unspecified_length_vector_of_strings() const {
+    return AsFlatbuffer().has_unspecified_length_vector_of_strings();
+  }
+
+  // Creates an empty object for the vector_of_tables field, which you can
+  // then populate/modify as desired.
+  // The field must not be populated yet.
+  ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false, 0> *
+  add_vector_of_tables() {
+    CHECK(!vector_of_tables_.has_value());
+    constexpr size_t kVtableIndex = 20;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3,
+                                     false, 0>::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_vector_of_tables;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset,
+                      ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3,
+                                         false, 0>::kSize,
+                      ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_vector_of_tables = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_tables_.emplace(
+          BufferForObject(object_absolute_offset_vector_of_tables,
+                          ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic,
+                                             3, false, 0>::kSize),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_tables_.emplace(
+          BufferForObject(object_absolute_offset_vector_of_tables,
+                          ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic,
+                                             3, false, 0>::kSize),
+          this);
+    }
+    // Actually set the appropriate fields in the flatbuffer memory itself.
+    SetField<::flatbuffers::uoffset_t>(
+        kInlineAbsoluteOffset_vector_of_tables, kVtableIndex,
+        object_absolute_offset_vector_of_tables -
+            kInlineAbsoluteOffset_vector_of_tables);
+    return &vector_of_tables_.value().t;
+  }
+
+  // Returns a pointer to the vector_of_tables field, if set. nullptr otherwise.
+  const ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false, 0> *
+  vector_of_tables() const {
+    return vector_of_tables_.has_value() ? &vector_of_tables_.value().t
+                                         : nullptr;
+  }
+  ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false, 0> *
+  mutable_vector_of_tables() {
+    return vector_of_tables_.has_value() ? &vector_of_tables_.value().t
+                                         : nullptr;
+  }
+
+  // Clears the vector_of_tables field. This will cause has_vector_of_tables()
+  // to return false.
+  void clear_vector_of_tables() {
+    vector_of_tables_.reset();
+    ClearField(kInlineAbsoluteOffset_vector_of_tables, 4, 20);
+  }
+
+  // Returns true if the vector_of_tables field is set and can be accessed.
+  bool has_vector_of_tables() const {
+    return AsFlatbuffer().has_vector_of_tables();
+  }
+
+  // Creates an empty object for the vector_aligned field, which you can
+  // then populate/modify as desired.
+  // The field must not be populated yet.
+  ::aos::fbs::Vector<int32_t, 3, true, 64> *add_vector_aligned() {
+    CHECK(!vector_aligned_.has_value());
+    constexpr size_t kVtableIndex = 16;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::Vector<int32_t, 3, true, 64>::kPreallocatedSize ==
+                  0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_vector_aligned;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset,
+                      ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize,
+                      ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_vector_aligned = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_aligned_.emplace(
+          BufferForObject(
+              object_absolute_offset_vector_aligned,
+              ::aos::fbs::Vector<int32_t, 3, true, 64>::RoundedLength(
+                  inserted_bytes.value().size())),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_aligned_.emplace(
+          BufferForObject(object_absolute_offset_vector_aligned,
+                          ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize),
+          this);
+    }
+    // Actually set the appropriate fields in the flatbuffer memory itself.
+    SetField<::flatbuffers::uoffset_t>(
+        kInlineAbsoluteOffset_vector_aligned, kVtableIndex,
+        object_absolute_offset_vector_aligned -
+            kInlineAbsoluteOffset_vector_aligned);
+    return &vector_aligned_.value().t;
+  }
+
+  // Returns a pointer to the vector_aligned field, if set. nullptr otherwise.
+  const ::aos::fbs::Vector<int32_t, 3, true, 64> *vector_aligned() const {
+    return vector_aligned_.has_value() ? &vector_aligned_.value().t : nullptr;
+  }
+  ::aos::fbs::Vector<int32_t, 3, true, 64> *mutable_vector_aligned() {
+    return vector_aligned_.has_value() ? &vector_aligned_.value().t : nullptr;
+  }
+
+  // Clears the vector_aligned field. This will cause has_vector_aligned() to
+  // return false.
+  void clear_vector_aligned() {
+    vector_aligned_.reset();
+    ClearField(kInlineAbsoluteOffset_vector_aligned, 4, 16);
+  }
+
+  // Returns true if the vector_aligned field is set and can be accessed.
+  bool has_vector_aligned() const {
+    return AsFlatbuffer().has_vector_aligned();
+  }
+
+  // Creates an empty object for the vector_of_strings field, which you can
+  // then populate/modify as desired.
+  // The field must not be populated yet.
+  ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0> *
+  add_vector_of_strings() {
+    CHECK(!vector_of_strings_.has_value());
+    constexpr size_t kVtableIndex = 10;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::Vector<::aos::fbs::String<10>, 3, false,
+                                     0>::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_vector_of_strings;
+      std::optional<std::span<uint8_t>> inserted_bytes = InsertBytes(
+          buffer().data() + object_absolute_offset,
+          ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kSize,
+          ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_vector_of_strings = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_strings_.emplace(
+          BufferForObject(
+              object_absolute_offset_vector_of_strings,
+              ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kSize),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_strings_.emplace(
+          BufferForObject(
+              object_absolute_offset_vector_of_strings,
+              ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kSize),
+          this);
+    }
+    // Actually set the appropriate fields in the flatbuffer memory itself.
+    SetField<::flatbuffers::uoffset_t>(
+        kInlineAbsoluteOffset_vector_of_strings, kVtableIndex,
+        object_absolute_offset_vector_of_strings -
+            kInlineAbsoluteOffset_vector_of_strings);
+    return &vector_of_strings_.value().t;
+  }
+
+  // Returns a pointer to the vector_of_strings field, if set. nullptr
+  // otherwise.
+  const ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0> *
+  vector_of_strings() const {
+    return vector_of_strings_.has_value() ? &vector_of_strings_.value().t
+                                          : nullptr;
+  }
+  ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0> *
+  mutable_vector_of_strings() {
+    return vector_of_strings_.has_value() ? &vector_of_strings_.value().t
+                                          : nullptr;
+  }
+
+  // Clears the vector_of_strings field. This will cause has_vector_of_strings()
+  // to return false.
+  void clear_vector_of_strings() {
+    vector_of_strings_.reset();
+    ClearField(kInlineAbsoluteOffset_vector_of_strings, 4, 10);
+  }
+
+  // Returns true if the vector_of_strings field is set and can be accessed.
+  bool has_vector_of_strings() const {
+    return AsFlatbuffer().has_vector_of_strings();
+  }
+
+  // Creates an empty object for the vector_of_scalars field, which you can
+  // then populate/modify as desired.
+  // The field must not be populated yet.
+  ::aos::fbs::Vector<int32_t, 3, true, 0> *add_vector_of_scalars() {
+    CHECK(!vector_of_scalars_.has_value());
+    constexpr size_t kVtableIndex = 6;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::Vector<int32_t, 3, true, 0>::kPreallocatedSize ==
+                  0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_vector_of_scalars;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset,
+                      ::aos::fbs::Vector<int32_t, 3, true, 0>::kSize,
+                      ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_vector_of_scalars = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_scalars_.emplace(
+          BufferForObject(
+              object_absolute_offset_vector_of_scalars,
+              ::aos::fbs::Vector<int32_t, 3, true, 0>::RoundedLength(
+                  inserted_bytes.value().size())),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      vector_of_scalars_.emplace(
+          BufferForObject(object_absolute_offset_vector_of_scalars,
+                          ::aos::fbs::Vector<int32_t, 3, true, 0>::kSize),
+          this);
+    }
+    // Actually set the appropriate fields in the flatbuffer memory itself.
+    SetField<::flatbuffers::uoffset_t>(
+        kInlineAbsoluteOffset_vector_of_scalars, kVtableIndex,
+        object_absolute_offset_vector_of_scalars -
+            kInlineAbsoluteOffset_vector_of_scalars);
+    return &vector_of_scalars_.value().t;
+  }
+
+  // Returns a pointer to the vector_of_scalars field, if set. nullptr
+  // otherwise.
+  const ::aos::fbs::Vector<int32_t, 3, true, 0> *vector_of_scalars() const {
+    return vector_of_scalars_.has_value() ? &vector_of_scalars_.value().t
+                                          : nullptr;
+  }
+  ::aos::fbs::Vector<int32_t, 3, true, 0> *mutable_vector_of_scalars() {
+    return vector_of_scalars_.has_value() ? &vector_of_scalars_.value().t
+                                          : nullptr;
+  }
+
+  // Clears the vector_of_scalars field. This will cause has_vector_of_scalars()
+  // to return false.
+  void clear_vector_of_scalars() {
+    vector_of_scalars_.reset();
+    ClearField(kInlineAbsoluteOffset_vector_of_scalars, 4, 6);
+  }
+
+  // Returns true if the vector_of_scalars field is set and can be accessed.
+  bool has_vector_of_scalars() const {
+    return AsFlatbuffer().has_vector_of_scalars();
+  }
 
   // Creates an empty object for the unspecified_length_string field, which you
   // can then populate/modify as desired. The field must not be populated yet.
   ::aos::fbs::String<0> *add_unspecified_length_string() {
     CHECK(!unspecified_length_string_.has_value());
     constexpr size_t kVtableIndex = 26;
-    // Construct the *Static object that we will use for managing this subtable.
-    unspecified_length_string_.emplace(
-        BufferForObject(object_absolute_offset_unspecified_length_string,
-                        ::aos::fbs::String<0>::kSize, kAlign),
-        this);
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::String<0>::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_unspecified_length_string;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset,
+                      ::aos::fbs::String<0>::kSize, ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_unspecified_length_string = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      unspecified_length_string_.emplace(
+          BufferForObject(object_absolute_offset_unspecified_length_string,
+                          ::aos::fbs::String<0>::RoundedLength(
+                              inserted_bytes.value().size())),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      unspecified_length_string_.emplace(
+          BufferForObject(object_absolute_offset_unspecified_length_string,
+                          ::aos::fbs::String<0>::kSize),
+          this);
+    }
     // Actually set the appropriate fields in the flatbuffer memory itself.
     SetField<::flatbuffers::uoffset_t>(
         kInlineAbsoluteOffset_unspecified_length_string, kVtableIndex,
-        object_absolute_offset_unspecified_length_string +
-            ::aos::fbs::String<0>::kOffset -
+        object_absolute_offset_unspecified_length_string -
             kInlineAbsoluteOffset_unspecified_length_string);
     return &unspecified_length_string_.value().t;
   }
@@ -772,16 +1114,53 @@
   ::aos::fbs::Vector<uint8_t, 0, true, 0> *add_unspecified_length_vector() {
     CHECK(!unspecified_length_vector_.has_value());
     constexpr size_t kVtableIndex = 24;
-    // Construct the *Static object that we will use for managing this subtable.
-    unspecified_length_vector_.emplace(
-        BufferForObject(object_absolute_offset_unspecified_length_vector,
-                        ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize, kAlign),
-        this);
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::Vector<uint8_t, 0, true, 0>::kPreallocatedSize ==
+                  0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_unspecified_length_vector;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset,
+                      ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize,
+                      ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_unspecified_length_vector = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      unspecified_length_vector_.emplace(
+          BufferForObject(
+              object_absolute_offset_unspecified_length_vector,
+              ::aos::fbs::Vector<uint8_t, 0, true, 0>::RoundedLength(
+                  inserted_bytes.value().size())),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      unspecified_length_vector_.emplace(
+          BufferForObject(object_absolute_offset_unspecified_length_vector,
+                          ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize),
+          this);
+    }
     // Actually set the appropriate fields in the flatbuffer memory itself.
     SetField<::flatbuffers::uoffset_t>(
         kInlineAbsoluteOffset_unspecified_length_vector, kVtableIndex,
-        object_absolute_offset_unspecified_length_vector +
-            ::aos::fbs::Vector<uint8_t, 0, true, 0>::kOffset -
+        object_absolute_offset_unspecified_length_vector -
             kInlineAbsoluteOffset_unspecified_length_vector);
     return &unspecified_length_vector_.value().t;
   }
@@ -813,303 +1192,260 @@
     return AsFlatbuffer().has_unspecified_length_vector();
   }
 
-  // Creates an empty object for the unspecified_length_vector_of_strings field,
-  // which you can then populate/modify as desired. The field must not be
-  // populated yet.
-  ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0> *
-  add_unspecified_length_vector_of_strings() {
-    CHECK(!unspecified_length_vector_of_strings_.has_value());
-    constexpr size_t kVtableIndex = 28;
-    // Construct the *Static object that we will use for managing this subtable.
-    unspecified_length_vector_of_strings_.emplace(
-        BufferForObject(
-            object_absolute_offset_unspecified_length_vector_of_strings,
-            ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kSize,
-            kAlign),
-        this);
-    // Actually set the appropriate fields in the flatbuffer memory itself.
-    SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_unspecified_length_vector_of_strings,
-        kVtableIndex,
-        object_absolute_offset_unspecified_length_vector_of_strings +
-            ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kOffset -
-            kInlineAbsoluteOffset_unspecified_length_vector_of_strings);
-    return &unspecified_length_vector_of_strings_.value().t;
-  }
-
-  // Returns a pointer to the unspecified_length_vector_of_strings field, if
-  // set. nullptr otherwise.
-  const ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0> *
-  unspecified_length_vector_of_strings() const {
-    return unspecified_length_vector_of_strings_.has_value()
-               ? &unspecified_length_vector_of_strings_.value().t
-               : nullptr;
-  }
-  ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0> *
-  mutable_unspecified_length_vector_of_strings() {
-    return unspecified_length_vector_of_strings_.has_value()
-               ? &unspecified_length_vector_of_strings_.value().t
-               : nullptr;
-  }
-
-  // Clears the unspecified_length_vector_of_strings field. This will cause
-  // has_unspecified_length_vector_of_strings() to return false.
-  void clear_unspecified_length_vector_of_strings() {
-    unspecified_length_vector_of_strings_.reset();
-    ClearField(kInlineAbsoluteOffset_unspecified_length_vector_of_strings, 4,
-               28);
-  }
-
-  // Returns true if the unspecified_length_vector_of_strings field is set and
-  // can be accessed.
-  bool has_unspecified_length_vector_of_strings() const {
-    return AsFlatbuffer().has_unspecified_length_vector_of_strings();
-  }
-
-  // Creates an empty object for the vector_aligned field, which you can
+  // Creates an empty object for the included_table field, which you can
   // then populate/modify as desired.
   // The field must not be populated yet.
-  ::aos::fbs::Vector<int32_t, 3, true, 64> *add_vector_aligned() {
-    CHECK(!vector_aligned_.has_value());
-    constexpr size_t kVtableIndex = 16;
-    // Construct the *Static object that we will use for managing this subtable.
-    vector_aligned_.emplace(
-        BufferForObject(object_absolute_offset_vector_aligned,
-                        ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize,
-                        kAlign),
-        this);
+  aos::fbs::testing::included::IncludedTableStatic *add_included_table() {
+    CHECK(!included_table_.has_value());
+    constexpr size_t kVtableIndex = 22;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (aos::fbs::testing::included::IncludedTableStatic::
+                      kPreallocatedSize == 0) {
+      const size_t object_absolute_offset =
+          object_absolute_offset_included_table;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset,
+                      aos::fbs::testing::included::IncludedTableStatic::kSize,
+                      ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_included_table = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      included_table_.emplace(
+          BufferForObject(
+              object_absolute_offset_included_table,
+              aos::fbs::testing::included::IncludedTableStatic::kSize),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      included_table_.emplace(
+          BufferForObject(
+              object_absolute_offset_included_table,
+              aos::fbs::testing::included::IncludedTableStatic::kSize),
+          this);
+    }
     // Actually set the appropriate fields in the flatbuffer memory itself.
     SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_vector_aligned, kVtableIndex,
-        object_absolute_offset_vector_aligned +
-            ::aos::fbs::Vector<int32_t, 3, true, 64>::kOffset -
-            kInlineAbsoluteOffset_vector_aligned);
-    return &vector_aligned_.value().t;
+        kInlineAbsoluteOffset_included_table, kVtableIndex,
+        object_absolute_offset_included_table -
+            kInlineAbsoluteOffset_included_table);
+    return &included_table_.value().t;
   }
 
-  // Returns a pointer to the vector_aligned field, if set. nullptr otherwise.
-  const ::aos::fbs::Vector<int32_t, 3, true, 64> *vector_aligned() const {
-    return vector_aligned_.has_value() ? &vector_aligned_.value().t : nullptr;
+  // Returns a pointer to the included_table field, if set. nullptr otherwise.
+  const aos::fbs::testing::included::IncludedTableStatic *included_table()
+      const {
+    return included_table_.has_value() ? &included_table_.value().t : nullptr;
   }
-  ::aos::fbs::Vector<int32_t, 3, true, 64> *mutable_vector_aligned() {
-    return vector_aligned_.has_value() ? &vector_aligned_.value().t : nullptr;
+  aos::fbs::testing::included::IncludedTableStatic *mutable_included_table() {
+    return included_table_.has_value() ? &included_table_.value().t : nullptr;
   }
 
-  // Clears the vector_aligned field. This will cause has_vector_aligned() to
+  // Clears the included_table field. This will cause has_included_table() to
   // return false.
-  void clear_vector_aligned() {
-    vector_aligned_.reset();
-    ClearField(kInlineAbsoluteOffset_vector_aligned, 4, 16);
+  void clear_included_table() {
+    included_table_.reset();
+    ClearField(kInlineAbsoluteOffset_included_table, 4, 22);
   }
 
-  // Returns true if the vector_aligned field is set and can be accessed.
-  bool has_vector_aligned() const {
-    return AsFlatbuffer().has_vector_aligned();
+  // Returns true if the included_table field is set and can be accessed.
+  bool has_included_table() const {
+    return AsFlatbuffer().has_included_table();
   }
 
-  // Creates an empty object for the vector_of_scalars field, which you can
+  // Creates an empty object for the subtable field, which you can
   // then populate/modify as desired.
   // The field must not be populated yet.
-  ::aos::fbs::Vector<int32_t, 3, true, 0> *add_vector_of_scalars() {
-    CHECK(!vector_of_scalars_.has_value());
-    constexpr size_t kVtableIndex = 6;
-    // Construct the *Static object that we will use for managing this subtable.
-    vector_of_scalars_.emplace(
-        BufferForObject(object_absolute_offset_vector_of_scalars,
-                        ::aos::fbs::Vector<int32_t, 3, true, 0>::kSize, kAlign),
-        this);
+  aos::fbs::testing::SubTableStatic *add_subtable() {
+    CHECK(!subtable_.has_value());
+    constexpr size_t kVtableIndex = 14;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (aos::fbs::testing::SubTableStatic::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset = object_absolute_offset_subtable;
+      std::optional<std::span<uint8_t>> inserted_bytes = InsertBytes(
+          buffer().data() + object_absolute_offset,
+          aos::fbs::testing::SubTableStatic::kSize, ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_subtable = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      subtable_.emplace(
+          BufferForObject(object_absolute_offset_subtable,
+                          aos::fbs::testing::SubTableStatic::kSize),
+          this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      subtable_.emplace(
+          BufferForObject(object_absolute_offset_subtable,
+                          aos::fbs::testing::SubTableStatic::kSize),
+          this);
+    }
     // Actually set the appropriate fields in the flatbuffer memory itself.
     SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_vector_of_scalars, kVtableIndex,
-        object_absolute_offset_vector_of_scalars +
-            ::aos::fbs::Vector<int32_t, 3, true, 0>::kOffset -
-            kInlineAbsoluteOffset_vector_of_scalars);
-    return &vector_of_scalars_.value().t;
+        kInlineAbsoluteOffset_subtable, kVtableIndex,
+        object_absolute_offset_subtable - kInlineAbsoluteOffset_subtable);
+    return &subtable_.value().t;
   }
 
-  // Returns a pointer to the vector_of_scalars field, if set. nullptr
-  // otherwise.
-  const ::aos::fbs::Vector<int32_t, 3, true, 0> *vector_of_scalars() const {
-    return vector_of_scalars_.has_value() ? &vector_of_scalars_.value().t
-                                          : nullptr;
+  // Returns a pointer to the subtable field, if set. nullptr otherwise.
+  const aos::fbs::testing::SubTableStatic *subtable() const {
+    return subtable_.has_value() ? &subtable_.value().t : nullptr;
   }
-  ::aos::fbs::Vector<int32_t, 3, true, 0> *mutable_vector_of_scalars() {
-    return vector_of_scalars_.has_value() ? &vector_of_scalars_.value().t
-                                          : nullptr;
+  aos::fbs::testing::SubTableStatic *mutable_subtable() {
+    return subtable_.has_value() ? &subtable_.value().t : nullptr;
   }
 
-  // Clears the vector_of_scalars field. This will cause has_vector_of_scalars()
-  // to return false.
-  void clear_vector_of_scalars() {
-    vector_of_scalars_.reset();
-    ClearField(kInlineAbsoluteOffset_vector_of_scalars, 4, 6);
+  // Clears the subtable field. This will cause has_subtable() to return false.
+  void clear_subtable() {
+    subtable_.reset();
+    ClearField(kInlineAbsoluteOffset_subtable, 4, 14);
   }
 
-  // Returns true if the vector_of_scalars field is set and can be accessed.
-  bool has_vector_of_scalars() const {
-    return AsFlatbuffer().has_vector_of_scalars();
-  }
+  // Returns true if the subtable field is set and can be accessed.
+  bool has_subtable() const { return AsFlatbuffer().has_subtable(); }
 
-  // Creates an empty object for the vector_of_strings field, which you can
+  // Creates an empty object for the string field, which you can
   // then populate/modify as desired.
   // The field must not be populated yet.
-  ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0> *
-  add_vector_of_strings() {
-    CHECK(!vector_of_strings_.has_value());
-    constexpr size_t kVtableIndex = 10;
-    // Construct the *Static object that we will use for managing this subtable.
-    vector_of_strings_.emplace(
-        BufferForObject(
-            object_absolute_offset_vector_of_strings,
-            ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kSize,
-            kAlign),
-        this);
+  ::aos::fbs::String<20> *add_string() {
+    CHECK(!string_.has_value());
+    constexpr size_t kVtableIndex = 8;
+    // If this object does not normally have its initial memory statically
+    // allocated, allocate it now (this is used for zero-length vectors).
+    if constexpr (::aos::fbs::String<20>::kPreallocatedSize == 0) {
+      const size_t object_absolute_offset = object_absolute_offset_string;
+      std::optional<std::span<uint8_t>> inserted_bytes =
+          InsertBytes(buffer().data() + object_absolute_offset,
+                      ::aos::fbs::String<20>::kSize, ::aos::fbs::SetZero::kYes);
+      if (!inserted_bytes.has_value()) {
+        return nullptr;
+      }
+      // Undo changes to the object absolute offset that will have been made by
+      // the InsertBytes call.
+      // The InsertBytes() call normally goes through and "fixes" any offsets
+      // that will have been affected by the memory insertion. Unfortunately,
+      // if this object currently takes up zero bytes then the InsertBytes()
+      // cannot distinguish between this offset and the (identical) offsets for
+      // any other objects that may have been "sharing" this location. The
+      // effect of this logic is that the first object that gets populated at
+      // any given location will bump all other objects to later. This is fine,
+      // although it does mean that the order in which objects appear in memory
+      // may vary depending on the order in which they are constructed (if they
+      // start out sharing a start pointer).
+      object_absolute_offset_string = object_absolute_offset;
+
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      string_.emplace(BufferForObject(object_absolute_offset_string,
+                                      ::aos::fbs::String<20>::RoundedLength(
+                                          inserted_bytes.value().size())),
+                      this);
+    } else {
+      // Construct the *Static object that we will use for managing this
+      // subtable.
+      string_.emplace(BufferForObject(object_absolute_offset_string,
+                                      ::aos::fbs::String<20>::kSize),
+                      this);
+    }
     // Actually set the appropriate fields in the flatbuffer memory itself.
     SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_vector_of_strings, kVtableIndex,
-        object_absolute_offset_vector_of_strings +
-            ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kOffset -
-            kInlineAbsoluteOffset_vector_of_strings);
-    return &vector_of_strings_.value().t;
+        kInlineAbsoluteOffset_string, kVtableIndex,
+        object_absolute_offset_string - kInlineAbsoluteOffset_string);
+    return &string_.value().t;
   }
 
-  // Returns a pointer to the vector_of_strings field, if set. nullptr
-  // otherwise.
-  const ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0> *
-  vector_of_strings() const {
-    return vector_of_strings_.has_value() ? &vector_of_strings_.value().t
-                                          : nullptr;
+  // Returns a pointer to the string field, if set. nullptr otherwise.
+  const ::aos::fbs::String<20> *string() const {
+    return string_.has_value() ? &string_.value().t : nullptr;
   }
-  ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0> *
-  mutable_vector_of_strings() {
-    return vector_of_strings_.has_value() ? &vector_of_strings_.value().t
-                                          : nullptr;
+  ::aos::fbs::String<20> *mutable_string() {
+    return string_.has_value() ? &string_.value().t : nullptr;
   }
 
-  // Clears the vector_of_strings field. This will cause has_vector_of_strings()
-  // to return false.
-  void clear_vector_of_strings() {
-    vector_of_strings_.reset();
-    ClearField(kInlineAbsoluteOffset_vector_of_strings, 4, 10);
+  // Clears the string field. This will cause has_string() to return false.
+  void clear_string() {
+    string_.reset();
+    ClearField(kInlineAbsoluteOffset_string, 4, 8);
   }
 
-  // Returns true if the vector_of_strings field is set and can be accessed.
-  bool has_vector_of_strings() const {
-    return AsFlatbuffer().has_vector_of_strings();
+  // Returns true if the string field is set and can be accessed.
+  bool has_string() const { return AsFlatbuffer().has_string(); }
+
+  // Sets the scalar field, causing it to be populated if it is not already.
+  // This will populate the field even if the specified value is the default.
+  void set_scalar(const int32_t &value) {
+    SetField<int32_t>(kInlineAbsoluteOffset_scalar, 4, value);
   }
 
-  // Creates an empty object for the vector_of_structs field, which you can
-  // then populate/modify as desired.
-  // The field must not be populated yet.
-  ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0> *
-  add_vector_of_structs() {
-    CHECK(!vector_of_structs_.has_value());
-    constexpr size_t kVtableIndex = 18;
-    // Construct the *Static object that we will use for managing this subtable.
-    vector_of_structs_.emplace(
-        BufferForObject(
-            object_absolute_offset_vector_of_structs,
-            ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0>::kSize,
-            kAlign),
-        this);
-    // Actually set the appropriate fields in the flatbuffer memory itself.
-    SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_vector_of_structs, kVtableIndex,
-        object_absolute_offset_vector_of_structs +
-            ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true,
-                               0>::kOffset -
-            kInlineAbsoluteOffset_vector_of_structs);
-    return &vector_of_structs_.value().t;
+  // Returns the value of scalar if set; nullopt otherwise.
+  std::optional<int32_t> scalar() const {
+    return has_scalar()
+               ? std::make_optional(Get<int32_t>(kInlineAbsoluteOffset_scalar))
+               : std::nullopt;
+  }
+  // Returns a pointer to modify the scalar field.
+  // The pointer may be invalidated by mutations/movements of the underlying
+  // buffer. Returns nullptr if the field is not set.
+  int32_t *mutable_scalar() {
+    return has_scalar() ? MutableGet<int32_t>(kInlineAbsoluteOffset_scalar)
+                        : nullptr;
   }
 
-  // Returns a pointer to the vector_of_structs field, if set. nullptr
-  // otherwise.
-  const ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0> *
-  vector_of_structs() const {
-    return vector_of_structs_.has_value() ? &vector_of_structs_.value().t
-                                          : nullptr;
-  }
-  ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0> *
-  mutable_vector_of_structs() {
-    return vector_of_structs_.has_value() ? &vector_of_structs_.value().t
-                                          : nullptr;
-  }
+  // Clears the scalar field. This will cause has_scalar() to return false.
+  void clear_scalar() { ClearField(kInlineAbsoluteOffset_scalar, 4, 4); }
 
-  // Clears the vector_of_structs field. This will cause has_vector_of_structs()
-  // to return false.
-  void clear_vector_of_structs() {
-    vector_of_structs_.reset();
-    ClearField(kInlineAbsoluteOffset_vector_of_structs, 4, 18);
-  }
-
-  // Returns true if the vector_of_structs field is set and can be accessed.
-  bool has_vector_of_structs() const {
-    return AsFlatbuffer().has_vector_of_structs();
-  }
-
-  // Creates an empty object for the vector_of_tables field, which you can
-  // then populate/modify as desired.
-  // The field must not be populated yet.
-  ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false, 0> *
-  add_vector_of_tables() {
-    CHECK(!vector_of_tables_.has_value());
-    constexpr size_t kVtableIndex = 20;
-    // Construct the *Static object that we will use for managing this subtable.
-    vector_of_tables_.emplace(
-        BufferForObject(object_absolute_offset_vector_of_tables,
-                        ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3,
-                                           false, 0>::kSize,
-                        kAlign),
-        this);
-    // Actually set the appropriate fields in the flatbuffer memory itself.
-    SetField<::flatbuffers::uoffset_t>(
-        kInlineAbsoluteOffset_vector_of_tables, kVtableIndex,
-        object_absolute_offset_vector_of_tables +
-            ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false,
-                               0>::kOffset -
-            kInlineAbsoluteOffset_vector_of_tables);
-    return &vector_of_tables_.value().t;
-  }
-
-  // Returns a pointer to the vector_of_tables field, if set. nullptr otherwise.
-  const ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false, 0> *
-  vector_of_tables() const {
-    return vector_of_tables_.has_value() ? &vector_of_tables_.value().t
-                                         : nullptr;
-  }
-  ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false, 0> *
-  mutable_vector_of_tables() {
-    return vector_of_tables_.has_value() ? &vector_of_tables_.value().t
-                                         : nullptr;
-  }
-
-  // Clears the vector_of_tables field. This will cause has_vector_of_tables()
-  // to return false.
-  void clear_vector_of_tables() {
-    vector_of_tables_.reset();
-    ClearField(kInlineAbsoluteOffset_vector_of_tables, 4, 20);
-  }
-
-  // Returns true if the vector_of_tables field is set and can be accessed.
-  bool has_vector_of_tables() const {
-    return AsFlatbuffer().has_vector_of_tables();
-  }
+  // Returns true if the scalar field is set and can be accessed.
+  bool has_scalar() const { return AsFlatbuffer().has_scalar(); }
 
   // Clears every field of the table, removing any existing state.
   void Clear() {
-    clear_included_table();
-    clear_scalar();
-    clear_string();
     clear_substruct();
-    clear_subtable();
+    clear_vector_of_structs();
+    clear_unspecified_length_vector_of_strings();
+    clear_vector_of_tables();
+    clear_vector_aligned();
+    clear_vector_of_strings();
+    clear_vector_of_scalars();
     clear_unspecified_length_string();
     clear_unspecified_length_vector();
-    clear_unspecified_length_vector_of_strings();
-    clear_vector_aligned();
-    clear_vector_of_scalars();
-    clear_vector_of_strings();
-    clear_vector_of_structs();
-    clear_vector_of_tables();
+    clear_included_table();
+    clear_subtable();
+    clear_string();
+    clear_scalar();
   }
 
   // Copies the contents of the provided flatbuffer into this flatbuffer,
@@ -1119,33 +1455,58 @@
   [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
     Clear();
 
-    if (other.has_included_table()) {
-      if (!CHECK_NOTNULL(add_included_table())
-               ->FromFlatbuffer(other.included_table())) {
-        // Fail if we were unable to copy (e.g., if we tried to copy in a long
-        // vector and do not have the space for it).
-        return false;
-      }
-    }
-
-    if (other.has_scalar()) {
-      set_scalar(other.scalar());
-    }
-
-    if (other.has_string()) {
-      if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string())) {
-        // Fail if we were unable to copy (e.g., if we tried to copy in a long
-        // vector and do not have the space for it).
-        return false;
-      }
-    }
-
     if (other.has_substruct()) {
       set_substruct(*other.substruct());
     }
 
-    if (other.has_subtable()) {
-      if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(other.subtable())) {
+    if (other.has_vector_of_structs()) {
+      if (!CHECK_NOTNULL(add_vector_of_structs())
+               ->FromFlatbuffer(other.vector_of_structs())) {
+        // Fail if we were unable to copy (e.g., if we tried to copy in a long
+        // vector and do not have the space for it).
+        return false;
+      }
+    }
+
+    if (other.has_unspecified_length_vector_of_strings()) {
+      if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
+               ->FromFlatbuffer(other.unspecified_length_vector_of_strings())) {
+        // Fail if we were unable to copy (e.g., if we tried to copy in a long
+        // vector and do not have the space for it).
+        return false;
+      }
+    }
+
+    if (other.has_vector_of_tables()) {
+      if (!CHECK_NOTNULL(add_vector_of_tables())
+               ->FromFlatbuffer(other.vector_of_tables())) {
+        // Fail if we were unable to copy (e.g., if we tried to copy in a long
+        // vector and do not have the space for it).
+        return false;
+      }
+    }
+
+    if (other.has_vector_aligned()) {
+      if (!CHECK_NOTNULL(add_vector_aligned())
+               ->FromFlatbuffer(other.vector_aligned())) {
+        // Fail if we were unable to copy (e.g., if we tried to copy in a long
+        // vector and do not have the space for it).
+        return false;
+      }
+    }
+
+    if (other.has_vector_of_strings()) {
+      if (!CHECK_NOTNULL(add_vector_of_strings())
+               ->FromFlatbuffer(other.vector_of_strings())) {
+        // Fail if we were unable to copy (e.g., if we tried to copy in a long
+        // vector and do not have the space for it).
+        return false;
+      }
+    }
+
+    if (other.has_vector_of_scalars()) {
+      if (!CHECK_NOTNULL(add_vector_of_scalars())
+               ->FromFlatbuffer(other.vector_of_scalars())) {
         // Fail if we were unable to copy (e.g., if we tried to copy in a long
         // vector and do not have the space for it).
         return false;
@@ -1170,58 +1531,33 @@
       }
     }
 
-    if (other.has_unspecified_length_vector_of_strings()) {
-      if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
-               ->FromFlatbuffer(other.unspecified_length_vector_of_strings())) {
+    if (other.has_included_table()) {
+      if (!CHECK_NOTNULL(add_included_table())
+               ->FromFlatbuffer(other.included_table())) {
         // Fail if we were unable to copy (e.g., if we tried to copy in a long
         // vector and do not have the space for it).
         return false;
       }
     }
 
-    if (other.has_vector_aligned()) {
-      if (!CHECK_NOTNULL(add_vector_aligned())
-               ->FromFlatbuffer(other.vector_aligned())) {
+    if (other.has_subtable()) {
+      if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(other.subtable())) {
         // Fail if we were unable to copy (e.g., if we tried to copy in a long
         // vector and do not have the space for it).
         return false;
       }
     }
 
-    if (other.has_vector_of_scalars()) {
-      if (!CHECK_NOTNULL(add_vector_of_scalars())
-               ->FromFlatbuffer(other.vector_of_scalars())) {
+    if (other.has_string()) {
+      if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string())) {
         // Fail if we were unable to copy (e.g., if we tried to copy in a long
         // vector and do not have the space for it).
         return false;
       }
     }
 
-    if (other.has_vector_of_strings()) {
-      if (!CHECK_NOTNULL(add_vector_of_strings())
-               ->FromFlatbuffer(other.vector_of_strings())) {
-        // Fail if we were unable to copy (e.g., if we tried to copy in a long
-        // vector and do not have the space for it).
-        return false;
-      }
-    }
-
-    if (other.has_vector_of_structs()) {
-      if (!CHECK_NOTNULL(add_vector_of_structs())
-               ->FromFlatbuffer(other.vector_of_structs())) {
-        // Fail if we were unable to copy (e.g., if we tried to copy in a long
-        // vector and do not have the space for it).
-        return false;
-      }
-    }
-
-    if (other.has_vector_of_tables()) {
-      if (!CHECK_NOTNULL(add_vector_of_tables())
-               ->FromFlatbuffer(other.vector_of_tables())) {
-        // Fail if we were unable to copy (e.g., if we tried to copy in a long
-        // vector and do not have the space for it).
-        return false;
-      }
+    if (other.has_scalar()) {
+      set_scalar(other.scalar());
     }
 
     return true;
@@ -1242,36 +1578,68 @@
   [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
     Clear();
 
-    if (other.included_table) {
-      if (!CHECK_NOTNULL(add_included_table())
-               ->FromFlatbuffer(*other.included_table)) {
-        // Fail if we were unable to copy (e.g., if we tried to copy in a long
-        // vector and do not have the space for it).
-        return false;
-      }
+    if (other.substruct) {
+      set_substruct(*other.substruct);
     }
 
-    set_scalar(other.scalar);
-
     // Unconditionally copy strings/vectors, even if it will just end up
     // being 0-length (this maintains consistency with the flatbuffer Pack()
     // behavior).
-    if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string)) {
+    if (!CHECK_NOTNULL(add_vector_of_structs())
+             ->FromFlatbuffer(other.vector_of_structs)) {
       // Fail if we were unable to copy (e.g., if we tried to copy in a long
       // vector and do not have the space for it).
       return false;
     }
 
-    if (other.substruct) {
-      set_substruct(*other.substruct);
+    // Unconditionally copy strings/vectors, even if it will just end up
+    // being 0-length (this maintains consistency with the flatbuffer Pack()
+    // behavior).
+    if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
+             ->FromFlatbuffer(other.unspecified_length_vector_of_strings)) {
+      // Fail if we were unable to copy (e.g., if we tried to copy in a long
+      // vector and do not have the space for it).
+      return false;
     }
 
-    if (other.subtable) {
-      if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(*other.subtable)) {
-        // Fail if we were unable to copy (e.g., if we tried to copy in a long
-        // vector and do not have the space for it).
-        return false;
-      }
+    // Unconditionally copy strings/vectors, even if it will just end up
+    // being 0-length (this maintains consistency with the flatbuffer Pack()
+    // behavior).
+    if (!CHECK_NOTNULL(add_vector_of_tables())
+             ->FromFlatbuffer(other.vector_of_tables)) {
+      // Fail if we were unable to copy (e.g., if we tried to copy in a long
+      // vector and do not have the space for it).
+      return false;
+    }
+
+    // Unconditionally copy strings/vectors, even if it will just end up
+    // being 0-length (this maintains consistency with the flatbuffer Pack()
+    // behavior).
+    if (!CHECK_NOTNULL(add_vector_aligned())
+             ->FromFlatbuffer(other.vector_aligned)) {
+      // Fail if we were unable to copy (e.g., if we tried to copy in a long
+      // vector and do not have the space for it).
+      return false;
+    }
+
+    // Unconditionally copy strings/vectors, even if it will just end up
+    // being 0-length (this maintains consistency with the flatbuffer Pack()
+    // behavior).
+    if (!CHECK_NOTNULL(add_vector_of_strings())
+             ->FromFlatbuffer(other.vector_of_strings)) {
+      // Fail if we were unable to copy (e.g., if we tried to copy in a long
+      // vector and do not have the space for it).
+      return false;
+    }
+
+    // Unconditionally copy strings/vectors, even if it will just end up
+    // being 0-length (this maintains consistency with the flatbuffer Pack()
+    // behavior).
+    if (!CHECK_NOTNULL(add_vector_of_scalars())
+             ->FromFlatbuffer(other.vector_of_scalars)) {
+      // Fail if we were unable to copy (e.g., if we tried to copy in a long
+      // vector and do not have the space for it).
+      return false;
     }
 
     // Unconditionally copy strings/vectors, even if it will just end up
@@ -1294,65 +1662,33 @@
       return false;
     }
 
-    // Unconditionally copy strings/vectors, even if it will just end up
-    // being 0-length (this maintains consistency with the flatbuffer Pack()
-    // behavior).
-    if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
-             ->FromFlatbuffer(other.unspecified_length_vector_of_strings)) {
-      // Fail if we were unable to copy (e.g., if we tried to copy in a long
-      // vector and do not have the space for it).
-      return false;
+    if (other.included_table) {
+      if (!CHECK_NOTNULL(add_included_table())
+               ->FromFlatbuffer(*other.included_table)) {
+        // Fail if we were unable to copy (e.g., if we tried to copy in a long
+        // vector and do not have the space for it).
+        return false;
+      }
+    }
+
+    if (other.subtable) {
+      if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(*other.subtable)) {
+        // Fail if we were unable to copy (e.g., if we tried to copy in a long
+        // vector and do not have the space for it).
+        return false;
+      }
     }
 
     // Unconditionally copy strings/vectors, even if it will just end up
     // being 0-length (this maintains consistency with the flatbuffer Pack()
     // behavior).
-    if (!CHECK_NOTNULL(add_vector_aligned())
-             ->FromFlatbuffer(other.vector_aligned)) {
+    if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string)) {
       // Fail if we were unable to copy (e.g., if we tried to copy in a long
       // vector and do not have the space for it).
       return false;
     }
 
-    // Unconditionally copy strings/vectors, even if it will just end up
-    // being 0-length (this maintains consistency with the flatbuffer Pack()
-    // behavior).
-    if (!CHECK_NOTNULL(add_vector_of_scalars())
-             ->FromFlatbuffer(other.vector_of_scalars)) {
-      // Fail if we were unable to copy (e.g., if we tried to copy in a long
-      // vector and do not have the space for it).
-      return false;
-    }
-
-    // Unconditionally copy strings/vectors, even if it will just end up
-    // being 0-length (this maintains consistency with the flatbuffer Pack()
-    // behavior).
-    if (!CHECK_NOTNULL(add_vector_of_strings())
-             ->FromFlatbuffer(other.vector_of_strings)) {
-      // Fail if we were unable to copy (e.g., if we tried to copy in a long
-      // vector and do not have the space for it).
-      return false;
-    }
-
-    // Unconditionally copy strings/vectors, even if it will just end up
-    // being 0-length (this maintains consistency with the flatbuffer Pack()
-    // behavior).
-    if (!CHECK_NOTNULL(add_vector_of_structs())
-             ->FromFlatbuffer(other.vector_of_structs)) {
-      // Fail if we were unable to copy (e.g., if we tried to copy in a long
-      // vector and do not have the space for it).
-      return false;
-    }
-
-    // Unconditionally copy strings/vectors, even if it will just end up
-    // being 0-length (this maintains consistency with the flatbuffer Pack()
-    // behavior).
-    if (!CHECK_NOTNULL(add_vector_of_tables())
-             ->FromFlatbuffer(other.vector_of_tables)) {
-      // Fail if we were unable to copy (e.g., if we tried to copy in a long
-      // vector and do not have the space for it).
-      return false;
-    }
+    set_scalar(other.scalar);
 
     return true;
   }
@@ -1368,286 +1704,9 @@
   TestTableStatic(TestTableStatic &&) = default;
   friend struct ::aos::fbs::internal::TableMover<TestTableStatic>;
 
-  // Members relating to the included_table field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<::aos::fbs::internal::TableMover<
-      aos::fbs::testing::included::IncludedTableStatic>>
-      included_table_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_included_table =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(0, kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_included_table = 4;
-
-  // Offset from the start of the buffer to the inline data for the scalar
-  // field.
-  static constexpr size_t kInlineAbsoluteOffset_scalar = 8;
-
-  // Members relating to the string field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<::aos::fbs::internal::TableMover<::aos::fbs::String<20>>>
-      string_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_string =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(0, kAlign) +
-              aos::fbs::testing::included::IncludedTableStatic::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_string = 12;
-
   // Offset from the start of the buffer to the inline data for the substruct
   // field.
-  static constexpr size_t kInlineAbsoluteOffset_substruct = 16;
-
-  // Members relating to the subtable field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<
-      ::aos::fbs::internal::TableMover<aos::fbs::testing::SubTableStatic>>
-      subtable_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_subtable =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(0, kAlign) +
-                  aos::fbs::testing::included::IncludedTableStatic::kSize,
-              kAlign) +
-              ::aos::fbs::String<20>::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_subtable = 32;
-
-  // Members relating to the unspecified_length_string field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<::aos::fbs::internal::TableMover<::aos::fbs::String<0>>>
-      unspecified_length_string_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_unspecified_length_string =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(0, kAlign) +
-                      aos::fbs::testing::included::IncludedTableStatic::kSize,
-                  kAlign) +
-                  ::aos::fbs::String<20>::kSize,
-              kAlign) +
-              aos::fbs::testing::SubTableStatic::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_unspecified_length_string = 36;
-
-  // Members relating to the unspecified_length_vector field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<
-      ::aos::fbs::internal::TableMover<::aos::fbs::Vector<uint8_t, 0, true, 0>>>
-      unspecified_length_vector_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_unspecified_length_vector =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(::aos::fbs::PaddedSize(0, kAlign) +
-                                             aos::fbs::testing::included::
-                                                 IncludedTableStatic::kSize,
-                                         kAlign) +
-                      ::aos::fbs::String<20>::kSize,
-                  kAlign) +
-                  aos::fbs::testing::SubTableStatic::kSize,
-              kAlign) +
-              ::aos::fbs::String<0>::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_unspecified_length_vector = 40;
-
-  // Members relating to the unspecified_length_vector_of_strings field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<::aos::fbs::internal::TableMover<
-      ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>>>
-      unspecified_length_vector_of_strings_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_unspecified_length_vector_of_strings =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(
-                      ::aos::fbs::PaddedSize(::aos::fbs::PaddedSize(0, kAlign) +
-                                                 aos::fbs::testing::included::
-                                                     IncludedTableStatic::kSize,
-                                             kAlign) +
-                          ::aos::fbs::String<20>::kSize,
-                      kAlign) +
-                      aos::fbs::testing::SubTableStatic::kSize,
-                  kAlign) +
-                  ::aos::fbs::String<0>::kSize,
-              kAlign) +
-              ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t
-      kInlineAbsoluteOffset_unspecified_length_vector_of_strings = 44;
-
-  // Members relating to the vector_aligned field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<::aos::fbs::internal::TableMover<
-      ::aos::fbs::Vector<int32_t, 3, true, 64>>>
-      vector_aligned_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_vector_aligned =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(
-                      ::aos::fbs::PaddedSize(
-                          ::aos::fbs::PaddedSize(
-                              ::aos::fbs::PaddedSize(0, kAlign) +
-                                  aos::fbs::testing::included::
-                                      IncludedTableStatic::kSize,
-                              kAlign) +
-                              ::aos::fbs::String<20>::kSize,
-                          kAlign) +
-                          aos::fbs::testing::SubTableStatic::kSize,
-                      kAlign) +
-                      ::aos::fbs::String<0>::kSize,
-                  kAlign) +
-                  ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize,
-              kAlign) +
-              ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_vector_aligned = 48;
-
-  // Members relating to the vector_of_scalars field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<
-      ::aos::fbs::internal::TableMover<::aos::fbs::Vector<int32_t, 3, true, 0>>>
-      vector_of_scalars_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_vector_of_scalars =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(
-                      ::aos::fbs::PaddedSize(
-                          ::aos::fbs::PaddedSize(
-                              ::aos::fbs::PaddedSize(
-                                  ::aos::fbs::PaddedSize(0, kAlign) +
-                                      aos::fbs::testing::included::
-                                          IncludedTableStatic::kSize,
-                                  kAlign) +
-                                  ::aos::fbs::String<20>::kSize,
-                              kAlign) +
-                              aos::fbs::testing::SubTableStatic::kSize,
-                          kAlign) +
-                          ::aos::fbs::String<0>::kSize,
-                      kAlign) +
-                      ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize,
-                  kAlign) +
-                  ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kSize,
-              kAlign) +
-              ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_vector_of_scalars = 52;
-
-  // Members relating to the vector_of_strings field.
-  //
-  // *Static object used for managing this subtable. Will be nullopt
-  // when the field is not populated.
-  // We use the TableMover to be able to make this object moveable.
-  std::optional<::aos::fbs::internal::TableMover<
-      ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>>>
-      vector_of_strings_;
-  // Offset from the start of the buffer to the start of the actual
-  // data for this field. Will be updated even when the table is not
-  // populated, so that we know where to construct it when requested.
-  size_t object_absolute_offset_vector_of_strings =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(
-                      ::aos::fbs::PaddedSize(
-                          ::aos::fbs::PaddedSize(
-                              ::aos::fbs::PaddedSize(
-                                  ::aos::fbs::PaddedSize(
-                                      ::aos::fbs::PaddedSize(0, kAlign) +
-                                          aos::fbs::testing::included::
-                                              IncludedTableStatic::kSize,
-                                      kAlign) +
-                                      ::aos::fbs::String<20>::kSize,
-                                  kAlign) +
-                                  aos::fbs::testing::SubTableStatic::kSize,
-                              kAlign) +
-                              ::aos::fbs::String<0>::kSize,
-                          kAlign) +
-                          ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize,
-                      kAlign) +
-                      ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false,
-                                         0>::kSize,
-                  kAlign) +
-                  ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize,
-              kAlign) +
-              ::aos::fbs::Vector<int32_t, 3, true, 0>::kSize,
-          kAlign);
-  // Offset from the start of the buffer to the offset in the inline data for
-  // this field.
-  static constexpr size_t kInlineAbsoluteOffset_vector_of_strings = 56;
+  static constexpr size_t kInlineAbsoluteOffset_substruct = 4;
 
   // Members relating to the vector_of_structs field.
   //
@@ -1660,41 +1719,47 @@
   // Offset from the start of the buffer to the start of the actual
   // data for this field. Will be updated even when the table is not
   // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetvector_of_structs =
+      ::aos::fbs::AlignOffset(
+          (kVtableStart + kVtableSize) - kAlignOffset,
+          ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true, 0>::kAlign,
+          ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true,
+                             0>::kAlignOffset) +
+      kAlignOffset;
   size_t object_absolute_offset_vector_of_structs =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(
-                      ::aos::fbs::PaddedSize(
-                          ::aos::fbs::PaddedSize(
-                              ::aos::fbs::PaddedSize(
-                                  ::aos::fbs::PaddedSize(
-                                      ::aos::fbs::PaddedSize(
-                                          ::aos::fbs::PaddedSize(0, kAlign) +
-                                              aos::fbs::testing::included::
-                                                  IncludedTableStatic::kSize,
-                                          kAlign) +
-                                          ::aos::fbs::String<20>::kSize,
-                                      kAlign) +
-                                      aos::fbs::testing::SubTableStatic::kSize,
-                                  kAlign) +
-                                  ::aos::fbs::String<0>::kSize,
-                              kAlign) +
-                              ::aos::fbs::Vector<uint8_t, 0, true, 0>::kSize,
-                          kAlign) +
-                          ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false,
-                                             0>::kSize,
-                      kAlign) +
-                      ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize,
-                  kAlign) +
-                  ::aos::fbs::Vector<int32_t, 3, true, 0>::kSize,
-              kAlign) +
-              ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kSize,
-          kAlign);
+      kDefaultObjectAbsoluteOffsetvector_of_structs;
   // Offset from the start of the buffer to the offset in the inline data for
   // this field.
-  static constexpr size_t kInlineAbsoluteOffset_vector_of_structs = 60;
+  static constexpr size_t kInlineAbsoluteOffset_vector_of_structs = 20;
+
+  // Members relating to the unspecified_length_vector_of_strings field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<::aos::fbs::internal::TableMover<
+      ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>>>
+      unspecified_length_vector_of_strings_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t
+      kDefaultObjectAbsoluteOffsetunspecified_length_vector_of_strings =
+          ::aos::fbs::AlignOffset(
+              kDefaultObjectAbsoluteOffsetvector_of_structs +
+                  ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true,
+                                     0>::kPreallocatedSize -
+                  kAlignOffset,
+              ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false, 0>::kAlign,
+              ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false,
+                                 0>::kAlignOffset) +
+          kAlignOffset;
+  size_t object_absolute_offset_unspecified_length_vector_of_strings =
+      kDefaultObjectAbsoluteOffsetunspecified_length_vector_of_strings;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t
+      kInlineAbsoluteOffset_unspecified_length_vector_of_strings = 24;
 
   // Members relating to the vector_of_tables field.
   //
@@ -1707,50 +1772,226 @@
   // Offset from the start of the buffer to the start of the actual
   // data for this field. Will be updated even when the table is not
   // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetvector_of_tables =
+      ::aos::fbs::AlignOffset(
+          kDefaultObjectAbsoluteOffsetunspecified_length_vector_of_strings +
+              ::aos::fbs::Vector<::aos::fbs::String<0>, 0, false,
+                                 0>::kPreallocatedSize -
+              kAlignOffset,
+          ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false,
+                             0>::kAlign,
+          ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false,
+                             0>::kAlignOffset) +
+      kAlignOffset;
   size_t object_absolute_offset_vector_of_tables =
-      ::aos::fbs::PaddedSize(kVtableStart + kVtableSize, kAlign) +
-      ::aos::fbs::PaddedSize(
-          ::aos::fbs::PaddedSize(
-              ::aos::fbs::PaddedSize(
-                  ::aos::fbs::PaddedSize(
-                      ::aos::fbs::PaddedSize(
-                          ::aos::fbs::PaddedSize(
-                              ::aos::fbs::PaddedSize(
-                                  ::aos::fbs::PaddedSize(
-                                      ::aos::fbs::PaddedSize(
-                                          ::aos::fbs::PaddedSize(
-                                              ::aos::fbs::PaddedSize(0,
-                                                                     kAlign) +
-                                                  aos::fbs::testing::included::
-                                                      IncludedTableStatic::
-                                                          kSize,
-                                              kAlign) +
-                                              ::aos::fbs::String<20>::kSize,
-                                          kAlign) +
-                                          aos::fbs::testing::SubTableStatic::
-                                              kSize,
-                                      kAlign) +
-                                      ::aos::fbs::String<0>::kSize,
-                                  kAlign) +
-                                  ::aos::fbs::Vector<uint8_t, 0, true,
-                                                     0>::kSize,
-                              kAlign) +
-                              ::aos::fbs::Vector<::aos::fbs::String<0>, 0,
-                                                 false, 0>::kSize,
-                          kAlign) +
-                          ::aos::fbs::Vector<int32_t, 3, true, 64>::kSize,
-                      kAlign) +
-                      ::aos::fbs::Vector<int32_t, 3, true, 0>::kSize,
-                  kAlign) +
-                  ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false,
-                                     0>::kSize,
-              kAlign) +
-              ::aos::fbs::Vector<aos::fbs::testing::SubStruct, 3, true,
-                                 0>::kSize,
-          kAlign);
+      kDefaultObjectAbsoluteOffsetvector_of_tables;
   // Offset from the start of the buffer to the offset in the inline data for
   // this field.
-  static constexpr size_t kInlineAbsoluteOffset_vector_of_tables = 64;
+  static constexpr size_t kInlineAbsoluteOffset_vector_of_tables = 28;
+
+  // Members relating to the vector_aligned field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<::aos::fbs::internal::TableMover<
+      ::aos::fbs::Vector<int32_t, 3, true, 64>>>
+      vector_aligned_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetvector_aligned =
+      ::aos::fbs::AlignOffset(
+          kDefaultObjectAbsoluteOffsetvector_of_tables +
+              ::aos::fbs::Vector<aos::fbs::testing::SubTableStatic, 3, false,
+                                 0>::kPreallocatedSize -
+              kAlignOffset,
+          ::aos::fbs::Vector<int32_t, 3, true, 64>::kAlign,
+          ::aos::fbs::Vector<int32_t, 3, true, 64>::kAlignOffset) +
+      kAlignOffset;
+  size_t object_absolute_offset_vector_aligned =
+      kDefaultObjectAbsoluteOffsetvector_aligned;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_vector_aligned = 32;
+
+  // Members relating to the vector_of_strings field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<::aos::fbs::internal::TableMover<
+      ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>>>
+      vector_of_strings_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetvector_of_strings =
+      ::aos::fbs::AlignOffset(
+          kDefaultObjectAbsoluteOffsetvector_aligned +
+              ::aos::fbs::Vector<int32_t, 3, true, 64>::kPreallocatedSize -
+              kAlignOffset,
+          ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false, 0>::kAlign,
+          ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false,
+                             0>::kAlignOffset) +
+      kAlignOffset;
+  size_t object_absolute_offset_vector_of_strings =
+      kDefaultObjectAbsoluteOffsetvector_of_strings;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_vector_of_strings = 36;
+
+  // Members relating to the vector_of_scalars field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<
+      ::aos::fbs::internal::TableMover<::aos::fbs::Vector<int32_t, 3, true, 0>>>
+      vector_of_scalars_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetvector_of_scalars =
+      ::aos::fbs::AlignOffset(
+          kDefaultObjectAbsoluteOffsetvector_of_strings +
+              ::aos::fbs::Vector<::aos::fbs::String<10>, 3, false,
+                                 0>::kPreallocatedSize -
+              kAlignOffset,
+          ::aos::fbs::Vector<int32_t, 3, true, 0>::kAlign,
+          ::aos::fbs::Vector<int32_t, 3, true, 0>::kAlignOffset) +
+      kAlignOffset;
+  size_t object_absolute_offset_vector_of_scalars =
+      kDefaultObjectAbsoluteOffsetvector_of_scalars;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_vector_of_scalars = 40;
+
+  // Members relating to the unspecified_length_string field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<::aos::fbs::internal::TableMover<::aos::fbs::String<0>>>
+      unspecified_length_string_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t
+      kDefaultObjectAbsoluteOffsetunspecified_length_string =
+          ::aos::fbs::AlignOffset(
+              kDefaultObjectAbsoluteOffsetvector_of_scalars +
+                  ::aos::fbs::Vector<int32_t, 3, true, 0>::kPreallocatedSize -
+                  kAlignOffset,
+              ::aos::fbs::String<0>::kAlign,
+              ::aos::fbs::String<0>::kAlignOffset) +
+          kAlignOffset;
+  size_t object_absolute_offset_unspecified_length_string =
+      kDefaultObjectAbsoluteOffsetunspecified_length_string;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_unspecified_length_string = 44;
+
+  // Members relating to the unspecified_length_vector field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<
+      ::aos::fbs::internal::TableMover<::aos::fbs::Vector<uint8_t, 0, true, 0>>>
+      unspecified_length_vector_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t
+      kDefaultObjectAbsoluteOffsetunspecified_length_vector =
+          ::aos::fbs::AlignOffset(
+              kDefaultObjectAbsoluteOffsetunspecified_length_string +
+                  ::aos::fbs::String<0>::kPreallocatedSize - kAlignOffset,
+              ::aos::fbs::Vector<uint8_t, 0, true, 0>::kAlign,
+              ::aos::fbs::Vector<uint8_t, 0, true, 0>::kAlignOffset) +
+          kAlignOffset;
+  size_t object_absolute_offset_unspecified_length_vector =
+      kDefaultObjectAbsoluteOffsetunspecified_length_vector;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_unspecified_length_vector = 48;
+
+  // Members relating to the included_table field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<::aos::fbs::internal::TableMover<
+      aos::fbs::testing::included::IncludedTableStatic>>
+      included_table_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetincluded_table =
+      ::aos::fbs::AlignOffset(
+          kDefaultObjectAbsoluteOffsetunspecified_length_vector +
+              ::aos::fbs::Vector<uint8_t, 0, true, 0>::kPreallocatedSize -
+              kAlignOffset,
+          aos::fbs::testing::included::IncludedTableStatic::kAlign,
+          aos::fbs::testing::included::IncludedTableStatic::kAlignOffset) +
+      kAlignOffset;
+  size_t object_absolute_offset_included_table =
+      kDefaultObjectAbsoluteOffsetincluded_table;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_included_table = 52;
+
+  // Members relating to the subtable field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<
+      ::aos::fbs::internal::TableMover<aos::fbs::testing::SubTableStatic>>
+      subtable_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetsubtable =
+      ::aos::fbs::AlignOffset(kDefaultObjectAbsoluteOffsetincluded_table +
+                                  aos::fbs::testing::included::
+                                      IncludedTableStatic::kPreallocatedSize -
+                                  kAlignOffset,
+                              aos::fbs::testing::SubTableStatic::kAlign,
+                              aos::fbs::testing::SubTableStatic::kAlignOffset) +
+      kAlignOffset;
+  size_t object_absolute_offset_subtable = kDefaultObjectAbsoluteOffsetsubtable;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_subtable = 56;
+
+  // Members relating to the string field.
+  //
+  // *Static object used for managing this subtable. Will be nullopt
+  // when the field is not populated.
+  // We use the TableMover to be able to make this object moveable.
+  std::optional<::aos::fbs::internal::TableMover<::aos::fbs::String<20>>>
+      string_;
+  // Offset from the start of the buffer to the start of the actual
+  // data for this field. Will be updated even when the table is not
+  // populated, so that we know where to construct it when requested.
+  static constexpr size_t kDefaultObjectAbsoluteOffsetstring =
+      ::aos::fbs::AlignOffset(
+          kDefaultObjectAbsoluteOffsetsubtable +
+              aos::fbs::testing::SubTableStatic::kPreallocatedSize -
+              kAlignOffset,
+          ::aos::fbs::String<20>::kAlign,
+          ::aos::fbs::String<20>::kAlignOffset) +
+      kAlignOffset;
+  size_t object_absolute_offset_string = kDefaultObjectAbsoluteOffsetstring;
+  // Offset from the start of the buffer to the offset in the inline data for
+  // this field.
+  static constexpr size_t kInlineAbsoluteOffset_string = 60;
+
+  // Offset from the start of the buffer to the inline data for the scalar
+  // field.
+  static constexpr size_t kInlineAbsoluteOffset_scalar = 64;
 
   size_t NumberOfSubObjects() const final { return 11; }
   using ::aos::fbs::ResizeableObject::SubObject;
@@ -1764,48 +2005,48 @@
     // Pointers because these may need to be modified when memory is
     // inserted into the buffer.
     const std::array<size_t *, 11> subobject_object_offsets{
-        &object_absolute_offset_included_table,
-        &object_absolute_offset_string,
-        &object_absolute_offset_subtable,
+        &object_absolute_offset_vector_of_structs,
+        &object_absolute_offset_unspecified_length_vector_of_strings,
+        &object_absolute_offset_vector_of_tables,
+        &object_absolute_offset_vector_aligned,
+        &object_absolute_offset_vector_of_strings,
+        &object_absolute_offset_vector_of_scalars,
         &object_absolute_offset_unspecified_length_string,
         &object_absolute_offset_unspecified_length_vector,
-        &object_absolute_offset_unspecified_length_vector_of_strings,
-        &object_absolute_offset_vector_aligned,
-        &object_absolute_offset_vector_of_scalars,
-        &object_absolute_offset_vector_of_strings,
-        &object_absolute_offset_vector_of_structs,
-        &object_absolute_offset_vector_of_tables};
+        &object_absolute_offset_included_table,
+        &object_absolute_offset_subtable,
+        &object_absolute_offset_string};
     // Actual subobjects; note that the pointers will be invalid when the
     // field is not populated.
     const std::array<::aos::fbs::ResizeableObject *, 11> subobject_objects{
-        &included_table_->t,
-        &string_->t,
-        &subtable_->t,
+        &vector_of_structs_->t,
+        &unspecified_length_vector_of_strings_->t,
+        &vector_of_tables_->t,
+        &vector_aligned_->t,
+        &vector_of_strings_->t,
+        &vector_of_scalars_->t,
         &unspecified_length_string_->t,
         &unspecified_length_vector_->t,
-        &unspecified_length_vector_of_strings_->t,
-        &vector_aligned_->t,
-        &vector_of_scalars_->t,
-        &vector_of_strings_->t,
-        &vector_of_structs_->t,
-        &vector_of_tables_->t};
+        &included_table_->t,
+        &subtable_->t,
+        &string_->t};
     // Absolute offsets from the start of the buffer to where the inline
     // entry is for each table. These offsets do not need to change at
     // runtime (because memory is never inserted into the start of
     // a given table), but the offsets pointed to by these offsets
     // may need to be updated.
     const std::array<size_t, 11> subobject_inline_offsets{
-        kInlineAbsoluteOffset_included_table,
-        kInlineAbsoluteOffset_string,
-        kInlineAbsoluteOffset_subtable,
+        kInlineAbsoluteOffset_vector_of_structs,
+        kInlineAbsoluteOffset_unspecified_length_vector_of_strings,
+        kInlineAbsoluteOffset_vector_of_tables,
+        kInlineAbsoluteOffset_vector_aligned,
+        kInlineAbsoluteOffset_vector_of_strings,
+        kInlineAbsoluteOffset_vector_of_scalars,
         kInlineAbsoluteOffset_unspecified_length_string,
         kInlineAbsoluteOffset_unspecified_length_vector,
-        kInlineAbsoluteOffset_unspecified_length_vector_of_strings,
-        kInlineAbsoluteOffset_vector_aligned,
-        kInlineAbsoluteOffset_vector_of_scalars,
-        kInlineAbsoluteOffset_vector_of_strings,
-        kInlineAbsoluteOffset_vector_of_structs,
-        kInlineAbsoluteOffset_vector_of_tables};
+        kInlineAbsoluteOffset_included_table,
+        kInlineAbsoluteOffset_subtable,
+        kInlineAbsoluteOffset_string};
     object.inline_entry =
         MutableGet<::flatbuffers::uoffset_t>(subobject_inline_offsets[index]);
     object.object =
@@ -1813,5 +2054,22 @@
     object.absolute_offset = subobject_object_offsets[index];
     return object;
   }
+
+ public:
+  // Nominal size of this object, in bytes. The object may grow beyond this
+  // size, but will always start at this size and so the initial buffer must
+  // match this size.
+  static constexpr size_t kSize =
+      ::aos::fbs::AlignOffset(kDefaultObjectAbsoluteOffsetstring +
+                                  ::aos::fbs::String<20>::kPreallocatedSize -
+                                  kAlignOffset,
+                              kAlign, kAlignOffset) +
+      kAlignOffset;
+  // Always statically allocate memory for tables (set for consistency with
+  // static_vector.h).
+  static constexpr size_t kPreallocatedSize = kSize;
+  // Size required for a buffer that includes a root table offset at the start.
+  static constexpr size_t kRootSize =
+      ::aos::fbs::AlignOffset(kSize + sizeof(::flatbuffers::uoffset_t), kAlign);
 };
 }  // namespace aos::fbs::testing