Support FromFlatbuffer() with Object API

This adds support so that FromFlatbuffer works on FlatbufferT types (the
"object" API), with the same semantics as Flatbuffer::Pack().

This makes it so that existing code which uses these types can easily be
translated into the new API.

Change-Id: I11b2e259708a15bdd62e215dfa514fcb9450ade5
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/flatbuffers/static_vector.h b/aos/flatbuffers/static_vector.h
index 7349a8d..bf40ce5 100644
--- a/aos/flatbuffers/static_vector.h
+++ b/aos/flatbuffers/static_vector.h
@@ -203,6 +203,11 @@
       typename internal::InlineWrapper<T, kInline>::FlatbufferType;
   using ConstFlatbufferType =
       typename internal::InlineWrapper<T, kInline>::ConstFlatbufferType;
+  // FlatbufferObjectType corresponds to the type used by the flatbuffer
+  // "object" API (i.e. the FlatbufferT types).
+  // This type will be something unintelligble for inline types.
+  using FlatbufferObjectType =
+      typename internal::InlineWrapper<T, kInline>::FlatbufferObjectType;
   // flatbuffers::Vector type that corresponds to this Vector.
   typedef flatbuffers::Vector<FlatbufferType> Flatbuffer;
   typedef const flatbuffers::Vector<ConstFlatbufferType> ConstFlatbuffer;
@@ -330,6 +335,66 @@
   // This is a deep copy, and will call FromFlatbuffer on any constituent
   // objects.
   [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer *vector);
+  // The remaining FromFlatbuffer() overloads are for when using the flatbuffer
+  // "object" API, which uses std::vector's for representing vectors.
+  [[nodiscard]] bool FromFlatbuffer(const std::vector<InlineType> &vector) {
+    static_assert(kInline);
+    return FromData(vector.data(), vector.size());
+  }
+  // Overload for vectors of bools, since the standard library may not use a
+  // full byte per vector element.
+  [[nodiscard]] bool FromFlatbuffer(const std::vector<bool> &vector) {
+    static_assert(kInline);
+    // We won't be able to do a clean memcpy because std::vector<bool> may be
+    // implemented using bit-packing.
+    return FromIterator(vector.cbegin(), vector.cend());
+  }
+  // Overload for non-inline types. Note that to avoid having this overload get
+  // resolved with inline types, we make FlatbufferObjectType != InlineType.
+  [[nodiscard]] bool FromFlatbuffer(
+      const std::vector<FlatbufferObjectType> &vector) {
+    static_assert(!kInline);
+    return FromNotInlineIterable(vector);
+  }
+
+  // Copies values from the provided data pointer into the vector, resizing the
+  // vector as needed to match. Returns false on failure (e.g., if the
+  // underlying allocator has insufficient space to perform the copy). Only
+  // works for inline data types.
+  [[nodiscard]] bool FromData(const InlineType *input_data, size_t input_size) {
+    static_assert(kInline);
+    if (!reserve(input_size)) {
+      return false;
+    }
+
+    // We will be overwriting the whole vector very shortly; there is no need to
+    // clear the buffer to zero.
+    resize_inline(input_size, SetZero::kNo);
+
+    memcpy(inline_data(), input_data, size() * sizeof(InlineType));
+    return true;
+  }
+
+  // Copies values from the provided iterators into the vector, resizing the
+  // vector as needed to match. Returns false on failure (e.g., if the
+  // underlying allocator has insufficient space to perform the copy). Only
+  // works for inline data types.
+  // Does not attempt any optimizations if the iterators meet the
+  // std::contiguous_iterator concept; instead, it simply copies each element
+  // out one-by-one.
+  template <typename Iterator>
+  [[nodiscard]] bool FromIterator(Iterator begin, Iterator end) {
+    static_assert(kInline);
+    resize(0);
+    for (Iterator it = begin; it != end; ++it) {
+      if (!reserve(size() + 1)) {
+        return false;
+      }
+      // Should never fail, due to the reserve() above.
+      CHECK(emplace_back(*it));
+    }
+    return true;
+  }
 
   // Returns the element at the provided index. index must be less than size().
   const T &at(size_t index) const {
@@ -570,28 +635,22 @@
   // Implementation that handles copying from a flatbuffers::Vector of an inline
   // data type.
   [[nodiscard]] bool FromInlineFlatbuffer(ConstFlatbuffer *vector) {
-    if (!reserve(CHECK_NOTNULL(vector)->size())) {
-      return false;
-    }
-
-    // We will be overwriting the whole vector very shortly; there is no need to
-    // clear the buffer to zero.
-    resize_inline(vector->size(), SetZero::kNo);
-
-    memcpy(inline_data(), vector->Data(), size() * sizeof(InlineType));
-    return true;
+    return FromData(
+        reinterpret_cast<const InlineType *>(CHECK_NOTNULL(vector)->Data()),
+        vector->size());
   }
 
   // Implementation that handles copying from a flatbuffers::Vector of a
   // not-inline data type.
-  [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer *vector) {
-    if (!reserve(vector->size())) {
+  template <typename Iterable>
+  [[nodiscard]] bool FromNotInlineIterable(const Iterable &vector) {
+    if (!reserve(vector.size())) {
       return false;
     }
     // "Clear" the vector.
     resize_not_inline(0);
 
-    for (const typename T::Flatbuffer *entry : *vector) {
+    for (const auto &entry : vector) {
       if (!CHECK_NOTNULL(emplace_back())->FromFlatbuffer(entry)) {
         return false;
       }
@@ -599,6 +658,10 @@
     return true;
   }
 
+  [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer *vector) {
+    return FromNotInlineIterable(*vector);
+  }
+
   // In order to allow for easy partial template specialization, we use a
   // non-member class to call FromInline/FromNotInlineFlatbuffer and
   // resize_inline/resize_not_inline. There are not actually any great ways to
@@ -659,6 +722,7 @@
  public:
   typedef Vector<char, kStaticLength, true, 0, true> VectorType;
   typedef flatbuffers::String Flatbuffer;
+  typedef std::string FlatbufferObjectType;
   String(std::span<uint8_t> buffer, ResizeableObject *parent)
       : VectorType(buffer, parent) {}
   virtual ~String() {}
@@ -667,6 +731,10 @@
     VectorType::resize_inline(string.size(), SetZero::kNo);
     memcpy(VectorType::data(), string.data(), string.size());
   }
+  using VectorType::FromFlatbuffer;
+  [[nodiscard]] bool FromFlatbuffer(const std::string &string) {
+    return VectorType::FromData(string.data(), string.size());
+  }
   std::string_view string_view() const {
     return std::string_view(VectorType::data(), VectorType::size());
   }
@@ -690,6 +758,7 @@
   typedef T ObjectType;
   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;
@@ -712,6 +781,7 @@
   typedef T ObjectType;
   typedef T FlatbufferType;
   typedef T ConstFlatbufferType;
+  typedef T *FlatbufferObjectType;
   static constexpr size_t kDataAlign = alignof(T);
   static constexpr size_t kDataSize = sizeof(T);
   template <typename StaticVector>
@@ -731,6 +801,7 @@
   typedef uint8_t ObjectType;
   typedef uint8_t FlatbufferType;
   typedef uint8_t ConstFlatbufferType;
+  typedef uint8_t *FlatbufferObjectType;
   static constexpr size_t kDataAlign = 1u;
   static constexpr size_t kDataSize = 1u;
   template <typename StaticVector>
@@ -753,6 +824,7 @@
   typedef T ObjectType;
   typedef T *FlatbufferType;
   typedef const T *ConstFlatbufferType;
+  typedef T *FlatbufferObjectType;
   static constexpr size_t kDataAlign = alignof(T);
   static constexpr size_t kDataSize = sizeof(T);
   template <typename StaticVector>