/* -*- 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/. */ /* Provides saturation arithmetics for scalar types. */ #ifndef mozilla_Saturate_h #define mozilla_Saturate_h #include #include #include #include "mozilla/Attributes.h" #include "mozilla/TypeTraits.h" namespace mozilla { namespace detail { /** * |SaturateOp| wraps scalar values for saturation arithmetics. Usage: * * uint32_t value = 1; * * ++SaturateOp(value); // value is 2 * --SaturateOp(value); // value is 1 * --SaturateOp(value); // value is 0 * --SaturateOp(value); // value is still 0 * * Please add new operators when required. * * |SaturateOp| will saturate at the minimum and maximum values of * type T. If you need other bounds, implement a clamped-type class and * specialize the type traits accordingly. */ template class SaturateOp { public: explicit SaturateOp(T& aValue) : mValue(aValue) { // We should actually check for |std::is_scalar::value| to be // true, but this type trait is not available everywhere. Relax // this assertion if you want to use floating point values as well. static_assert(std::is_integral_v, "Integral type required in instantiation"); } // Add and subtract operators T operator+(const T& aRhs) const { return T(mValue) += aRhs; } T operator-(const T& aRhs) const { return T(mValue) -= aRhs; } // Compound operators const T& operator+=(const T& aRhs) const { const T min = std::numeric_limits::min(); const T max = std::numeric_limits::max(); if (aRhs > static_cast(0)) { mValue = (max - aRhs) < mValue ? max : mValue + aRhs; } else { mValue = (min - aRhs) > mValue ? min : mValue + aRhs; } return mValue; } const T& operator-=(const T& aRhs) const { const T min = std::numeric_limits::min(); const T max = std::numeric_limits::max(); if (aRhs > static_cast(0)) { mValue = (min + aRhs) > mValue ? min : mValue - aRhs; } else { mValue = (max + aRhs) < mValue ? max : mValue - aRhs; } return mValue; } // Increment and decrement operators const T& operator++() const // prefix { return operator+=(static_cast(1)); } T operator++(int) const // postfix { const T value(mValue); operator++(); return value; } const T& operator--() const // prefix { return operator-=(static_cast(1)); } T operator--(int) const // postfix { const T value(mValue); operator--(); return value; } private: SaturateOp(const SaturateOp&) = delete; SaturateOp(SaturateOp&&) = delete; SaturateOp& operator=(const SaturateOp&) = delete; SaturateOp& operator=(SaturateOp&&) = delete; T& mValue; }; /** * |Saturate| is a value type for saturation arithmetics. It's * built on top of |SaturateOp|. */ template class Saturate { public: Saturate() = default; MOZ_IMPLICIT Saturate(const Saturate&) = default; MOZ_IMPLICIT Saturate(Saturate&& aValue) { mValue = std::move(aValue.mValue); } explicit Saturate(const T& aValue) : mValue(aValue) {} const T& value() const { return mValue; } // Compare operators bool operator==(const Saturate& aRhs) const { return mValue == aRhs.mValue; } bool operator!=(const Saturate& aRhs) const { return !operator==(aRhs); } bool operator==(const T& aRhs) const { return mValue == aRhs; } bool operator!=(const T& aRhs) const { return !operator==(aRhs); } // Assignment operators Saturate& operator=(const Saturate&) = default; Saturate& operator=(Saturate&& aRhs) { mValue = std::move(aRhs.mValue); return *this; } // Add and subtract operators Saturate operator+(const Saturate& aRhs) const { Saturate lhs(mValue); return lhs += aRhs.mValue; } Saturate operator+(const T& aRhs) const { Saturate lhs(mValue); return lhs += aRhs; } Saturate operator-(const Saturate& aRhs) const { Saturate lhs(mValue); return lhs -= aRhs.mValue; } Saturate operator-(const T& aRhs) const { Saturate lhs(mValue); return lhs -= aRhs; } // Compound operators Saturate& operator+=(const Saturate& aRhs) { SaturateOp(mValue) += aRhs.mValue; return *this; } Saturate& operator+=(const T& aRhs) { SaturateOp(mValue) += aRhs; return *this; } Saturate& operator-=(const Saturate& aRhs) { SaturateOp(mValue) -= aRhs.mValue; return *this; } Saturate& operator-=(const T& aRhs) { SaturateOp(mValue) -= aRhs; return *this; } // Increment and decrement operators Saturate& operator++() // prefix { ++SaturateOp(mValue); return *this; } Saturate operator++(int) // postfix { return Saturate(SaturateOp(mValue)++); } Saturate& operator--() // prefix { --SaturateOp(mValue); return *this; } Saturate operator--(int) // postfix { return Saturate(SaturateOp(mValue)--); } private: T mValue; }; } // namespace detail typedef detail::Saturate SaturateInt8; typedef detail::Saturate SaturateInt16; typedef detail::Saturate SaturateInt32; typedef detail::Saturate SaturateUint8; typedef detail::Saturate SaturateUint16; typedef detail::Saturate SaturateUint32; } // namespace mozilla template bool operator==(LhsT aLhs, const mozilla::detail::Saturate& aRhs) { return aRhs.operator==(static_cast(aLhs)); } template bool operator!=(LhsT aLhs, const mozilla::detail::Saturate& aRhs) { return !(aLhs == aRhs); } #endif // mozilla_Saturate_h