BigInt.h (7697B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* BigInt. */ 7 8 #ifndef js_BigInt_h 9 #define js_BigInt_h 10 11 #include "mozilla/Span.h" // mozilla::Span 12 13 #include <limits> // std::numeric_limits 14 #include <stdint.h> // int64_t, uint64_t 15 #include <type_traits> // std::enable_if_t, std::{true,false}_type, std::is_{integral,signed,unsigned}_v 16 17 #include "jstypes.h" // JS_PUBLIC_API 18 #include "js/TypeDecls.h" 19 20 namespace mozilla { 21 template <typename T> 22 class Range; 23 } 24 25 namespace JS { 26 27 class JS_PUBLIC_API BigInt; 28 29 namespace detail { 30 31 using Int64Limits = std::numeric_limits<int64_t>; 32 using Uint64Limits = std::numeric_limits<uint64_t>; 33 34 extern JS_PUBLIC_API BigInt* BigIntFromInt64(JSContext* cx, int64_t num); 35 extern JS_PUBLIC_API BigInt* BigIntFromUint64(JSContext* cx, uint64_t num); 36 extern JS_PUBLIC_API BigInt* BigIntFromBool(JSContext* cx, bool b); 37 38 template <typename T, typename = void> 39 struct NumberToBigIntConverter; 40 41 template <typename SignedIntT> 42 struct NumberToBigIntConverter< 43 SignedIntT, 44 std::enable_if_t< 45 std::is_integral_v<SignedIntT> && std::is_signed_v<SignedIntT> && 46 Int64Limits::min() <= std::numeric_limits<SignedIntT>::min() && 47 std::numeric_limits<SignedIntT>::max() <= Int64Limits::max()>> { 48 static BigInt* convert(JSContext* cx, SignedIntT num) { 49 return BigIntFromInt64(cx, num); 50 } 51 }; 52 53 template <typename UnsignedIntT> 54 struct NumberToBigIntConverter< 55 UnsignedIntT, 56 std::enable_if_t< 57 std::is_integral_v<UnsignedIntT> && std::is_unsigned_v<UnsignedIntT> && 58 std::numeric_limits<UnsignedIntT>::max() <= Uint64Limits::max()>> { 59 static BigInt* convert(JSContext* cx, UnsignedIntT num) { 60 return BigIntFromUint64(cx, num); 61 } 62 }; 63 64 template <> 65 struct NumberToBigIntConverter<bool> { 66 static BigInt* convert(JSContext* cx, bool b) { 67 return BigIntFromBool(cx, b); 68 } 69 }; 70 71 extern JS_PUBLIC_API bool BigIntIsInt64(const BigInt* bi, int64_t* result); 72 extern JS_PUBLIC_API bool BigIntIsUint64(const BigInt* bi, uint64_t* result); 73 74 template <typename T, typename = void> 75 struct BigIntToNumberChecker; 76 77 template <typename SignedIntT> 78 struct BigIntToNumberChecker< 79 SignedIntT, 80 std::enable_if_t< 81 std::is_integral_v<SignedIntT> && std::is_signed_v<SignedIntT> && 82 Int64Limits::min() <= std::numeric_limits<SignedIntT>::min() && 83 std::numeric_limits<SignedIntT>::max() <= Int64Limits::max()>> { 84 using TypeLimits = std::numeric_limits<SignedIntT>; 85 86 static bool fits(const BigInt* bi, SignedIntT* result) { 87 int64_t innerResult; 88 if (!BigIntIsInt64(bi, &innerResult)) { 89 return false; 90 } 91 if (TypeLimits::min() <= innerResult && innerResult <= TypeLimits::max()) { 92 *result = SignedIntT(innerResult); 93 return true; 94 } 95 return false; 96 } 97 }; 98 99 template <typename UnsignedIntT> 100 struct BigIntToNumberChecker< 101 UnsignedIntT, 102 std::enable_if_t< 103 std::is_integral_v<UnsignedIntT> && std::is_unsigned_v<UnsignedIntT> && 104 std::numeric_limits<UnsignedIntT>::max() <= Uint64Limits::max()>> { 105 static bool fits(const BigInt* bi, UnsignedIntT* result) { 106 uint64_t innerResult; 107 if (!BigIntIsUint64(bi, &innerResult)) { 108 return false; 109 } 110 if (innerResult <= std::numeric_limits<UnsignedIntT>::max()) { 111 *result = UnsignedIntT(innerResult); 112 return true; 113 } 114 return false; 115 } 116 }; 117 118 } // namespace detail 119 120 /** 121 * Create a BigInt from an integer value. All integral types not larger than 64 122 * bits in size are supported. 123 */ 124 template <typename NumericT> 125 static inline BigInt* NumberToBigInt(JSContext* cx, NumericT val) { 126 return detail::NumberToBigIntConverter<NumericT>::convert(cx, val); 127 } 128 129 /** 130 * Create a BigInt from a floating-point value. If the number isn't integral 131 * (that is, if it's NaN, an infinity, or contains a fractional component), 132 * this function returns null and throws an exception. 133 * 134 * Passing -0.0 will produce the bigint 0n. 135 */ 136 extern JS_PUBLIC_API BigInt* NumberToBigInt(JSContext* cx, double num); 137 138 /** 139 * Create a BigInt by parsing a string using the ECMAScript StringToBigInt 140 * algorithm (https://tc39.es/ecma262/#sec-stringtobigint). Latin1 and two-byte 141 * character ranges are supported. It may be convenient to use 142 * JS::ConstLatin1Chars or JS::ConstTwoByteChars. 143 * 144 * (StringToBigInt performs parsing similar to that performed by the |Number| 145 * global function when passed a string, but it doesn't allow infinities, 146 * decimal points, or exponential notation, and neither algorithm allows numeric 147 * separators or an 'n' suffix character. This fast-and-loose description is 148 * offered purely as a convenience to the reader: see the specification 149 * algorithm for exact behavior.) 150 * 151 * If parsing fails, this function returns null and throws an exception. 152 */ 153 extern JS_PUBLIC_API BigInt* StringToBigInt( 154 JSContext* cx, const mozilla::Range<const Latin1Char>& chars); 155 156 extern JS_PUBLIC_API BigInt* StringToBigInt( 157 JSContext* cx, const mozilla::Range<const char16_t>& chars); 158 159 /** 160 * Create a BigInt by parsing a string consisting of an optional sign character 161 * followed by one or more alphanumeric ASCII digits in the provided radix. 162 * 163 * If the radix is not in the range [2, 36], or the string fails to parse, this 164 * function returns null and throws an exception. 165 */ 166 extern JS_PUBLIC_API BigInt* SimpleStringToBigInt( 167 JSContext* cx, mozilla::Span<const char> chars, uint8_t radix); 168 169 /** 170 * Convert a JS::Value to a BigInt using the ECMAScript ToBigInt algorithm 171 * (https://tc39.es/ecma262/#sec-tobigint). 172 * 173 * (Note in particular that this will throw if passed a value whose type is 174 * 'number'. To convert a number to a BigInt, use one of the overloads of 175 * JS::NumberToBigInt().) 176 */ 177 extern JS_PUBLIC_API BigInt* ToBigInt(JSContext* cx, Handle<Value> val); 178 179 /** 180 * Convert the given BigInt, modulo 2**64, to a signed 64-bit integer. 181 */ 182 extern JS_PUBLIC_API int64_t ToBigInt64(const BigInt* bi); 183 184 /** 185 * Convert the given BigInt, modulo 2**64, to an unsigned 64-bit integer. 186 */ 187 extern JS_PUBLIC_API uint64_t ToBigUint64(const BigInt* bi); 188 189 /** 190 * Convert the given BigInt to a Number value as if calling the Number 191 * constructor on it 192 * (https://tc39.es/ecma262/#sec-number-constructor-number-value). The value 193 * may be rounded if it doesn't fit without loss of precision. 194 */ 195 extern JS_PUBLIC_API double BigIntToNumber(const BigInt* bi); 196 197 /** 198 * Return true if the given BigInt is negative. 199 */ 200 extern JS_PUBLIC_API bool BigIntIsNegative(const BigInt* bi); 201 202 /** 203 * Return true if the given BigInt fits inside the given NumericT type without 204 * loss of precision, and store the value in the out parameter. Otherwise return 205 * false and leave the value of the out parameter unspecified. 206 */ 207 template <typename NumericT> 208 static inline bool BigIntFits(const BigInt* bi, NumericT* out) { 209 return detail::BigIntToNumberChecker<NumericT>::fits(bi, out); 210 } 211 212 /** 213 * Same as BigIntFits(), but checks if the value fits inside a JS Number value. 214 */ 215 extern JS_PUBLIC_API bool BigIntFitsNumber(const BigInt* bi, double* out); 216 217 /** 218 * Convert the given BigInt to a String value as if toString() were called on 219 * it. 220 * 221 * If the radix is not in the range [2, 36], then this function returns null and 222 * throws an exception. 223 */ 224 extern JS_PUBLIC_API JSString* BigIntToString(JSContext* cx, Handle<BigInt*> bi, 225 uint8_t radix); 226 227 } // namespace JS 228 229 #endif /* js_BigInt_h */