1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_RollingNumber_h_
8 #define mozilla_RollingNumber_h_
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
16 // Unsigned number suited to index elements in a never-ending queue, as
17 // order-comparison behaves nicely around the overflow.
19 // Additive operators work the same as for the underlying value type, but
20 // expect "small" jumps, as should normally happen when manipulating indices.
22 // Comparison functions are different, they keep the ordering based on the
23 // distance between numbers, modulo the value type range:
24 // If the distance is less than half the range of the value type, the usual
27 // However if the distance is more than half the range, we assume that we are
28 // continuing along the queue, and therefore consider the smaller number to
29 // actually be greater!
32 // The expected usage is to always work on nearby rolling numbers: slowly
33 // incrementing/decrementing them, and translating&comparing them within a
35 // To enforce this usage during development, debug-build assertions catch API
36 // calls involving distances of more than a *quarter* of the type range.
37 // In non-debug builds, all APIs will still work as consistently as possible
38 // without crashing, but performing operations on "distant" nunbers could lead
39 // to unexpected results.
42 static_assert(!std::numeric_limits
<T
>::is_signed
,
43 "RollingNumber only accepts unsigned number types");
48 RollingNumber() : mIndex(0) {}
50 explicit RollingNumber(ValueType aIndex
) : mIndex(aIndex
) {}
52 RollingNumber(const RollingNumber
&) = default;
53 RollingNumber
& operator=(const RollingNumber
&) = default;
55 ValueType
Value() const { return mIndex
; }
57 // Normal increments/decrements.
59 RollingNumber
& operator++() {
64 RollingNumber
operator++(int) { return RollingNumber
{mIndex
++}; }
66 RollingNumber
& operator--() {
71 RollingNumber
operator--(int) { return RollingNumber
{mIndex
--}; }
73 RollingNumber
& operator+=(const ValueType
& aIncrement
) {
74 MOZ_ASSERT(aIncrement
<= MaxDiff
);
79 RollingNumber
operator+(const ValueType
& aIncrement
) const {
80 RollingNumber n
= *this;
81 return n
+= aIncrement
;
84 RollingNumber
& operator-=(const ValueType
& aDecrement
) {
85 MOZ_ASSERT(aDecrement
<= MaxDiff
);
90 // Translate a RollingNumber by a negative value.
91 RollingNumber
operator-(const ValueType
& aDecrement
) const {
92 RollingNumber n
= *this;
93 return n
-= aDecrement
;
96 // Distance between two RollingNumbers, giving a value.
97 ValueType
operator-(const RollingNumber
& aOther
) const {
98 ValueType diff
= mIndex
- aOther
.mIndex
;
99 MOZ_ASSERT(diff
<= MaxDiff
);
103 // Normal (in)equality operators.
105 bool operator==(const RollingNumber
& aOther
) const {
106 return mIndex
== aOther
.mIndex
;
108 bool operator!=(const RollingNumber
& aOther
) const {
109 return !(*this == aOther
);
112 // Modified comparison operators.
114 bool operator<(const RollingNumber
& aOther
) const {
116 const T
& b
= aOther
.mIndex
;
117 // static_cast needed because of possible integer promotion
118 // (e.g., from uint8_t to int, which would make the test useless).
119 const bool lessThanOther
= static_cast<ValueType
>(a
- b
) > MidWay
;
120 MOZ_ASSERT((lessThanOther
? (b
- a
) : (a
- b
)) <= MaxDiff
);
121 return lessThanOther
;
124 bool operator<=(const RollingNumber
& aOther
) const {
126 const T
& b
= aOther
.mIndex
;
127 const bool lessishThanOther
= static_cast<ValueType
>(b
- a
) <= MidWay
;
128 MOZ_ASSERT((lessishThanOther
? (b
- a
) : (a
- b
)) <= MaxDiff
);
129 return lessishThanOther
;
132 bool operator>=(const RollingNumber
& aOther
) const {
134 const T
& b
= aOther
.mIndex
;
135 const bool greaterishThanOther
= static_cast<ValueType
>(a
- b
) <= MidWay
;
136 MOZ_ASSERT((greaterishThanOther
? (a
- b
) : (b
- a
)) <= MaxDiff
);
137 return greaterishThanOther
;
140 bool operator>(const RollingNumber
& aOther
) const {
142 const T
& b
= aOther
.mIndex
;
143 const bool greaterThanOther
= static_cast<ValueType
>(b
- a
) > MidWay
;
144 MOZ_ASSERT((greaterThanOther
? (a
- b
) : (b
- a
)) <= MaxDiff
);
145 return greaterThanOther
;
149 // MidWay is used to split the type range in two, to decide how two numbers
151 static const T MidWay
= std::numeric_limits
<T
>::max() / 2;
153 // MaxDiff is the expected maximum difference between two numbers, either
154 // during comparisons, or when adding/subtracting.
155 // This is only used during debugging, to highlight algorithmic issues.
156 static const T MaxDiff
= std::numeric_limits
<T
>::max() / 4;
161 } // namespace mozilla
163 #endif // mozilla_RollingNumber_h_