diff options
Diffstat (limited to 'dom/media/doctor/RollingNumber.h')
-rw-r--r-- | dom/media/doctor/RollingNumber.h | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/dom/media/doctor/RollingNumber.h b/dom/media/doctor/RollingNumber.h new file mode 100644 index 0000000000..a04296f8bc --- /dev/null +++ b/dom/media/doctor/RollingNumber.h @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_RollingNumber_h_ +#define mozilla_RollingNumber_h_ + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include <limits> + +namespace mozilla { + +// Unsigned number suited to index elements in a never-ending queue, as +// order-comparison behaves nicely around the overflow. +// +// Additive operators work the same as for the underlying value type, but +// expect "small" jumps, as should normally happen when manipulating indices. +// +// Comparison functions are different, they keep the ordering based on the +// distance between numbers, modulo the value type range: +// If the distance is less than half the range of the value type, the usual +// ordering stays. +// 0 < 1, 2^23 < 2^24 +// However if the distance is more than half the range, we assume that we are +// continuing along the queue, and therefore consider the smaller number to +// actually be greater! +// uint(-1) < 0. +// +// The expected usage is to always work on nearby rolling numbers: slowly +// incrementing/decrementing them, and translating&comparing them within a +// small window. +// To enforce this usage during development, debug-build assertions catch API +// calls involving distances of more than a *quarter* of the type range. +// In non-debug builds, all APIs will still work as consistently as possible +// without crashing, but performing operations on "distant" nunbers could lead +// to unexpected results. +template <typename T> +class RollingNumber { + static_assert(!std::numeric_limits<T>::is_signed, + "RollingNumber only accepts unsigned number types"); + + public: + using ValueType = T; + + RollingNumber() : mIndex(0) {} + + explicit RollingNumber(ValueType aIndex) : mIndex(aIndex) {} + + RollingNumber(const RollingNumber&) = default; + RollingNumber& operator=(const RollingNumber&) = default; + + ValueType Value() const { return mIndex; } + + // Normal increments/decrements. + + RollingNumber& operator++() { + ++mIndex; + return *this; + } + + RollingNumber operator++(int) { return RollingNumber{mIndex++}; } + + RollingNumber& operator--() { + --mIndex; + return *this; + } + + RollingNumber operator--(int) { return RollingNumber{mIndex--}; } + + RollingNumber& operator+=(const ValueType& aIncrement) { + MOZ_ASSERT(aIncrement <= MaxDiff); + mIndex += aIncrement; + return *this; + } + + RollingNumber operator+(const ValueType& aIncrement) const { + RollingNumber n = *this; + return n += aIncrement; + } + + RollingNumber& operator-=(const ValueType& aDecrement) { + MOZ_ASSERT(aDecrement <= MaxDiff); + mIndex -= aDecrement; + return *this; + } + + // Translate a RollingNumber by a negative value. + RollingNumber operator-(const ValueType& aDecrement) const { + RollingNumber n = *this; + return n -= aDecrement; + } + + // Distance between two RollingNumbers, giving a value. + ValueType operator-(const RollingNumber& aOther) const { + ValueType diff = mIndex - aOther.mIndex; + MOZ_ASSERT(diff <= MaxDiff); + return diff; + } + + // Normal (in)equality operators. + + bool operator==(const RollingNumber& aOther) const { + return mIndex == aOther.mIndex; + } + bool operator!=(const RollingNumber& aOther) const { + return !(*this == aOther); + } + + // Modified comparison operators. + + bool operator<(const RollingNumber& aOther) const { + const T& a = mIndex; + const T& b = aOther.mIndex; + // static_cast needed because of possible integer promotion + // (e.g., from uint8_t to int, which would make the test useless). + const bool lessThanOther = static_cast<ValueType>(a - b) > MidWay; + MOZ_ASSERT((lessThanOther ? (b - a) : (a - b)) <= MaxDiff); + return lessThanOther; + } + + bool operator<=(const RollingNumber& aOther) const { + const T& a = mIndex; + const T& b = aOther.mIndex; + const bool lessishThanOther = static_cast<ValueType>(b - a) <= MidWay; + MOZ_ASSERT((lessishThanOther ? (b - a) : (a - b)) <= MaxDiff); + return lessishThanOther; + } + + bool operator>=(const RollingNumber& aOther) const { + const T& a = mIndex; + const T& b = aOther.mIndex; + const bool greaterishThanOther = static_cast<ValueType>(a - b) <= MidWay; + MOZ_ASSERT((greaterishThanOther ? (a - b) : (b - a)) <= MaxDiff); + return greaterishThanOther; + } + + bool operator>(const RollingNumber& aOther) const { + const T& a = mIndex; + const T& b = aOther.mIndex; + const bool greaterThanOther = static_cast<ValueType>(b - a) > MidWay; + MOZ_ASSERT((greaterThanOther ? (a - b) : (b - a)) <= MaxDiff); + return greaterThanOther; + } + + private: + // MidWay is used to split the type range in two, to decide how two numbers + // are ordered. + static const T MidWay = std::numeric_limits<T>::max() / 2; +#ifdef DEBUG + // MaxDiff is the expected maximum difference between two numbers, either + // during comparisons, or when adding/subtracting. + // This is only used during debugging, to highlight algorithmic issues. + static const T MaxDiff = std::numeric_limits<T>::max() / 4; +#endif + ValueType mIndex; +}; + +} // namespace mozilla + +#endif // mozilla_RollingNumber_h_ |