Bug 1039924 part 5 - Add StickyTimeDuration; r=froydnj
authorBrian Birtles <birtles@gmail.com>
Thu, 25 Sep 2014 14:25:50 +0900
changeset 222593 2dd874cbd5f794bbfb6879c14464ef03018275ab
parent 222592 82fbeb45b3d6bb6560ca4ee15138dcc46a2982bf
child 222594 b92289e2e95f203eb60580dba6bccf4119ef7458
push id7107
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 17:43:31 +0000
treeherdermozilla-aurora@b4b34e0acc75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1039924
milestone35.0a1
Bug 1039924 part 5 - Add StickyTimeDuration; r=froydnj This patch adds another implementation of BaseTimeDuration's ValueCalculator template parameter that is careful to preserve Forever/-Forever values when performing arithmetic. It also defines a typedef for a specialization of BaseTimeDuration that uses this new ValueCalculator definition.
xpcom/ds/StickyTimeDuration.h
xpcom/ds/moz.build
new file mode 100644
--- /dev/null
+++ b/xpcom/ds/StickyTimeDuration.h
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "mozilla/TimeStamp.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_MAX : INT64_MIN;
+  }
+
+  return aA * aB;
+}
+
+template <>
+inline int64_t
+StickyTimeDurationValueCalculator::Multiply<double>(int64_t aA, double aB)
+{
+  MOZ_ASSERT((aA != 0 || (!IsInfinite(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 || IsInfinite(aB)) {
+    return (aA >= 0) ^ (aB >= 0.0) ? INT64_MAX : INT64_MIN;
+  }
+
+  return aA * aB;
+}
+
+template <>
+inline int64_t
+StickyTimeDurationValueCalculator::Multiply<float>(int64_t aA, float aB)
+{
+  MOZ_ASSERT(IsInfinite(aB) == IsInfinite(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 */
--- a/xpcom/ds/moz.build
+++ b/xpcom/ds/moz.build
@@ -57,16 +57,17 @@ EXPORTS += [
     'nsStringEnumerator.h',
     'nsSupportsArray.h',
     'nsSupportsPrimitives.h',
     'nsVariant.h',
     'nsWhitespaceTokenizer.h',
 ]
 
 EXPORTS.mozilla += [
+    'StickyTimeDuration.h',
     'TimeStamp.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsArray.cpp',
     'nsAtomService.cpp',
     'nsAtomTable.cpp',
     'nsCRT.cpp',