Add std::array-based ErrorCounter

Essentially the same interface as the existing error counter, but
doesn't require that you keep a flatbuffer around to back the memory.

Change-Id: I52338016f749395ad7d2d9b2443e3e6b30aee9b8
Signed-off-by: James Kuszmaul <jabukuszmaul+collab@gmail.com>
diff --git a/aos/util/error_counter.h b/aos/util/error_counter.h
index cf6cb7f..9fbe242 100644
--- a/aos/util/error_counter.h
+++ b/aos/util/error_counter.h
@@ -67,5 +67,40 @@
  private:
   flatbuffers::Vector<flatbuffers::Offset<Count>> *vector_ = nullptr;
 };
+
+// The ArrayErrorCounter serves the same purpose as the ErrorCounter class,
+// except that:
+// (a) It owns its own memory, rather than modifying a flatbuffer in-place.
+// (b) Because of this, the user has greater flexibility in choosing when to
+//     reset the error counters.
+template <typename Error, typename Count>
+class ArrayErrorCounter {
+ public:
+  static constexpr size_t kNumErrors = ErrorCounter<Error, Count>::kNumErrors;
+  ArrayErrorCounter() { ResetCounts(); }
+
+  flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
+  PopulateCounts(flatbuffers::FlatBufferBuilder *fbb) {
+    const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
+        offset = ErrorCounter<Error, Count>::Initialize(fbb);
+    flatbuffers::Vector<flatbuffers::Offset<Count>> *vector =
+        flatbuffers::GetMutableTemporaryPointer(*fbb, offset);
+    for (size_t ii = 0; ii < kNumErrors; ++ii) {
+      vector->GetMutableObject(ii)->mutate_count(error_counts_.at(ii));
+    }
+    return offset;
+  }
+
+  void IncrementError(Error error) {
+    DCHECK_LT(static_cast<size_t>(error), error_counts_.size());
+    error_counts_.at(static_cast<size_t>(error))++;
+  }
+
+  // Sets all the error counts to zero.
+  void ResetCounts() { error_counts_.fill(0); }
+
+ private:
+  std::array<size_t, kNumErrors> error_counts_;
+};
 }  // namespace aos::util
 #endif  // AOS_UTIL_ERROR_COUNTER_H_
diff --git a/aos/util/error_counter_test.cc b/aos/util/error_counter_test.cc
index 2166cea..567d71d 100644
--- a/aos/util/error_counter_test.cc
+++ b/aos/util/error_counter_test.cc
@@ -34,4 +34,45 @@
   EXPECT_EQ(0u, message.message().error_counts()->Get(0)->count());
   EXPECT_EQ(0u, message.message().error_counts()->Get(1)->count());
 }
+
+// Tests the ArrayErrorCounter
+TEST(ErrorCounterTest, ARrayErrorCounter) {
+  ArrayErrorCounter<aos::timing::SendError, aos::timing::SendErrorCount>
+      counter;
+  flatbuffers::FlatBufferBuilder fbb;
+  fbb.ForceDefaults(true);
+  counter.IncrementError(aos::timing::SendError::MESSAGE_SENT_TOO_FAST);
+  counter.IncrementError(aos::timing::SendError::MESSAGE_SENT_TOO_FAST);
+  counter.IncrementError(aos::timing::SendError::INVALID_REDZONE);
+  {
+    const flatbuffers::Offset<
+        flatbuffers::Vector<flatbuffers::Offset<aos::timing::SendErrorCount>>>
+        counts_offset = counter.PopulateCounts(&fbb);
+    aos::timing::Sender::Builder builder(fbb);
+    builder.add_error_counts(counts_offset);
+    fbb.Finish(builder.Finish());
+    aos::FlatbufferDetachedBuffer<aos::timing::Sender> message = fbb.Release();
+    ASSERT_EQ(2u, message.message().error_counts()->size());
+    EXPECT_EQ(aos::timing::SendError::MESSAGE_SENT_TOO_FAST,
+              message.message().error_counts()->Get(0)->error());
+    EXPECT_EQ(2u, message.message().error_counts()->Get(0)->count());
+    EXPECT_EQ(aos::timing::SendError::INVALID_REDZONE,
+              message.message().error_counts()->Get(1)->error());
+    EXPECT_EQ(1u, message.message().error_counts()->Get(1)->count());
+  }
+
+  counter.ResetCounts();
+  {
+    const flatbuffers::Offset<
+        flatbuffers::Vector<flatbuffers::Offset<aos::timing::SendErrorCount>>>
+        counts_offset = counter.PopulateCounts(&fbb);
+    aos::timing::Sender::Builder builder(fbb);
+    builder.add_error_counts(counts_offset);
+    fbb.Finish(builder.Finish());
+    aos::FlatbufferDetachedBuffer<aos::timing::Sender> message = fbb.Release();
+    ASSERT_EQ(2u, message.message().error_counts()->size());
+    EXPECT_EQ(0u, message.message().error_counts()->Get(0)->count());
+    EXPECT_EQ(0u, message.message().error_counts()->Get(1)->count());
+  }
+}
 }  // namespace aos::util::testing