diff options
-rw-r--r-- | src/base/sat_counter.hh | 114 | ||||
-rw-r--r-- | src/base/sat_counter.test.cc | 173 |
2 files changed, 287 insertions, 0 deletions
diff --git a/src/base/sat_counter.hh b/src/base/sat_counter.hh index 342a3382f..3de9486db 100644 --- a/src/base/sat_counter.hh +++ b/src/base/sat_counter.hh @@ -56,6 +56,9 @@ class SatCounter { public: + /** The default constructor should never be used. */ + SatCounter() = delete; + /** * Constructor for the counter. The explicit keyword is used to make * sure the user does not assign a number to the counter thinking it @@ -75,6 +78,58 @@ class SatCounter "Saturating counter's Initial value exceeds max value."); } + /** Copy constructor. */ + SatCounter(const SatCounter& other) + : initialVal(other.initialVal), maxVal(other.maxVal), + counter(other.counter) + { + } + + /** Copy assignment. */ + SatCounter& operator=(const SatCounter& other) { + if (this != &other) { + SatCounter temp(other); + this->swap(temp); + } + return *this; + } + + /** Move constructor. */ + SatCounter(SatCounter&& other) + { + initialVal = other.initialVal; + maxVal = other.maxVal; + counter = other.counter; + SatCounter temp(0); + other.swap(temp); + } + + /** Move assignment. */ + SatCounter& operator=(SatCounter&& other) { + if (this != &other) { + initialVal = other.initialVal; + maxVal = other.maxVal; + counter = other.counter; + SatCounter temp(0); + other.swap(temp); + } + return *this; + } + + /** + * Swap the contents of every member of the class. Used for the default + * copy-assignment created by the compiler. + * + * @param other The other object to swap contents with. + */ + void + swap(SatCounter& other) + { + std::swap(initialVal, other.initialVal); + std::swap(maxVal, other.maxVal); + std::swap(counter, other.counter); + } + /** Pre-increment operator. */ SatCounter& operator++() @@ -113,6 +168,49 @@ class SatCounter return old_counter; } + /** Shift-right-assignment. */ + SatCounter& + operator>>=(const int& shift) + { + this->counter >>= shift; + return *this; + } + + /** Shift-left-assignment. */ + SatCounter& + operator<<=(const int& shift) + { + this->counter <<= shift; + if (this->counter > maxVal) { + this->counter = maxVal; + } + return *this; + } + + /** Add-assignment. */ + SatCounter& + operator+=(const int& value) + { + if (maxVal - this->counter >= value) { + this->counter += value; + } else { + this->counter = maxVal; + } + return *this; + } + + /** Subtract-assignment. */ + SatCounter& + operator-=(const int& value) + { + if (this->counter > value) { + this->counter -= value; + } else { + this->counter = 0; + } + return *this; + } + /** * Read the counter's value. */ @@ -121,6 +219,22 @@ class SatCounter /** Reset the counter to its initial value. */ void reset() { counter = initialVal; } + /** + * Calculate saturation percentile of the current counter's value + * with regard to its maximum possible value. + * + * @return A value between 0.0 and 1.0 to indicate which percentile of + * the maximum value the current value is. + */ + double calcSaturation() const { return (double) counter / maxVal; } + + /** + * Whether the counter has achieved its maximum value or not. + * + * @return True if the counter saturated. + */ + bool isSaturated() const { return counter == maxVal; } + private: uint8_t initialVal; uint8_t maxVal; diff --git a/src/base/sat_counter.test.cc b/src/base/sat_counter.test.cc index efc79e034..dbdaf0ae6 100644 --- a/src/base/sat_counter.test.cc +++ b/src/base/sat_counter.test.cc @@ -30,6 +30,8 @@ #include <gtest/gtest.h> +#include <utility> + #include "base/sat_counter.hh" /** @@ -78,6 +80,23 @@ TEST(SatCounterTest, InitialValue) } /** + * Test calculating saturation percentile. + */ +TEST(SatCounterTest, SaturationPercentile) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + SatCounter counter(bits); + + ASSERT_FALSE(counter.isSaturated()); + for (double value = 0.0; value <= max_value; value++, counter++) { + const double saturation = value / max_value; + ASSERT_DOUBLE_EQ(counter.calcSaturation(), saturation); + } + ASSERT_TRUE(counter.isSaturated()); +} + +/** * Test back and forth against an int. */ TEST(SatCounterTest, IntComparison) @@ -102,6 +121,55 @@ TEST(SatCounterTest, IntComparison) } /** + * Test shift operators. + */ +TEST(SatCounterTest, Shift) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + const unsigned initial_value = 1; + SatCounter counter(bits, initial_value); + SatCounter other(bits, initial_value); + // The saturated shift value is just enough to saturate, since greater + // values could generate undefined behavior + SatCounter saturated_counter(bits, bits); + int value = initial_value; + + // Test random shifts + counter <<= 2; + value <<= 2; + ASSERT_EQ(counter, value); + counter >>= 1; + value >>= 1; + ASSERT_EQ(counter, value); + + // Test saturation + counter <<= bits; + ASSERT_EQ(counter, max_value); + + // Test zeroing + counter >>= bits; + ASSERT_EQ(counter, 0); + + // Test saturation against other saturating counter + counter.reset(); + value = initial_value; + counter <<= other; + value <<= other; + ASSERT_EQ(counter, value); + counter <<= saturated_counter; + value = max_value; + ASSERT_EQ(counter, max_value); + + // Test zeroing against other saturating counter + counter >>= other; + value >>= other; + ASSERT_EQ(counter, value); + counter >>= saturated_counter; + ASSERT_EQ(counter, 0); +} + +/** * Test both pre and post operators. */ TEST(SatCounterTest, PrePostOperators) @@ -129,3 +197,108 @@ TEST(SatCounterTest, PrePostOperators) ASSERT_EQ(counter_pre, 0); ASSERT_EQ(counter_post, 0); } + +/** + * Test copy and move for both constructor and assignment. + */ +TEST(SatCounterTest, CopyMove) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + const unsigned initial_value = 1; + SatCounter counter(bits, initial_value); + SatCounter deep_copy(1); + SatCounter counter_copy(2); + + // Increase counter value so that we can check if the inner counter is + // being copied + counter++; + + // Copy counter using both the copy constructor and the copy assignment + SatCounter counter_copy_constructor(counter); + deep_copy = counter_copy = counter; + ASSERT_EQ(counter_copy_constructor, initial_value + 1); + ASSERT_EQ(counter_copy, initial_value + 1); + ASSERT_EQ(deep_copy, initial_value + 1); + + // Make sure max value is the same for all of them, and that modifying + // the copies does not modify the original + for (int i = 0; i < 2*max_value; i++) { + counter_copy_constructor++; + counter_copy++; + deep_copy++; + } + ASSERT_EQ(counter, initial_value + 1); + ASSERT_EQ(counter_copy_constructor, max_value); + ASSERT_EQ(counter_copy, max_value); + ASSERT_EQ(deep_copy, max_value); + + // Make sure initial value is the same for all of them + counter_copy_constructor.reset(); + counter_copy.reset(); + deep_copy.reset(); + ASSERT_EQ(counter_copy_constructor, initial_value); + ASSERT_EQ(counter_copy, initial_value); + ASSERT_EQ(deep_copy, initial_value); + + // Now check move + SatCounter counter_move_constructor(std::move(counter)); + ASSERT_EQ(counter, 0); + ASSERT_EQ(counter_move_constructor, initial_value + 1); + + SatCounter counter_move(bits); + counter_move = std::move(counter_move_constructor); + ASSERT_EQ(counter_move_constructor, 0); + ASSERT_EQ(counter_move, initial_value + 1); +} + +/** + * Test add-assignment and subtract assignment. + */ +TEST(SatCounterTest, AddSubAssignment) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + SatCounter counter(bits); + SatCounter other(bits, 2); + SatCounter saturated_counter(bits, max_value); + int value = 0; + + // Test add-assignment for a few random values and then saturate + counter += 2; + value += 2; + ASSERT_EQ(counter, value); + counter += 3; + value += 3; + ASSERT_EQ(counter, value); + counter += max_value; + value = max_value; + ASSERT_EQ(counter, value); + + // Test subtract-assignment for a few random values until back to zero + counter -= 2; + value -= 2; + ASSERT_EQ(counter, value); + counter -= 3; + value -= 3; + ASSERT_EQ(counter, value); + counter -= max_value; + value = 0; + ASSERT_EQ(counter, value); + + // Test add-assignment of other saturating counter + counter += other; + value += other; + ASSERT_EQ(counter, value); + counter += saturated_counter; + value = max_value; + ASSERT_EQ(counter, saturated_counter); + + // Test subtract-assignment of other saturating counter + counter -= other; + value -= other; + ASSERT_EQ(counter, value); + counter -= saturated_counter; + ASSERT_EQ(counter, 0); +} + |