Replace aos::SizedArray with absl::InlinedVector

Change-Id: I4dae096c5079afa3135e3a1cb7cb17e2789275e6
Signed-off-by: Tyler Chatow <tchatow@gmail.com>
diff --git a/aos/containers/BUILD b/aos/containers/BUILD
index e9f8262..9fdc93f 100644
--- a/aos/containers/BUILD
+++ b/aos/containers/BUILD
@@ -46,6 +46,9 @@
     hdrs = [
         "sized_array.h",
     ],
+    deps = [
+        "@com_google_absl//absl/container:inlined_vector",
+    ],
 )
 
 cc_test(
diff --git a/aos/containers/sized_array.h b/aos/containers/sized_array.h
index 4264cf6..8a8d2de 100644
--- a/aos/containers/sized_array.h
+++ b/aos/containers/sized_array.h
@@ -1,134 +1,28 @@
 #ifndef AOS_CONTAINERS_SIZED_ARRAY_H_
 #define AOS_CONTAINERS_SIZED_ARRAY_H_
 
-#include <array>
-#include <cstddef>
+#include "absl/container/inlined_vector.h"
 
 namespace aos {
 
-// An array along with a variable size. This is a simple variable-size container
-// with a fixed maximum size.
-//
-// Note that it default-constructs N T instances at construction time. This
-// simplifies the internal bookkeeping a lot (I believe this can be
-// all-constexpr in C++17), but makes it a poor choice for complex T.
-template <typename T, size_t N>
-class SizedArray {
- private:
-  using array = std::array<T, N>;
-
+// Minimal compliant allocator whose allocating operations are all fatal.
+template <typename T>
+class FatalAllocator {
  public:
-  using value_type = typename array::value_type;
-  using size_type = typename array::size_type;
-  using difference_type = typename array::difference_type;
-  using reference = typename array::reference;
-  using const_reference = typename array::const_reference;
-  using pointer = typename array::pointer;
-  using const_pointer = typename array::const_pointer;
-  using iterator = typename array::iterator;
-  using const_iterator = typename array::const_iterator;
-  using reverse_iterator = typename array::reverse_iterator;
-  using const_reverse_iterator = typename array::const_reverse_iterator;
+  using value_type = T;
 
-  constexpr SizedArray() = default;
-  SizedArray(const SizedArray &) = default;
-  SizedArray(SizedArray &&) = default;
-  SizedArray &operator=(const SizedArray &) = default;
-  SizedArray &operator=(SizedArray &&) = default;
+  [[nodiscard, noreturn]] T *allocate(std::size_t) { __builtin_trap(); }
 
-  bool operator==(const SizedArray &other) const {
-    if (other.size() != size()) {
-      return false;
-    }
-    for (size_t i = 0; i < size(); ++i) {
-      if (other[i] != (*this)[i]) {
-        return false;
-      }
-    }
-    return true;
-  }
-  bool operator!=(const SizedArray &other) const {
-    return !(*this == other);
-  }
-
-  reference at(size_t i) {
-    check_index(i);
-    return array_.at(i);
-  }
-  const_reference at(size_t i) const {
-    check_index(i);
-    return array_.at(i);
-  }
-
-  reference operator[](size_t i) { return array_[i]; }
-  const_reference operator[](size_t i) const { return array_[i]; }
-
-  reference front() { return array_.front(); }
-  const_reference front() const { return array_.front(); }
-
-  reference back() { return array_[size_ - 1]; }
-  const_reference back() const { return array_[size_ - 1]; }
-
-  T *data() { return array_.data(); }
-  const T *data() const { return array_.data(); }
-
-  iterator begin() { return array_.begin(); }
-  const_iterator begin() const { return array_.begin(); }
-  const_iterator cbegin() const { return array_.cbegin(); }
-
-  iterator end() { return array_.begin() + size_; }
-  const_iterator end() const { return array_.begin() + size_; }
-  const_iterator cend() const { return array_.cbegin() + size_; }
-
-  reverse_iterator rbegin() { return array_.rend() - size_; }
-  const_reverse_iterator rbegin() const { return array_.rend() - size_; }
-  const_reverse_iterator crbegin() const { return array_.crend() - size_; }
-
-  reverse_iterator rend() { return array_.rend(); }
-  const_reverse_iterator rend() const { return array_.rend(); }
-  const_reverse_iterator crend() const { return array_.crend(); }
-
-  bool empty() const { return size_ == 0; }
-  bool full() const { return size() == max_size(); }
-
-  size_t size() const { return size_; }
-  constexpr size_t max_size() const { return array_.max_size(); }
-
-  void push_back(const T &t) {
-    array_.at(size_) = t;
-    ++size_;
-  }
-  void push_back(T &&t) {
-    array_.at(size_) = std::move(t);
-    ++size_;
-  }
-
-  void pop_back() {
-    if (empty()) {
-      __builtin_trap();
-    }
-    --size_;
-  }
-
-  void clear() { size_ = 0; }
-
-  // These allow access to the underlying storage. The data here may be outside
-  // the current logical extents of the container.
-  const array &backing_array() const { return array_; }
-  array *mutable_backing_array() { return &array_; }
-  void set_size(size_t size) { size_ = size; }
-
- private:
-  void check_index(size_t i) const {
-    if (i >= size_) {
-      __builtin_trap();
-    }
-  }
-
-  array array_;
-  size_t size_ = 0;
+  [[noreturn]] void deallocate(T *, std::size_t) { __builtin_trap(); }
 };
 
+// Reuse the logic from absl::InlinedVector for a statically allocated,
+// dynamically sized list of values. InlinedVector's default behavior is to
+// allocate from the heap when growing beyond the static capacity, make this
+// fatal instead to enforce RT guarantees.
+template <typename T, size_t N>
+using SizedArray = absl::InlinedVector<T, N, FatalAllocator<T>>;
+
 }  // namespace aos
 
 #endif  // AOS_CONTAINERS_SIZED_ARRAY_H_
diff --git a/aos/containers/sized_array_test.cc b/aos/containers/sized_array_test.cc
index bfaba06..d182ad2 100644
--- a/aos/containers/sized_array_test.cc
+++ b/aos/containers/sized_array_test.cc
@@ -32,39 +32,39 @@
 TEST(SizedArrayTest, Accessors) {
   SizedArray<int, 5> a;
   EXPECT_TRUE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   EXPECT_EQ(0u, a.size());
-  EXPECT_EQ(5u, a.max_size());
+  EXPECT_EQ(5u, a.capacity());
 
   a.push_back(9);
   EXPECT_FALSE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   EXPECT_EQ(1u, a.size());
-  EXPECT_EQ(5u, a.max_size());
+  EXPECT_EQ(5u, a.capacity());
 
   a.push_back(9);
   EXPECT_FALSE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   EXPECT_EQ(2u, a.size());
-  EXPECT_EQ(5u, a.max_size());
+  EXPECT_EQ(5u, a.capacity());
 
   a.push_back(9);
   EXPECT_FALSE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   EXPECT_EQ(3u, a.size());
-  EXPECT_EQ(5u, a.max_size());
+  EXPECT_EQ(5u, a.capacity());
 
   a.push_back(9);
   EXPECT_FALSE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   EXPECT_EQ(4u, a.size());
-  EXPECT_EQ(5u, a.max_size());
+  EXPECT_EQ(5u, a.capacity());
 
   a.push_back(9);
   EXPECT_FALSE(a.empty());
-  EXPECT_TRUE(a.full());
+  EXPECT_EQ(a.size(), a.capacity());
   EXPECT_EQ(5u, a.size());
-  EXPECT_EQ(5u, a.max_size());
+  EXPECT_EQ(5u, a.capacity());
 }
 
 // Tests the various kinds of iterator.
@@ -139,20 +139,35 @@
 TEST(SizedArrayTest, FillEmpty) {
   SizedArray<int, 2> a;
   EXPECT_TRUE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   a.push_back(9);
   EXPECT_FALSE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   a.push_back(7);
   EXPECT_FALSE(a.empty());
-  EXPECT_TRUE(a.full());
+  EXPECT_EQ(a.size(), a.capacity());
 
   a.clear();
   EXPECT_TRUE(a.empty());
-  EXPECT_FALSE(a.full());
+  EXPECT_NE(a.size(), a.capacity());
   a.push_back(1);
   EXPECT_EQ(1, a.back());
 }
 
+TEST(SizedArrayTest, OverflowTest) {
+  SizedArray<int, 4> a;
+  EXPECT_EQ(a.capacity(), 4u);
+  EXPECT_TRUE(a.empty());
+
+  const int* const pre_front = a.data();
+  a.assign({1, 2, 3, 4});
+
+  EXPECT_EQ(a.capacity(), 4u);
+  // Verify that we didn't reallocate
+  EXPECT_EQ(pre_front, a.data());
+
+  EXPECT_DEATH(a.emplace_back(5), "SIGILL");
+}
+
 }  // namespace testing
 }  // namespace aos