Skip to content

Commit ce7c06d

Browse files
committed
add bigint class
1 parent 9816197 commit ce7c06d

File tree

10 files changed

+424
-2
lines changed

10 files changed

+424
-2
lines changed

doc/bigint.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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 `napi_env` Environment
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 `napi_env` Environment
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+
### Constructor
39+
40+
```cpp
41+
Napi::BigInt();
42+
```
43+
44+
Returns a new empty JavaScript BigInt.
45+
46+
47+
### Int64Value
48+
49+
```cpp
50+
int64_t Int64Value(bool* lossless) const;
51+
```
52+
53+
- `[out] lossless`: Indicates whether the BigInt value was converted
54+
losslessly.
55+
56+
This API returns the C `int64_t` primitive equivalent of the given JavaScript
57+
BigInt. If needed it will truncate the value, setting lossless to false.
58+
59+
### Uint64Value
60+
61+
```cpp
62+
uint64_t Uint64Value(bool* lossless) const;
63+
```
64+
65+
- `[out] lossless`: Indicates whether the BigInt value was converted
66+
losslessly.
67+
68+
This API returns the C `uint64_t` primitive equivalent of the given JavaScript
69+
BigInt. If needed it will truncate the value, setting lossless to false.
70+
71+
### WordCount
72+
73+
```cpp
74+
size_t WordCount() const;
75+
```
76+
77+
Returns the number of words needed to store this BigInt value.
78+
79+
### ToWords
80+
81+
```cpp
82+
void ToWords(size_t* word_count, int* sign_bit, uint64_t* words);
83+
```
84+
85+
- `[out] sign_bit`: Integer representing if the JavaScript BigInt is positive
86+
or negative.
87+
- `[in/out] word_count`: Must be initialized to the length of the words array.
88+
Upon return, it will be set to the actual number of words that would be
89+
needed to store this BigInt.
90+
- `[out] words`: Pointer to a pre-allocated 64-bit word array.
91+
92+
This API converts a single BigInt value into a sign bit, 64-bit little-endian
93+
array, 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
@@ -287,6 +287,12 @@ inline bool Value::IsNumber() const {
287287
return Type() == napi_number;
288288
}
289289

290+
#ifdef NAPI_EXPERIMENTAL
291+
inline bool Value::IsBigInt() const {
292+
return Type() == napi_bigint;
293+
}
294+
#endif // NAPI_EXPERIMENTAL
295+
290296
inline bool Value::IsString() const {
291297
return Type() == napi_string;
292298
}
@@ -505,6 +511,69 @@ inline double Number::DoubleValue() const {
505511
return result;
506512
}
507513

514+
#ifdef NAPI_EXPERIMENTAL
515+
////////////////////////////////////////////////////////////////////////////////
516+
// BigInt Class
517+
////////////////////////////////////////////////////////////////////////////////
518+
519+
inline BigInt BigInt::New(napi_env env, int64_t val) {
520+
napi_value value;
521+
napi_status status = napi_create_bigint_int64(env, val, &value);
522+
NAPI_THROW_IF_FAILED(env, status, BigInt());
523+
return BigInt(env, value);
524+
}
525+
526+
inline BigInt BigInt::New(napi_env env, uint64_t val) {
527+
napi_value value;
528+
napi_status status = napi_create_bigint_uint64(env, val, &value);
529+
NAPI_THROW_IF_FAILED(env, status, BigInt());
530+
return BigInt(env, value);
531+
}
532+
533+
inline BigInt BigInt::New(napi_env env, int sign_bit, size_t word_count, const uint64_t* words) {
534+
napi_value value;
535+
napi_status status = napi_create_bigint_words(env, sign_bit, word_count, words, &value);
536+
NAPI_THROW_IF_FAILED(env, status, BigInt());
537+
return BigInt(env, value);
538+
}
539+
540+
inline BigInt::BigInt() : Value() {
541+
}
542+
543+
inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) {
544+
}
545+
546+
inline int64_t BigInt::Int64Value(bool* lossless) const {
547+
int64_t result;
548+
napi_status status = napi_get_value_bigint_int64(
549+
_env, _value, &result, lossless);
550+
NAPI_THROW_IF_FAILED(_env, status, 0);
551+
return result;
552+
}
553+
554+
inline uint64_t BigInt::Uint64Value(bool* lossless) const {
555+
uint64_t result;
556+
napi_status status = napi_get_value_bigint_uint64(
557+
_env, _value, &result, lossless);
558+
NAPI_THROW_IF_FAILED(_env, status, 0);
559+
return result;
560+
}
561+
562+
inline size_t BigInt::WordCount() const {
563+
size_t word_count;
564+
napi_status status = napi_get_value_bigint_words(
565+
_env, _value, nullptr, &word_count, nullptr);
566+
NAPI_THROW_IF_FAILED(_env, status, 0);
567+
return word_count;
568+
}
569+
570+
inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) {
571+
napi_status status = napi_get_value_bigint_words(
572+
_env, _value, sign_bit, word_count, words);
573+
NAPI_THROW_IF_FAILED(_env, status);
574+
}
575+
#endif // NAPI_EXPERIMENTAL
576+
508577
////////////////////////////////////////////////////////////////////////////////
509578
// Name class
510579
////////////////////////////////////////////////////////////////////////////////

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
@@ -1551,9 +1606,9 @@ namespace Napi {
15511606
std::string _error;
15521607
};
15531608

1554-
// Memory management.
1609+
// Memory management.
15551610
class MemoryManagement {
1556-
public:
1611+
public:
15571612
static int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes);
15581613
};
15591614

test/bigint.cc

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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
51+
}
52+
53+
return BigInt::New(info.Env(), sign_bit, word_count, words);
54+
}
55+
56+
Value TestTooBigBigInt(const CallbackInfo& info) {
57+
int sign_bit = 0;
58+
size_t word_count = SIZE_MAX;
59+
uint64_t words[10];
60+
61+
return BigInt::New(info.Env(), sign_bit, word_count, words);
62+
}
63+
64+
} // anonymous namespace
65+
66+
Object InitBigInt(Env env) {
67+
Object exports = Object::New(env);
68+
exports["IsLossless"] = Function::New(env, IsLossless);
69+
exports["TestInt64"] = Function::New(env, TestInt64);
70+
exports["TestUint64"] = Function::New(env, TestUint64);
71+
exports["TestWords"] = Function::New(env, TestWords);
72+
exports["TestTooBigBigInt"] = Function::New(env, TestTooBigBigInt);
73+
74+
return exports;
75+
}

0 commit comments

Comments
 (0)