diff options
Diffstat (limited to 'xpcom/ds/StickyTimeDuration.h')
-rw-r--r-- | xpcom/ds/StickyTimeDuration.h | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/xpcom/ds/StickyTimeDuration.h b/xpcom/ds/StickyTimeDuration.h new file mode 100644 index 0000000000..ce4e8c1e69 --- /dev/null +++ b/xpcom/ds/StickyTimeDuration.h @@ -0,0 +1,239 @@ +/* -*- 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_StickyTimeDuration_h +#define mozilla_StickyTimeDuration_h + +#include <cmath> +#include "mozilla/TimeStamp.h" +#include "mozilla/FloatingPoint.h" + +namespace mozilla { + +/** + * A ValueCalculator class that performs additional checks before performing + * arithmetic operations such that if either operand is Forever (or the + * negative equivalent) the result remains Forever (or the negative equivalent + * as appropriate). + * + * Currently this only checks if either argument to each operation is + * Forever/-Forever. However, it is possible that, for example, + * aA + aB > INT64_MAX (or < INT64_MIN). + * + * We currently don't check for that case since we don't expect that to + * happen often except under test conditions in which case the wrapping + * behavior is probably acceptable. + */ +class StickyTimeDurationValueCalculator { + public: + static int64_t Add(int64_t aA, int64_t aB) { + MOZ_ASSERT((aA != INT64_MAX || aB != INT64_MIN) && + (aA != INT64_MIN || aB != INT64_MAX), + "'Infinity + -Infinity' and '-Infinity + Infinity'" + " are undefined"); + + // Forever + x = Forever + // x + Forever = Forever + if (aA == INT64_MAX || aB == INT64_MAX) { + return INT64_MAX; + } + // -Forever + x = -Forever + // x + -Forever = -Forever + if (aA == INT64_MIN || aB == INT64_MIN) { + return INT64_MIN; + } + + return aA + aB; + } + + // Note that we can't just define Add and have BaseTimeDuration call Add with + // negative arguments since INT64_MAX != -INT64_MIN so the saturating logic + // won't work. + static int64_t Subtract(int64_t aA, int64_t aB) { + MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) || aA != aB, + "'Infinity - Infinity' and '-Infinity - -Infinity'" + " are undefined"); + + // Forever - x = Forever + // x - -Forever = Forever + if (aA == INT64_MAX || aB == INT64_MIN) { + return INT64_MAX; + } + // -Forever - x = -Forever + // x - Forever = -Forever + if (aA == INT64_MIN || aB == INT64_MAX) { + return INT64_MIN; + } + + return aA - aB; + } + + template <typename T> + static int64_t Multiply(int64_t aA, T aB) { + // Specializations for double, float, and int64_t are provided following. + return Multiply(aA, static_cast<int64_t>(aB)); + } + + static int64_t Divide(int64_t aA, int64_t aB) { + MOZ_ASSERT(aB != 0, "Division by zero"); + MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) || + (aB != INT64_MAX && aB != INT64_MIN), + "Dividing +/-Infinity by +/-Infinity is undefined"); + + // Forever / +x = Forever + // Forever / -x = -Forever + // -Forever / +x = -Forever + // -Forever / -x = Forever + if (aA == INT64_MAX || aA == INT64_MIN) { + return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX; + } + // x / Forever = 0 + // x / -Forever = 0 + if (aB == INT64_MAX || aB == INT64_MIN) { + return 0; + } + + return aA / aB; + } + + static double DivideDouble(int64_t aA, int64_t aB) { + MOZ_ASSERT(aB != 0, "Division by zero"); + MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) || + (aB != INT64_MAX && aB != INT64_MIN), + "Dividing +/-Infinity by +/-Infinity is undefined"); + + // Forever / +x = Forever + // Forever / -x = -Forever + // -Forever / +x = -Forever + // -Forever / -x = Forever + if (aA == INT64_MAX || aA == INT64_MIN) { + return (aA >= 0) ^ (aB >= 0) ? NegativeInfinity<double>() + : PositiveInfinity<double>(); + } + // x / Forever = 0 + // x / -Forever = 0 + if (aB == INT64_MAX || aB == INT64_MIN) { + return 0.0; + } + + return static_cast<double>(aA) / aB; + } + + static int64_t Modulo(int64_t aA, int64_t aB) { + MOZ_ASSERT(aA != INT64_MAX && aA != INT64_MIN, + "Infinity modulo x is undefined"); + + return aA % aB; + } +}; + +template <> +inline int64_t StickyTimeDurationValueCalculator::Multiply<int64_t>( + int64_t aA, int64_t aB) { + MOZ_ASSERT((aA != 0 || (aB != INT64_MIN && aB != INT64_MAX)) && + ((aA != INT64_MIN && aA != INT64_MAX) || aB != 0), + "Multiplication of infinity by zero"); + + // Forever * +x = Forever + // Forever * -x = -Forever + // -Forever * +x = -Forever + // -Forever * -x = Forever + // + // i.e. If one or more of the arguments is +/-Forever, then + // return -Forever if the signs differ, or +Forever otherwise. + if (aA == INT64_MAX || aA == INT64_MIN || aB == INT64_MAX || + aB == INT64_MIN) { + return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX; + } + + return aA * aB; +} + +template <> +inline int64_t StickyTimeDurationValueCalculator::Multiply<double>(int64_t aA, + double aB) { + MOZ_ASSERT((aA != 0 || (!std::isinf(aB))) && + ((aA != INT64_MIN && aA != INT64_MAX) || aB != 0.0), + "Multiplication of infinity by zero"); + + // As with Multiply<int64_t>, if one or more of the arguments is + // +/-Forever or +/-Infinity, then return -Forever if the signs differ, + // or +Forever otherwise. + if (aA == INT64_MAX || aA == INT64_MIN || std::isinf(aB)) { + return (aA >= 0) ^ (aB >= 0.0) ? INT64_MIN : INT64_MAX; + } + + return aA * aB; +} + +template <> +inline int64_t StickyTimeDurationValueCalculator::Multiply<float>(int64_t aA, + float aB) { + MOZ_ASSERT(std::isinf(aB) == std::isinf(static_cast<double>(aB)), + "Casting to float loses infinite-ness"); + + return Multiply(aA, static_cast<double>(aB)); +} + +/** + * Specialization of BaseTimeDuration that uses + * StickyTimeDurationValueCalculator for arithmetic on the mValue member. + * + * Use this class when you need a time duration that is expected to hold values + * of Forever (or the negative equivalent) *and* when you expect that + * time duration to be used in arithmetic operations (and not just value + * comparisons). + */ +typedef BaseTimeDuration<StickyTimeDurationValueCalculator> StickyTimeDuration; + +// Template specializations to allow arithmetic between StickyTimeDuration +// and TimeDuration objects by falling back to the safe behavior. +inline StickyTimeDuration operator+(const TimeDuration& aA, + const StickyTimeDuration& aB) { + return StickyTimeDuration(aA) + aB; +} +inline StickyTimeDuration operator+(const StickyTimeDuration& aA, + const TimeDuration& aB) { + return aA + StickyTimeDuration(aB); +} + +inline StickyTimeDuration operator-(const TimeDuration& aA, + const StickyTimeDuration& aB) { + return StickyTimeDuration(aA) - aB; +} +inline StickyTimeDuration operator-(const StickyTimeDuration& aA, + const TimeDuration& aB) { + return aA - StickyTimeDuration(aB); +} + +inline StickyTimeDuration& operator+=(StickyTimeDuration& aA, + const TimeDuration& aB) { + return aA += StickyTimeDuration(aB); +} +inline StickyTimeDuration& operator-=(StickyTimeDuration& aA, + const TimeDuration& aB) { + return aA -= StickyTimeDuration(aB); +} + +inline double operator/(const TimeDuration& aA, const StickyTimeDuration& aB) { + return StickyTimeDuration(aA) / aB; +} +inline double operator/(const StickyTimeDuration& aA, const TimeDuration& aB) { + return aA / StickyTimeDuration(aB); +} + +inline StickyTimeDuration operator%(const TimeDuration& aA, + const StickyTimeDuration& aB) { + return StickyTimeDuration(aA) % aB; +} +inline StickyTimeDuration operator%(const StickyTimeDuration& aA, + const TimeDuration& aB) { + return aA % StickyTimeDuration(aB); +} + +} // namespace mozilla + +#endif /* mozilla_StickyTimeDuration_h */ |