summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/temporal/Int96.h
blob: dfb0a5c231c24e274c92bd03671be4686dc36584 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* -*- 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 builtin_temporal_Int96_h
#define builtin_temporal_Int96_h

#include "mozilla/Assertions.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Maybe.h"

#include <array>
#include <climits>
#include <stddef.h>
#include <stdint.h>
#include <utility>

namespace js::temporal {

/**
 * 96-bit integer with explicit sign. Supports integers in the range
 * [-(2**96 - 1), 2**96 - 1].
 */
class Int96 final {
 public:
  using Digit = uint32_t;
  using TwoDigit = uint64_t;

  // The 96-bit integer is stored as three separate 32-bit integers.
  using Digits = std::array<Digit, 3>;

 private:
  // Unsigned number in the range [0, 0xffff'ffff'ffff'ffff'ffff'ffff].
  //
  // The least significant digit is stored at index 0. The most significant
  // digit is stored at index 2.
  Digits digits = {};

  // Explicit negative sign.
  bool negative = false;

 public:
  // Default constructor initializes to zero.
  constexpr Int96() = default;

  // Create from an 64-bit integer.
  constexpr explicit Int96(int64_t value) : negative(value < 0) {
    // NB: Not std::abs, because std::abs(INT64_MIN) is undefined behavior.
    uint64_t abs = mozilla::Abs(value);
    digits[1] = uint32_t(abs >> 32);
    digits[0] = uint32_t(abs);
  }

  constexpr Int96(Digits digits, bool negative)
      : digits(digits), negative(negative) {
    // Assert zero is non-negative.
    MOZ_ASSERT_IF((digits[0] | digits[1] | digits[2]) == 0, !negative);
  }

  constexpr bool operator==(const Int96& other) const {
    return digits[0] == other.digits[0] && digits[1] == other.digits[1] &&
           digits[2] == other.digits[2] && negative == other.negative;
  }

  constexpr bool operator<(const Int96& other) const {
    if (negative != other.negative) {
      return negative;
    }
    for (size_t i = digits.size(); i != 0; --i) {
      Digit x = digits[i - 1];
      Digit y = other.digits[i - 1];
      if (x != y) {
        return negative ? x > y : x < y;
      }
    }
    return false;
  }

  // Other operators are implemented in terms of operator== and operator<.
  constexpr bool operator!=(const Int96& other) const {
    return !(*this == other);
  }
  constexpr bool operator>(const Int96& other) const { return other < *this; }
  constexpr bool operator<=(const Int96& other) const {
    return !(other < *this);
  }
  constexpr bool operator>=(const Int96& other) const {
    return !(*this < other);
  }

  /**
   * Multiply this integer with an multiplier. Overflow is not supported.
   */
  constexpr Int96& operator*=(Digit multiplier) {
    Digit carry = 0;
    for (auto& digit : digits) {
      TwoDigit d = digit;
      d *= multiplier;
      d += carry;

      digit = Digit(d);
      carry = Digit(d >> 32);
    }
    MOZ_ASSERT(carry == 0, "unsupported overflow");

    return *this;
  }

  /**
   * Multiply this integer with an multiplier. Overflow is not supported.
   */
  constexpr Int96 operator*(Digit multiplier) const {
    auto result = *this;
    result *= multiplier;
    return result;
  }

  /**
   * Divide this integer by the divisor using Euclidean division. The divisor
   * must be smaller than the most significant digit of the integer. Returns the
   * quotient and the remainder.
   */
  constexpr std::pair<int64_t, int32_t> operator/(Digit divisor) const {
    MOZ_ASSERT(digits[2] < divisor, "unsupported divisor");

    Digit quotient[2] = {};
    Digit remainder = digits[2];
    for (int32_t i = 1; i >= 0; i--) {
      TwoDigit n = (TwoDigit(remainder) << 32) | digits[i];
      quotient[i] = n / divisor;
      remainder = n % divisor;
    }

    int64_t result = (TwoDigit(quotient[1]) << 32) | quotient[0];
    if (negative) {
      result *= -1;
      if (remainder != 0) {
        result -= 1;
        remainder = divisor - remainder;
      }
    }
    return {result, int32_t(remainder)};
  }

  /**
   * Return the absolute value of this integer.
   */
  constexpr Int96 abs() const { return {digits, false}; }

  /**
   * Return Some(Int96) if the integer value fits into a 96-bit integer.
   * Otherwise returns Nothing().
   */
  static mozilla::Maybe<Int96> fromInteger(double value);
};

} /* namespace js::temporal */

#endif /* builtin_temporal_Int96_h */