summaryrefslogtreecommitdiffstats
path: root/js/public/BigInt.h
blob: 94f4aab5c42d451c80a98b36395dce81fef6cb9c (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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

/* BigInt. */

#ifndef js_BigInt_h
#define js_BigInt_h

#include "mozilla/Span.h"  // mozilla::Span

#include <limits>       // std::numeric_limits
#include <stdint.h>     // int64_t, uint64_t
#include <type_traits>  // std::enable_if_t, std::{true,false}_type, std::is_{integral,signed,unsigned}_v

#include "jstypes.h"  // JS_PUBLIC_API
#include "js/TypeDecls.h"

namespace mozilla {
template <typename T>
class Range;
}

namespace JS {

class JS_PUBLIC_API BigInt;

namespace detail {

using Int64Limits = std::numeric_limits<int64_t>;
using Uint64Limits = std::numeric_limits<uint64_t>;

extern JS_PUBLIC_API BigInt* BigIntFromInt64(JSContext* cx, int64_t num);
extern JS_PUBLIC_API BigInt* BigIntFromUint64(JSContext* cx, uint64_t num);
extern JS_PUBLIC_API BigInt* BigIntFromBool(JSContext* cx, bool b);

template <typename T, typename = void>
struct NumberToBigIntConverter;

template <typename SignedIntT>
struct NumberToBigIntConverter<
    SignedIntT,
    std::enable_if_t<
        std::is_integral_v<SignedIntT> && std::is_signed_v<SignedIntT> &&
        Int64Limits::min() <= std::numeric_limits<SignedIntT>::min() &&
        std::numeric_limits<SignedIntT>::max() <= Int64Limits::max()>> {
  static BigInt* convert(JSContext* cx, SignedIntT num) {
    return BigIntFromInt64(cx, num);
  }
};

template <typename UnsignedIntT>
struct NumberToBigIntConverter<
    UnsignedIntT,
    std::enable_if_t<
        std::is_integral_v<UnsignedIntT> && std::is_unsigned_v<UnsignedIntT> &&
        std::numeric_limits<UnsignedIntT>::max() <= Uint64Limits::max()>> {
  static BigInt* convert(JSContext* cx, UnsignedIntT num) {
    return BigIntFromUint64(cx, num);
  }
};

template <>
struct NumberToBigIntConverter<bool> {
  static BigInt* convert(JSContext* cx, bool b) {
    return BigIntFromBool(cx, b);
  }
};

extern JS_PUBLIC_API bool BigIntIsInt64(BigInt* bi, int64_t* result);
extern JS_PUBLIC_API bool BigIntIsUint64(BigInt* bi, uint64_t* result);

template <typename T, typename = void>
struct BigIntToNumberChecker;

template <typename SignedIntT>
struct BigIntToNumberChecker<
    SignedIntT,
    std::enable_if_t<
        std::is_integral_v<SignedIntT> && std::is_signed_v<SignedIntT> &&
        Int64Limits::min() <= std::numeric_limits<SignedIntT>::min() &&
        std::numeric_limits<SignedIntT>::max() <= Int64Limits::max()>> {
  using TypeLimits = std::numeric_limits<SignedIntT>;

  static bool fits(BigInt* bi, SignedIntT* result) {
    int64_t innerResult;
    if (!BigIntIsInt64(bi, &innerResult)) {
      return false;
    }
    if (TypeLimits::min() <= innerResult && innerResult <= TypeLimits::max()) {
      *result = SignedIntT(innerResult);
      return true;
    }
    return false;
  }
};

template <typename UnsignedIntT>
struct BigIntToNumberChecker<
    UnsignedIntT,
    std::enable_if_t<
        std::is_integral_v<UnsignedIntT> && std::is_unsigned_v<UnsignedIntT> &&
        std::numeric_limits<UnsignedIntT>::max() <= Uint64Limits::max()>> {
  static bool fits(BigInt* bi, UnsignedIntT* result) {
    uint64_t innerResult;
    if (!BigIntIsUint64(bi, &innerResult)) {
      return false;
    }
    if (innerResult <= std::numeric_limits<UnsignedIntT>::max()) {
      *result = UnsignedIntT(innerResult);
      return true;
    }
    return false;
  }
};

}  // namespace detail

/**
 * Create a BigInt from an integer value. All integral types not larger than 64
 * bits in size are supported.
 */
template <typename NumericT>
static inline BigInt* NumberToBigInt(JSContext* cx, NumericT val) {
  return detail::NumberToBigIntConverter<NumericT>::convert(cx, val);
}

/**
 * Create a BigInt from a floating-point value. If the number isn't integral
 * (that is, if it's NaN, an infinity, or contains a fractional component),
 * this function returns null and throws an exception.
 *
 * Passing -0.0 will produce the bigint 0n.
 */
extern JS_PUBLIC_API BigInt* NumberToBigInt(JSContext* cx, double num);

/**
 * Create a BigInt by parsing a string using the ECMAScript StringToBigInt
 * algorithm (https://tc39.es/ecma262/#sec-stringtobigint). Latin1 and two-byte
 * character ranges are supported. It may be convenient to use
 * JS::ConstLatin1Chars or JS::ConstTwoByteChars.
 *
 * (StringToBigInt performs parsing similar to that performed by the |Number|
 * global function when passed a string, but it doesn't allow infinities,
 * decimal points, or exponential notation, and neither algorithm allows numeric
 * separators or an 'n' suffix character. This fast-and-loose description is
 * offered purely as a convenience to the reader: see the specification
 * algorithm for exact behavior.)
 *
 * If parsing fails, this function returns null and throws an exception.
 */
extern JS_PUBLIC_API BigInt* StringToBigInt(
    JSContext* cx, mozilla::Range<const Latin1Char> chars);

extern JS_PUBLIC_API BigInt* StringToBigInt(
    JSContext* cx, mozilla::Range<const char16_t> chars);

/**
 * Create a BigInt by parsing a string consisting of an optional sign character
 * followed by one or more alphanumeric ASCII digits in the provided radix.
 *
 * If the radix is not in the range [2, 36], or the string fails to parse, this
 * function returns null and throws an exception.
 */
extern JS_PUBLIC_API BigInt* SimpleStringToBigInt(
    JSContext* cx, mozilla::Span<const char> chars, uint8_t radix);

/**
 * Convert a JS::Value to a BigInt using the ECMAScript ToBigInt algorithm
 * (https://tc39.es/ecma262/#sec-tobigint).
 *
 * (Note in particular that this will throw if passed a value whose type is
 * 'number'. To convert a number to a BigInt, use one of the overloads of
 * JS::NumberToBigInt().)
 */
extern JS_PUBLIC_API BigInt* ToBigInt(JSContext* cx, Handle<Value> val);

/**
 * Convert the given BigInt, modulo 2**64, to a signed 64-bit integer.
 */
extern JS_PUBLIC_API int64_t ToBigInt64(BigInt* bi);

/**
 * Convert the given BigInt, modulo 2**64, to an unsigned 64-bit integer.
 */
extern JS_PUBLIC_API uint64_t ToBigUint64(BigInt* bi);

/**
 * Convert the given BigInt to a Number value as if calling the Number
 * constructor on it
 * (https://tc39.es/ecma262/#sec-number-constructor-number-value). The value
 * may be rounded if it doesn't fit without loss of precision.
 */
extern JS_PUBLIC_API double BigIntToNumber(BigInt* bi);

/**
 * Return true if the given BigInt is negative.
 */
extern JS_PUBLIC_API bool BigIntIsNegative(BigInt* bi);

/**
 * Return true if the given BigInt fits inside the given NumericT type without
 * loss of precision, and store the value in the out parameter. Otherwise return
 * false and leave the value of the out parameter unspecified.
 */
template <typename NumericT>
static inline bool BigIntFits(BigInt* bi, NumericT* out) {
  return detail::BigIntToNumberChecker<NumericT>::fits(bi, out);
}

/**
 * Same as BigIntFits(), but checks if the value fits inside a JS Number value.
 */
extern JS_PUBLIC_API bool BigIntFitsNumber(BigInt* bi, double* out);

/**
 * Convert the given BigInt to a String value as if toString() were called on
 * it.
 *
 * If the radix is not in the range [2, 36], then this function returns null and
 * throws an exception.
 */
extern JS_PUBLIC_API JSString* BigIntToString(JSContext* cx, Handle<BigInt*> bi,
                                              uint8_t radix);

}  // namespace JS

#endif /* js_BigInt_h */