зеркало из https://github.com/mozilla/gecko-dev.git
163 строки
5.2 KiB
C++
163 строки
5.2 KiB
C++
/* -*- 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/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_
|