Skip to content

Commit 2009c01

Browse files
authored
Merge pull request #292 from devsnek/feature/bigint
bigint support
2 parents 622ffae + e44aca9 commit 2009c01

File tree

10 files changed

+426
-2
lines changed

10 files changed

+426
-2
lines changed

doc/bigint.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# BigInt
2+
3+
A JavaScript BigInt value.
4+
5+
## Methods
6+
7+
### New
8+
9+
```cpp
10+
static BigInt New(Napi::Env env, int64_t value);
11+
static BigInt New(Napi::Env env, uint64_t value);
12+
```
13+
14+
- `[in] env`: The environment in which to construct the `BigInt` object.
15+
- `[in] value`: The value the JavaScript `BigInt` will contain
16+
17+
These APIs convert the C `int64_t` and `uint64_t` types to the JavaScript
18+
`BigInt` type.
19+
20+
```cpp
21+
static BigInt New(Napi::Env env,
22+
int sign_bit,
23+
size_t word_count,
24+
const uint64_t* words);
25+
```
26+
27+
- `[in] env`: The environment in which to construct the `BigInt` object.
28+
- `[in] sign_bit`: Determines if the resulting `BigInt` will be positive or negative.
29+
- `[in] word_count`: The length of the words array.
30+
- `[in] words`: An array of `uint64_t` little-endian 64-bit words.
31+
32+
This API converts an array of unsigned 64-bit words into a single `BigInt`
33+
value.
34+
35+
The resulting `BigInt` is calculated as: (–1)<sup>`sign_bit`</sup> (`words[0]`
36+
× (2<sup>64</sup>)<sup>0</sup> + `words[1]` × (2<sup>64</sup>)<sup>1</sup> + …)
37+
38+
Returns a new JavaScript `BigInt`.
39+
40+
### Constructor
41+
42+
```cpp
43+
Napi::BigInt();
44+
```
45+
46+
Returns a new empty JavaScript `BigInt`.
47+
48+
### Int64Value
49+
50+
```cpp
51+
int64_t Int64Value(bool* lossless) const;
52+
```
53+
54+
- `[out] lossless`: Indicates whether the `BigInt` value was converted
55+
losslessly.
56+
57+
Returns the C `int64_t` primitive equivalent of the given JavaScript
58+
`BigInt`. If needed it will truncate the value, setting lossless to false.
59+
60+
### Uint64Value
61+
62+
```cpp
63+
uint64_t Uint64Value(bool* lossless) const;
64+
```
65+
66+
- `[out] lossless`: Indicates whether the `BigInt` value was converted
67+
losslessly.
68+
69+
Returns the C `uint64_t` primitive equivalent of the given JavaScript
70+
`BigInt`. If needed it will truncate the value, setting lossless to false.
71+
72+
### WordCount
73+
74+
```cpp
75+
size_t WordCount() const;
76+
```
77+
78+
Returns the number of words needed to store this `BigInt` value.
79+
80+
### ToWords
81+
82+
```cpp
83+
void ToWords(size_t* word_count, int* sign_bit, uint64_t* words);
84+
```
85+
86+
- `[out] sign_bit`: Integer representing if the JavaScript `BigInt` is positive
87+
or negative.
88+
- `[in/out] word_count`: Must be initialized to the length of the words array.
89+
Upon return, it will be set to the actual number of words that would be
90+
needed to store this `BigInt`.
91+
- `[out] words`: Pointer to a pre-allocated 64-bit word array.
92+
93+
Returns a single `BigInt` value into a sign bit, 64-bit little-endian array,
94+
and the number of elements in the array.

napi-inl.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,12 @@ inline bool Value::IsNumber() const {
298298
return Type() == napi_number;
299299
}
300300

301+
#ifdef NAPI_EXPERIMENTAL
302+
inline bool Value::IsBigInt() const {
303+
return Type() == napi_bigint;
304+
}
305+
#endif // NAPI_EXPERIMENTAL
306+
301307
inline bool Value::IsString() const {
302308
return Type() == napi_string;
303309
}
@@ -516,6 +522,69 @@ inline double Number::DoubleValue() const {
516522
return result;
517523
}
518524

525+
#ifdef NAPI_EXPERIMENTAL
526+
////////////////////////////////////////////////////////////////////////////////
527+
// BigInt Class
528+
////////////////////////////////////////////////////////////////////////////////
529+
530+
inline BigInt BigInt::New(napi_env env, int64_t val) {
531+
napi_value value;
532+
napi_status status = napi_create_bigint_int64(env, val, &value);
533+
NAPI_THROW_IF_FAILED(env, status, BigInt());
534+
return BigInt(env, value);
535+
}
536+
537+
inline BigInt BigInt::New(napi_env env, uint64_t val) {
538+
napi_value value;
539+
napi_status status = napi_create_bigint_uint64(env, val, &value);
540+
NAPI_THROW_IF_FAILED(env, status, BigInt());
541+
return BigInt(env, value);
542+
}
543+
544+
inline BigInt BigInt::New(napi_env env, int sign_bit, size_t word_count, const uint64_t* words) {
545+
napi_value value;
546+
napi_status status = napi_create_bigint_words(env, sign_bit, word_count, words, &value);
547+
NAPI_THROW_IF_FAILED(env, status, BigInt());
548+
return BigInt(env, value);
549+
}
550+
551+
inline BigInt::BigInt() : Value() {
552+
}
553+
554+
inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) {
555+
}
556+
557+
inline int64_t BigInt::Int64Value(bool* lossless) const {
558+
int64_t result;
559+
napi_status status = napi_get_value_bigint_int64(
560+
_env, _value, &result, lossless);
561+
NAPI_THROW_IF_FAILED(_env, status, 0);
562+
return result;
563+
}
564+
565+
inline uint64_t BigInt::Uint64Value(bool* lossless) const {
566+
uint64_t result;
567+
napi_status status = napi_get_value_bigint_uint64(
568+
_env, _value, &result, lossless);
569+
NAPI_THROW_IF_FAILED(_env, status, 0);
570+
return result;
571+
}
572+
573+
inline size_t BigInt::WordCount() const {
574+
size_t word_count;
575+
napi_status status = napi_get_value_bigint_words(
576+
_env, _value, nullptr, &word_count, nullptr);
577+
NAPI_THROW_IF_FAILED(_env, status, 0);
578+
return word_count;
579+
}
580+
581+
inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) {
582+
napi_status status = napi_get_value_bigint_words(
583+
_env, _value, sign_bit, word_count, words);
584+
NAPI_THROW_IF_FAILED(_env, status);
585+
}
586+
#endif // NAPI_EXPERIMENTAL
587+
519588
////////////////////////////////////////////////////////////////////////////////
520589
// Name class
521590
////////////////////////////////////////////////////////////////////////////////

napi.h

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ namespace Napi {
5252
class Value;
5353
class Boolean;
5454
class Number;
55+
#ifdef NAPI_EXPERIMENTAL
56+
class BigInt;
57+
#endif // NAPI_EXPERIMENTAL
5558
class String;
5659
class Object;
5760
class Array;
@@ -72,6 +75,10 @@ namespace Napi {
7275
typedef TypedArrayOf<uint32_t> Uint32Array; ///< Typed-array of unsigned 32-bit integers
7376
typedef TypedArrayOf<float> Float32Array; ///< Typed-array of 32-bit floating-point values
7477
typedef TypedArrayOf<double> Float64Array; ///< Typed-array of 64-bit floating-point values
78+
#ifdef NAPI_EXPERIMENTAL
79+
typedef TypedArrayOf<int64_t> BigInt64Array; ///< Typed array of signed 64-bit integers
80+
typedef TypedArrayOf<uint64_t> BigUint64Array; ///< Typed array of unsigned 64-bit integers
81+
#endif // NAPI_EXPERIMENTAL
7582

7683
/// Defines the signature of a N-API C++ module's registration callback (init) function.
7784
typedef Object (*ModuleRegisterCallback)(Env env, Object exports);
@@ -171,6 +178,9 @@ namespace Napi {
171178
bool IsNull() const; ///< Tests if a value is a null JavaScript value.
172179
bool IsBoolean() const; ///< Tests if a value is a JavaScript boolean.
173180
bool IsNumber() const; ///< Tests if a value is a JavaScript number.
181+
#ifdef NAPI_EXPERIMENTAL
182+
bool IsBigInt() const; ///< Tests if a value is a JavaScript bigint.
183+
#endif // NAPI_EXPERIMENTAL
174184
bool IsString() const; ///< Tests if a value is a JavaScript string.
175185
bool IsSymbol() const; ///< Tests if a value is a JavaScript symbol.
176186
bool IsArray() const; ///< Tests if a value is a JavaScript array.
@@ -242,6 +252,47 @@ namespace Napi {
242252
double DoubleValue() const; ///< Converts a Number value to a 64-bit floating-point value.
243253
};
244254

255+
#ifdef NAPI_EXPERIMENTAL
256+
/// A JavaScript bigint value.
257+
class BigInt : public Value {
258+
public:
259+
static BigInt New(
260+
napi_env env, ///< N-API environment
261+
int64_t value ///< Number value
262+
);
263+
static BigInt New(
264+
napi_env env, ///< N-API environment
265+
uint64_t value ///< Number value
266+
);
267+
268+
/// Creates a new BigInt object using a specified sign bit and a
269+
/// specified list of digits/words.
270+
/// The resulting number is calculated as:
271+
/// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...)
272+
static BigInt New(
273+
napi_env env, ///< N-API environment
274+
int sign_bit, ///< Sign bit. 1 if negative.
275+
size_t word_count, ///< Number of words in array
276+
const uint64_t* words ///< Array of words
277+
);
278+
279+
BigInt(); ///< Creates a new _empty_ BigInt instance.
280+
BigInt(napi_env env, napi_value value); ///< Wraps a N-API value primitive.
281+
282+
int64_t Int64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit signed integer value.
283+
uint64_t Uint64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit unsigned integer value.
284+
285+
size_t WordCount() const; ///< The number of 64-bit words needed to store the result of ToWords().
286+
287+
/// Writes the contents of this BigInt to a specified memory location.
288+
/// `sign_bit` must be provided and will be set to 1 if this BigInt is negative.
289+
/// `*word_count` has to be initialized to the length of the `words` array.
290+
/// Upon return, it will be set to the actual number of words that would
291+
/// be needed to store this BigInt (i.e. the return value of `WordCount()`).
292+
void ToWords(int* sign_bit, size_t* word_count, uint64_t* words);
293+
};
294+
#endif // NAPI_EXPERIMENTAL
295+
245296
/// A JavaScript string or symbol value (that can be used as a property name).
246297
class Name : public Value {
247298
public:
@@ -705,6 +756,10 @@ namespace Napi {
705756
: std::is_same<T, uint32_t>::value ? napi_uint32_array
706757
: std::is_same<T, float>::value ? napi_float32_array
707758
: std::is_same<T, double>::value ? napi_float64_array
759+
#ifdef NAPI_EXPERIMENTAL
760+
: std::is_same<T, int64_t>::value ? napi_bigint64_array
761+
: std::is_same<T, uint64_t>::value ? napi_biguint64_array
762+
#endif // NAPI_EXPERIMENTAL
708763
: unknown_array_type;
709764
}
710765
/// !endcond
@@ -1552,9 +1607,9 @@ namespace Napi {
15521607
std::string _error;
15531608
};
15541609

1555-
// Memory management.
1610+
// Memory management.
15561611
class MemoryManagement {
1557-
public:
1612+
public:
15581613
static int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes);
15591614
};
15601615

test/bigint.cc

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#define NAPI_EXPERIMENTAL
2+
#include "napi.h"
3+
4+
using namespace Napi;
5+
6+
namespace {
7+
8+
Value IsLossless(const CallbackInfo& info) {
9+
Env env = info.Env();
10+
11+
BigInt big = info[0].As<BigInt>();
12+
bool is_signed = info[1].ToBoolean().Value();
13+
14+
bool lossless;
15+
if (is_signed) {
16+
big.Int64Value(&lossless);
17+
} else {
18+
big.Uint64Value(&lossless);
19+
}
20+
21+
return Boolean::New(env, lossless);
22+
}
23+
24+
Value TestInt64(const CallbackInfo& info) {
25+
bool lossless;
26+
int64_t input = info[0].As<BigInt>().Int64Value(&lossless);
27+
28+
return BigInt::New(info.Env(), input);
29+
}
30+
31+
Value TestUint64(const CallbackInfo& info) {
32+
bool lossless;
33+
uint64_t input = info[0].As<BigInt>().Uint64Value(&lossless);
34+
35+
return BigInt::New(info.Env(), input);
36+
}
37+
38+
Value TestWords(const CallbackInfo& info) {
39+
BigInt big = info[0].As<BigInt>();
40+
41+
size_t expected_word_count = big.WordCount();
42+
43+
int sign_bit;
44+
size_t word_count = 10;
45+
uint64_t words[10];
46+
47+
big.ToWords(&sign_bit, &word_count, words);
48+
49+
if (word_count != expected_word_count) {
50+
Error::New(info.Env(), "word count did not match").ThrowAsJavaScriptException();
51+
return BigInt();
52+
}
53+
54+
return BigInt::New(info.Env(), sign_bit, word_count, words);
55+
}
56+
57+
Value TestTooBigBigInt(const CallbackInfo& info) {
58+
int sign_bit = 0;
59+
size_t word_count = SIZE_MAX;
60+
uint64_t words[10];
61+
62+
return BigInt::New(info.Env(), sign_bit, word_count, words);
63+
}
64+
65+
} // anonymous namespace
66+
67+
Object InitBigInt(Env env) {
68+
Object exports = Object::New(env);
69+
exports["IsLossless"] = Function::New(env, IsLossless);
70+
exports["TestInt64"] = Function::New(env, TestInt64);
71+
exports["TestUint64"] = Function::New(env, TestUint64);
72+
exports["TestWords"] = Function::New(env, TestWords);
73+
exports["TestTooBigBigInt"] = Function::New(env, TestTooBigBigInt);
74+
75+
return exports;
76+
}

0 commit comments

Comments
 (0)