added a nice class for 1-byte counters that wrap
diff --git a/aos/common/util/util.gyp b/aos/common/util/util.gyp
index e295682..852f3d7 100644
--- a/aos/common/util/util.gyp
+++ b/aos/common/util/util.gyp
@@ -41,5 +41,23 @@
'<(AOS)/build/aos.gyp:logging',
],
},
+ {
+ 'target_name': 'wrapping_counter',
+ 'type': 'static_library',
+ 'sources': [
+ 'wrapping_counter.cc',
+ ],
+ },
+ {
+ 'target_name': 'wrapping_counter_test',
+ 'type': 'executable',
+ 'sources': [
+ 'wrapping_counter_test.cc',
+ ],
+ 'dependencies': [
+ 'wrapping_counter',
+ '<(EXTERNALS):gtest',
+ ],
+ },
],
}
diff --git a/aos/common/util/wrapping_counter.cc b/aos/common/util/wrapping_counter.cc
new file mode 100644
index 0000000..61f5047
--- /dev/null
+++ b/aos/common/util/wrapping_counter.cc
@@ -0,0 +1,19 @@
+#include "aos/common/util/wrapping_counter.h"
+
+namespace aos {
+namespace util {
+
+WrappingCounter::WrappingCounter(int32_t initial_count)
+ : count_(initial_count), last_count_(0) {}
+
+int32_t WrappingCounter::Update(uint8_t current) {
+ if (last_count_ > current) {
+ count_ += 0x100;
+ }
+ count_ = (count_ & 0xffffff00) | current;
+ last_count_ = current;
+ return count_;
+}
+
+} // namespace util
+} // namespace aos
diff --git a/aos/common/util/wrapping_counter.h b/aos/common/util/wrapping_counter.h
new file mode 100644
index 0000000..fbf3611
--- /dev/null
+++ b/aos/common/util/wrapping_counter.h
@@ -0,0 +1,34 @@
+#ifndef AOS_COMMON_UTIL_WRAPPING_COUNTER_H_
+#define AOS_COMMON_UTIL_WRAPPING_COUNTER_H_
+
+#include <stdint.h>
+
+namespace aos {
+namespace util {
+
+// Deals correctly with 1-byte counters which wrap.
+// This is only possible if the counter never wraps twice between Update calls.
+// It will also fail if the counter ever goes down (that will be interpreted as
+// +255 instead of -1, for example).
+class WrappingCounter {
+ public:
+ WrappingCounter(int32_t initial_count = 0);
+
+ // Updates the internal counter with a new raw value.
+ // Returns count() for convenience.
+ int32_t Update(uint8_t current);
+
+ // Resets the actual count to value.
+ void Reset(int32_t value = 0) { count_ = value; }
+
+ int32_t count() const { return count_; }
+
+ private:
+ int32_t count_;
+ uint8_t last_count_;
+};
+
+} // namespace util
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_WRAPPING_COUNTER_H_
diff --git a/aos/common/util/wrapping_counter_test.cc b/aos/common/util/wrapping_counter_test.cc
new file mode 100644
index 0000000..e257fb0
--- /dev/null
+++ b/aos/common/util/wrapping_counter_test.cc
@@ -0,0 +1,58 @@
+#include "aos/common/util/wrapping_counter.h"
+
+#include <limits.h>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+TEST(WrappingCounterTest, Basic) {
+ WrappingCounter test_counter;
+ EXPECT_EQ(0, test_counter.count());
+ EXPECT_EQ(1, test_counter.Update(1));
+ EXPECT_EQ(1, test_counter.Update(1));
+ EXPECT_EQ(2, test_counter.Update(2));
+ EXPECT_EQ(7, test_counter.Update(7));
+ EXPECT_EQ(7, test_counter.count());
+ EXPECT_EQ(123, test_counter.Update(123));
+ EXPECT_EQ(123, test_counter.count());
+}
+
+TEST(WrappingCounterTest, Reset) {
+ WrappingCounter test_counter;
+ test_counter.Update(5);
+ test_counter.Reset();
+ EXPECT_EQ(0, test_counter.count());
+ test_counter.Reset(56);
+ EXPECT_EQ(56, test_counter.count());
+}
+
+namespace {
+void test_wrapping(int16_t start, int16_t step) {
+ WrappingCounter test_counter;
+ for (int16_t i = start; i < INT16_MAX - step; i += step) {
+ EXPECT_EQ(i, test_counter.Update(i & 0xFF));
+ }
+}
+}
+
+// This tests the basic wrapping functionality.
+TEST(WrappingCounterTest, ReasonableWrapping) {
+ test_wrapping(0, 13);
+ test_wrapping(0, 53);
+ test_wrapping(0, 64);
+ test_wrapping(0, 73);
+}
+
+// It would be reasonable for these to fail if the implementation changes.
+TEST(WrappingCounterTest, UnreasonableWrapping) {
+ test_wrapping(0, 128);
+ test_wrapping(0, 213);
+ test_wrapping(0, 255);
+}
+
+} // namespace testing
+} // namespace util
+} // namespace aos