Implement iterators for static flatbuffers Vector

This is largely a quality-of-life improvement.

Change-Id: I7fb156b90c5e71dbf8ae189117866422701590ba
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/flatbuffers/static_flatbuffers_test.cc b/aos/flatbuffers/static_flatbuffers_test.cc
index acea168..2acc3c8 100644
--- a/aos/flatbuffers/static_flatbuffers_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_test.cc
@@ -913,6 +913,78 @@
        *object->AsFlatbuffer().unspecified_length_vector()) {
     EXPECT_EQ(expected++, value);
   }
+  expected = 0;
+  for (const uint8_t value : *object->unspecified_length_vector()) {
+    EXPECT_EQ(expected++, value);
+  }
   TestMemory(builder.buffer());
 }
+
+// Tests that the iterators on the Vector type work.
+TEST_F(StaticFlatbuffersTest, IteratorTest) {
+  Builder<TestTableStatic> builder(std::make_unique<VectorAllocator>());
+  {
+    auto vector = builder->add_unspecified_length_vector();
+    ASSERT_TRUE(vector->reserve(9000));
+    vector->resize(256);
+    uint8_t set_value = 0;
+    for (uint8_t &destination : *vector) {
+      destination = set_value;
+      ++set_value;
+    }
+    uint8_t expected = 0;
+    for (const uint8_t value : *builder->unspecified_length_vector()) {
+      EXPECT_EQ(expected, value);
+      ++expected;
+    }
+    // Exercise some of the random access iterator functionality to ensure that
+    // we have it implemented.
+    auto begin_it = vector->begin();
+    EXPECT_EQ(begin_it + 256, vector->end());
+    EXPECT_EQ(7, *(begin_it + 7));
+    EXPECT_EQ(255, *(vector->end() - 1));
+    EXPECT_EQ(256, vector->end() - vector->begin());
+    EXPECT_EQ(-256, vector->begin() - vector->end());
+    static_assert(std::random_access_iterator<decltype(vector->begin())>,
+                  "The vector iterator does not meet the requirements of a "
+                  "random access iterator.");
+  }
+  {
+    auto vector = builder->add_vector_of_structs();
+    vector->resize(3);
+    double set_value = 0;
+    for (SubStruct &destination : *vector) {
+      destination.mutate_x(set_value);
+      destination.mutate_y(-set_value);
+      set_value += 1.0;
+    }
+    double expected = 0;
+    for (const SubStruct &value : *builder->vector_of_structs()) {
+      EXPECT_EQ(expected, value.x());
+      EXPECT_EQ(-expected, value.y());
+      expected += 1.0;
+    }
+    static_assert(std::random_access_iterator<decltype(vector->begin())>,
+                  "The vector iterator does not meet the requirements of a "
+                  "random access iterator.");
+  }
+  {
+    auto vector = builder->add_vector_of_tables();
+    vector->resize(3);
+    int set_value = 0;
+    for (SubTableStatic &destination : *vector) {
+      destination.set_foo(set_value);
+      set_value += 1;
+    }
+    int expected = 0;
+    for (const SubTableStatic &value : *builder->vector_of_tables()) {
+      EXPECT_EQ(expected, value.foo());
+      EXPECT_FALSE(value.has_baz());
+      expected += 1;
+    }
+    static_assert(std::random_access_iterator<decltype(vector->begin())>,
+                  "The vector iterator does not meet the requirements of a "
+                  "random access iterator.");
+  }
+}
 }  // namespace aos::fbs::testing
diff --git a/aos/flatbuffers/static_vector.h b/aos/flatbuffers/static_vector.h
index f4cfea6..0aff48e 100644
--- a/aos/flatbuffers/static_vector.h
+++ b/aos/flatbuffers/static_vector.h
@@ -109,7 +109,87 @@
 template <typename T, size_t kStaticLength, bool kInline,
           size_t kForceAlign = 0, bool kNullTerminate = false>
 class Vector : public ResizeableObject {
+  template <typename VectorType, typename ValueType>
+  class generic_iterator {
+   public:
+    using iterator_category = std::random_access_iterator_tag;
+    using value_type = ValueType;
+    using difference_type = std::ptrdiff_t;
+    using pointer = value_type *;
+    using reference = value_type &;
+
+    explicit generic_iterator(VectorType *vector, size_t index)
+        : vector_(vector), index_(index) {}
+    generic_iterator(const generic_iterator &) = default;
+    generic_iterator() : vector_(nullptr), index_(0) {}
+    generic_iterator &operator=(const generic_iterator &) = default;
+
+    generic_iterator &operator++() {
+      ++index_;
+      return *this;
+    }
+    generic_iterator operator++(int) {
+      generic_iterator retval = *this;
+      ++(*this);
+      return retval;
+    }
+    generic_iterator &operator--() {
+      --index_;
+      return *this;
+    }
+    generic_iterator operator--(int) {
+      generic_iterator retval = *this;
+      --(*this);
+      return retval;
+    }
+    bool operator==(const generic_iterator &other) const {
+      CHECK_EQ(other.vector_, vector_);
+      return index_ == other.index_;
+    }
+    std::strong_ordering operator<=>(const generic_iterator &other) const {
+      CHECK_EQ(other.vector_, vector_);
+      return index_ <=> other.index_;
+    }
+    reference operator*() const { return vector_->at(index_); }
+    difference_type operator-(const generic_iterator &other) const {
+      CHECK_EQ(other.vector_, vector_);
+      return index_ - other.index_;
+    }
+    generic_iterator operator-(difference_type decrement) const {
+      return generic_iterator(vector_, index_ - decrement);
+    }
+    friend generic_iterator operator-(difference_type decrement,
+                                      const generic_iterator &rhs) {
+      return rhs - decrement;
+    }
+    generic_iterator operator+(difference_type increment) const {
+      return generic_iterator(vector_, index_ + increment);
+    }
+    friend generic_iterator operator+(difference_type increment,
+                                      const generic_iterator &rhs) {
+      return rhs + increment;
+    }
+    generic_iterator &operator+=(difference_type increment) {
+      index_ += increment;
+      return *this;
+    }
+    generic_iterator &operator-=(difference_type increment) {
+      index_ -= increment;
+      return *this;
+    }
+    reference operator[](difference_type index) const {
+      return *(*this + index);
+    }
+
+   private:
+    VectorType *vector_;
+    size_t index_;
+  };
+
  public:
+  using iterator = generic_iterator<Vector, T>;
+  using const_iterator = generic_iterator<const Vector, const T>;
+
   static_assert(kInline || !kNullTerminate,
                 "It does not make sense to null-terminate vectors of objects.");
   // Type stored inline in the serialized vector (offsets for tables/strings; T
@@ -380,6 +460,12 @@
     return inline_data();
   }
 
+  // Iterators to allow easy use with standard C++ features.
+  iterator begin() { return iterator(this, 0); }
+  iterator end() { return iterator(this, size()); }
+  const_iterator begin() const { return const_iterator(this, 0); }
+  const_iterator end() const { return const_iterator(this, size()); }
+
   std::string SerializationDebugString() const {
     std::stringstream str;
     str << "Raw Size: " << kSize << " alignment: " << kAlign