/* -*- 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 "mozilla/Attributes.h" #include "mozilla/Move.h" #include "mozilla/TypeTraits.h" #include <limits> namespace mozilla { namespace detail { /** * |SaturateOp<T>| wraps scalar values for saturation arithmetics. Usage: * * uint32_t value = 1; * * ++SaturateOp<uint32_t>(value); // value is 2 * --SaturateOp<uint32_t>(value); // value is 1 * --SaturateOp<uint32_t>(value); // value is 0 * --SaturateOp<uint32_t>(value); // value is still 0 * * Please add new operators when required. * * |SaturateOp<T>| 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 <typename T> class SaturateOp { public: explicit SaturateOp(T& aValue) : mValue(aValue) { // We should actually check for |std::is_scalar<T>::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(IsIntegral<T>::value, "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<T>::min(); const T max = std::numeric_limits<T>::max(); if (aRhs > static_cast<T>(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<T>::min(); const T max = std::numeric_limits<T>::max(); if (aRhs > static_cast<T>(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<T>(1)); } T operator++(int) const // postfix { const T value(mValue); operator++(); return value; } const T& operator--() const // prefix { return operator-=(static_cast<T>(1)); } T operator--(int) const // postfix { const T value(mValue); operator--(); return value; } private: SaturateOp(const SaturateOp<T>&) = delete; SaturateOp(SaturateOp<T>&&) = delete; SaturateOp& operator=(const SaturateOp<T>&) = delete; SaturateOp& operator=(SaturateOp<T>&&) = delete; T& mValue; }; /** * |Saturate<T>| is a value type for saturation arithmetics. It's * build on top of |SaturateOp<T>|. */ template <typename T> class Saturate { public: Saturate() = default; MOZ_IMPLICIT Saturate(const Saturate<T>&) = default; MOZ_IMPLICIT Saturate(Saturate<T>&& aValue) { mValue = Move(aValue.mValue); } explicit Saturate(const T& aValue) : mValue(aValue) { } const T& value() const { return mValue; } // Compare operators bool operator==(const Saturate<T>& aRhs) const { return mValue == aRhs.mValue; } bool operator!=(const Saturate<T>& 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<T>& operator=(const Saturate<T>&) = default; Saturate<T>& operator=(Saturate<T>&& aRhs) { mValue = Move(aRhs.mValue); return *this; } // Add and subtract operators Saturate<T> operator+(const Saturate<T>& aRhs) const { Saturate<T> lhs(mValue); return lhs += aRhs.mValue; } Saturate<T> operator+(const T& aRhs) const { Saturate<T> lhs(mValue); return lhs += aRhs; } Saturate<T> operator-(const Saturate<T>& aRhs) const { Saturate<T> lhs(mValue); return lhs -= aRhs.mValue; } Saturate<T> operator-(const T& aRhs) const { Saturate<T> lhs(mValue); return lhs -= aRhs; } // Compound operators Saturate<T>& operator+=(const Saturate<T>& aRhs) { SaturateOp<T>(mValue) += aRhs.mValue; return *this; } Saturate<T>& operator+=(const T& aRhs) { SaturateOp<T>(mValue) += aRhs; return *this; } Saturate<T>& operator-=(const Saturate<T>& aRhs) { SaturateOp<T>(mValue) -= aRhs.mValue; return *this; } Saturate<T>& operator-=(const T& aRhs) { SaturateOp<T>(mValue) -= aRhs; return *this; } // Increment and decrement operators Saturate<T>& operator++() // prefix { ++SaturateOp<T>(mValue); return *this; } Saturate<T> operator++(int) // postfix { return Saturate<T>(SaturateOp<T>(mValue)++); } Saturate<T>& operator--() // prefix { --SaturateOp<T>(mValue); return *this; } Saturate<T> operator--(int) // postfix { return Saturate<T>(SaturateOp<T>(mValue)--); } private: T mValue; }; } // namespace detail typedef detail::Saturate<int8_t> SaturateInt8; typedef detail::Saturate<int16_t> SaturateInt16; typedef detail::Saturate<int32_t> SaturateInt32; typedef detail::Saturate<uint8_t> SaturateUint8; typedef detail::Saturate<uint16_t> SaturateUint16; typedef detail::Saturate<uint32_t> SaturateUint32; } // namespace mozilla template<typename LhsT, typename RhsT> bool operator==(LhsT aLhs, const mozilla::detail::Saturate<RhsT>& aRhs) { return aRhs.operator==(static_cast<RhsT>(aLhs)); } template<typename LhsT, typename RhsT> bool operator!=(LhsT aLhs, const mozilla::detail::Saturate<RhsT>& aRhs) { return !(aLhs == aRhs); } #endif // mozilla_Saturate_h