summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/temporal/Int96.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/temporal/Int96.h')
-rw-r--r--js/src/builtin/temporal/Int96.h161
1 files changed, 161 insertions, 0 deletions
diff --git a/js/src/builtin/temporal/Int96.h b/js/src/builtin/temporal/Int96.h
new file mode 100644
index 0000000000..dfb0a5c231
--- /dev/null
+++ b/js/src/builtin/temporal/Int96.h
@@ -0,0 +1,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 */