mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
fda4ea0ba7
|Saturate<T>| implements saturation arithmetics for arbitrary basic types. Operations on its value won't over- or underflow the type's range.
216 lines
4.1 KiB
C++
216 lines
4.1 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/. */
|
|
|
|
#include <mozilla/Saturate.h>
|
|
|
|
#include <mozilla/Assertions.h>
|
|
#include <mozilla/NumericLimits.h>
|
|
|
|
using mozilla::detail::Saturate;
|
|
using mozilla::NumericLimits;
|
|
|
|
#define A(a) MOZ_RELEASE_ASSERT(a, "Test \'" #a "\' failed.")
|
|
|
|
static const unsigned long sNumOps = 32;
|
|
|
|
template<typename T>
|
|
static T
|
|
StartValue()
|
|
{
|
|
// Specialize |StartValue| for the given type.
|
|
A(false);
|
|
}
|
|
|
|
template<>
|
|
int8_t
|
|
StartValue<int8_t>()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
template<>
|
|
int16_t
|
|
StartValue<int16_t>()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
template<>
|
|
int32_t
|
|
StartValue<int32_t>()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
template<>
|
|
uint8_t
|
|
StartValue<uint8_t>()
|
|
{
|
|
// Picking a value near middle of uint8_t's range.
|
|
return static_cast<uint8_t>(NumericLimits<int8_t>::max());
|
|
}
|
|
|
|
template<>
|
|
uint16_t
|
|
StartValue<uint16_t>()
|
|
{
|
|
// Picking a value near middle of uint16_t's range.
|
|
return static_cast<uint8_t>(NumericLimits<int16_t>::max());
|
|
}
|
|
|
|
template<>
|
|
uint32_t
|
|
StartValue<uint32_t>()
|
|
{
|
|
// Picking a value near middle of uint32_t's range.
|
|
return static_cast<uint8_t>(NumericLimits<int32_t>::max());
|
|
}
|
|
|
|
// Add
|
|
//
|
|
|
|
template<typename T>
|
|
static void
|
|
TestPrefixIncr()
|
|
{
|
|
T value = StartValue<T>();
|
|
Saturate<T> satValue(value);
|
|
|
|
for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
|
|
A(++value == ++satValue);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
static void
|
|
TestPostfixIncr()
|
|
{
|
|
T value = StartValue<T>();
|
|
Saturate<T> satValue(value);
|
|
|
|
for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
|
|
A(value++ == satValue++);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
static void
|
|
TestAdd()
|
|
{
|
|
T value = StartValue<T>();
|
|
Saturate<T> satValue(value);
|
|
|
|
for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
|
|
A((value + i) == (satValue + i));
|
|
}
|
|
}
|
|
|
|
// Subtract
|
|
//
|
|
|
|
template<typename T>
|
|
static void
|
|
TestPrefixDecr()
|
|
{
|
|
T value = StartValue<T>();
|
|
Saturate<T> satValue(value);
|
|
|
|
for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
|
|
A(--value == --satValue);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
static void
|
|
TestPostfixDecr()
|
|
{
|
|
T value = StartValue<T>();
|
|
Saturate<T> satValue(value);
|
|
|
|
for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
|
|
A(value-- == satValue--);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
static void
|
|
TestSub()
|
|
{
|
|
T value = StartValue<T>();
|
|
Saturate<T> satValue(value);
|
|
|
|
for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
|
|
A((value - i) == (satValue - i));
|
|
}
|
|
}
|
|
|
|
// Corner cases near bounds
|
|
//
|
|
|
|
template<typename T>
|
|
static void
|
|
TestUpperBound()
|
|
{
|
|
Saturate<T> satValue(NumericLimits<T>::max());
|
|
|
|
A(--satValue == (NumericLimits<T>::max() - 1));
|
|
A(++satValue == (NumericLimits<T>::max()));
|
|
A(++satValue == (NumericLimits<T>::max())); // don't overflow here
|
|
A(++satValue == (NumericLimits<T>::max())); // don't overflow here
|
|
A(--satValue == (NumericLimits<T>::max() - 1)); // back at (max - 1)
|
|
A(--satValue == (NumericLimits<T>::max() - 2));
|
|
}
|
|
|
|
template<typename T>
|
|
static void
|
|
TestLowerBound()
|
|
{
|
|
Saturate<T> satValue(NumericLimits<T>::min());
|
|
|
|
A(++satValue == (NumericLimits<T>::min() + 1));
|
|
A(--satValue == (NumericLimits<T>::min()));
|
|
A(--satValue == (NumericLimits<T>::min())); // don't overflow here
|
|
A(--satValue == (NumericLimits<T>::min())); // don't overflow here
|
|
A(++satValue == (NumericLimits<T>::min() + 1)); // back at (max + 1)
|
|
A(++satValue == (NumericLimits<T>::min() + 2));
|
|
}
|
|
|
|
// Framework
|
|
//
|
|
|
|
template<typename T>
|
|
static void
|
|
TestAll()
|
|
{
|
|
// Assert that we don't accidently hit type's range limits in tests.
|
|
const T value = StartValue<T>();
|
|
A(NumericLimits<T>::min() + static_cast<T>(sNumOps) <= value);
|
|
A(NumericLimits<T>::max() - static_cast<T>(sNumOps) >= value);
|
|
|
|
TestPrefixIncr<T>();
|
|
TestPostfixIncr<T>();
|
|
TestAdd<T>();
|
|
|
|
TestPrefixDecr<T>();
|
|
TestPostfixDecr<T>();
|
|
TestSub<T>();
|
|
|
|
TestUpperBound<T>();
|
|
TestLowerBound<T>();
|
|
}
|
|
|
|
int
|
|
main()
|
|
{
|
|
TestAll<int8_t>();
|
|
TestAll<int16_t>();
|
|
TestAll<int32_t>();
|
|
TestAll<uint8_t>();
|
|
TestAll<uint16_t>();
|
|
TestAll<uint32_t>();
|
|
return 0;
|
|
}
|