Skip to content

Commit

Permalink
Merge pull request #292 from devsnek/feature/bigint
Browse files Browse the repository at this point in the history
bigint support
  • Loading branch information
devsnek authored Sep 18, 2018
2 parents 622ffae + e44aca9 commit 2009c01
Show file tree
Hide file tree
Showing 10 changed files with 426 additions and 2 deletions.
94 changes: 94 additions & 0 deletions doc/bigint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# BigInt

A JavaScript BigInt value.

## Methods

### New

```cpp
static BigInt New(Napi::Env env, int64_t value);
static BigInt New(Napi::Env env, uint64_t value);
```
- `[in] env`: The environment in which to construct the `BigInt` object.
- `[in] value`: The value the JavaScript `BigInt` will contain
These APIs convert the C `int64_t` and `uint64_t` types to the JavaScript
`BigInt` type.
```cpp
static BigInt New(Napi::Env env,
int sign_bit,
size_t word_count,
const uint64_t* words);
```

- `[in] env`: The environment in which to construct the `BigInt` object.
- `[in] sign_bit`: Determines if the resulting `BigInt` will be positive or negative.
- `[in] word_count`: The length of the words array.
- `[in] words`: An array of `uint64_t` little-endian 64-bit words.

This API converts an array of unsigned 64-bit words into a single `BigInt`
value.

The resulting `BigInt` is calculated as: (–1)<sup>`sign_bit`</sup> (`words[0]`
× (2<sup>64</sup>)<sup>0</sup> + `words[1]` × (2<sup>64</sup>)<sup>1</sup> + …)

Returns a new JavaScript `BigInt`.

### Constructor

```cpp
Napi::BigInt();
```

Returns a new empty JavaScript `BigInt`.

### Int64Value

```cpp
int64_t Int64Value(bool* lossless) const;
```
- `[out] lossless`: Indicates whether the `BigInt` value was converted
losslessly.
Returns the C `int64_t` primitive equivalent of the given JavaScript
`BigInt`. If needed it will truncate the value, setting lossless to false.
### Uint64Value
```cpp
uint64_t Uint64Value(bool* lossless) const;
```

- `[out] lossless`: Indicates whether the `BigInt` value was converted
losslessly.

Returns the C `uint64_t` primitive equivalent of the given JavaScript
`BigInt`. If needed it will truncate the value, setting lossless to false.

### WordCount

```cpp
size_t WordCount() const;
```

Returns the number of words needed to store this `BigInt` value.

### ToWords

```cpp
void ToWords(size_t* word_count, int* sign_bit, uint64_t* words);
```
- `[out] sign_bit`: Integer representing if the JavaScript `BigInt` is positive
or negative.
- `[in/out] word_count`: Must be initialized to the length of the words array.
Upon return, it will be set to the actual number of words that would be
needed to store this `BigInt`.
- `[out] words`: Pointer to a pre-allocated 64-bit word array.
Returns a single `BigInt` value into a sign bit, 64-bit little-endian array,
and the number of elements in the array.
69 changes: 69 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ inline bool Value::IsNumber() const {
return Type() == napi_number;
}

#ifdef NAPI_EXPERIMENTAL
inline bool Value::IsBigInt() const {
return Type() == napi_bigint;
}
#endif // NAPI_EXPERIMENTAL

inline bool Value::IsString() const {
return Type() == napi_string;
}
Expand Down Expand Up @@ -516,6 +522,69 @@ inline double Number::DoubleValue() const {
return result;
}

#ifdef NAPI_EXPERIMENTAL
////////////////////////////////////////////////////////////////////////////////
// BigInt Class
////////////////////////////////////////////////////////////////////////////////

inline BigInt BigInt::New(napi_env env, int64_t val) {
napi_value value;
napi_status status = napi_create_bigint_int64(env, val, &value);
NAPI_THROW_IF_FAILED(env, status, BigInt());
return BigInt(env, value);
}

inline BigInt BigInt::New(napi_env env, uint64_t val) {
napi_value value;
napi_status status = napi_create_bigint_uint64(env, val, &value);
NAPI_THROW_IF_FAILED(env, status, BigInt());
return BigInt(env, value);
}

inline BigInt BigInt::New(napi_env env, int sign_bit, size_t word_count, const uint64_t* words) {
napi_value value;
napi_status status = napi_create_bigint_words(env, sign_bit, word_count, words, &value);
NAPI_THROW_IF_FAILED(env, status, BigInt());
return BigInt(env, value);
}

inline BigInt::BigInt() : Value() {
}

inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) {
}

inline int64_t BigInt::Int64Value(bool* lossless) const {
int64_t result;
napi_status status = napi_get_value_bigint_int64(
_env, _value, &result, lossless);
NAPI_THROW_IF_FAILED(_env, status, 0);
return result;
}

inline uint64_t BigInt::Uint64Value(bool* lossless) const {
uint64_t result;
napi_status status = napi_get_value_bigint_uint64(
_env, _value, &result, lossless);
NAPI_THROW_IF_FAILED(_env, status, 0);
return result;
}

inline size_t BigInt::WordCount() const {
size_t word_count;
napi_status status = napi_get_value_bigint_words(
_env, _value, nullptr, &word_count, nullptr);
NAPI_THROW_IF_FAILED(_env, status, 0);
return word_count;
}

inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) {
napi_status status = napi_get_value_bigint_words(
_env, _value, sign_bit, word_count, words);
NAPI_THROW_IF_FAILED(_env, status);
}
#endif // NAPI_EXPERIMENTAL

////////////////////////////////////////////////////////////////////////////////
// Name class
////////////////////////////////////////////////////////////////////////////////
Expand Down
59 changes: 57 additions & 2 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ namespace Napi {
class Value;
class Boolean;
class Number;
#ifdef NAPI_EXPERIMENTAL
class BigInt;
#endif // NAPI_EXPERIMENTAL
class String;
class Object;
class Array;
Expand All @@ -72,6 +75,10 @@ namespace Napi {
typedef TypedArrayOf<uint32_t> Uint32Array; ///< Typed-array of unsigned 32-bit integers
typedef TypedArrayOf<float> Float32Array; ///< Typed-array of 32-bit floating-point values
typedef TypedArrayOf<double> Float64Array; ///< Typed-array of 64-bit floating-point values
#ifdef NAPI_EXPERIMENTAL
typedef TypedArrayOf<int64_t> BigInt64Array; ///< Typed array of signed 64-bit integers
typedef TypedArrayOf<uint64_t> BigUint64Array; ///< Typed array of unsigned 64-bit integers
#endif // NAPI_EXPERIMENTAL

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

#ifdef NAPI_EXPERIMENTAL
/// A JavaScript bigint value.
class BigInt : public Value {
public:
static BigInt New(
napi_env env, ///< N-API environment
int64_t value ///< Number value
);
static BigInt New(
napi_env env, ///< N-API environment
uint64_t value ///< Number value
);

/// Creates a new BigInt object using a specified sign bit and a
/// specified list of digits/words.
/// The resulting number is calculated as:
/// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...)
static BigInt New(
napi_env env, ///< N-API environment
int sign_bit, ///< Sign bit. 1 if negative.
size_t word_count, ///< Number of words in array
const uint64_t* words ///< Array of words
);

BigInt(); ///< Creates a new _empty_ BigInt instance.
BigInt(napi_env env, napi_value value); ///< Wraps a N-API value primitive.

int64_t Int64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit signed integer value.
uint64_t Uint64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit unsigned integer value.

size_t WordCount() const; ///< The number of 64-bit words needed to store the result of ToWords().

/// Writes the contents of this BigInt to a specified memory location.
/// `sign_bit` must be provided and will be set to 1 if this BigInt is negative.
/// `*word_count` has to be initialized to the length of the `words` array.
/// Upon return, it will be set to the actual number of words that would
/// be needed to store this BigInt (i.e. the return value of `WordCount()`).
void ToWords(int* sign_bit, size_t* word_count, uint64_t* words);
};
#endif // NAPI_EXPERIMENTAL

/// A JavaScript string or symbol value (that can be used as a property name).
class Name : public Value {
public:
Expand Down Expand Up @@ -705,6 +756,10 @@ namespace Napi {
: std::is_same<T, uint32_t>::value ? napi_uint32_array
: std::is_same<T, float>::value ? napi_float32_array
: std::is_same<T, double>::value ? napi_float64_array
#ifdef NAPI_EXPERIMENTAL
: std::is_same<T, int64_t>::value ? napi_bigint64_array
: std::is_same<T, uint64_t>::value ? napi_biguint64_array
#endif // NAPI_EXPERIMENTAL
: unknown_array_type;
}
/// !endcond
Expand Down Expand Up @@ -1552,9 +1607,9 @@ namespace Napi {
std::string _error;
};

// Memory management.
// Memory management.
class MemoryManagement {
public:
public:
static int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes);
};

Expand Down
76 changes: 76 additions & 0 deletions test/bigint.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#define NAPI_EXPERIMENTAL
#include "napi.h"

using namespace Napi;

namespace {

Value IsLossless(const CallbackInfo& info) {
Env env = info.Env();

BigInt big = info[0].As<BigInt>();
bool is_signed = info[1].ToBoolean().Value();

bool lossless;
if (is_signed) {
big.Int64Value(&lossless);
} else {
big.Uint64Value(&lossless);
}

return Boolean::New(env, lossless);
}

Value TestInt64(const CallbackInfo& info) {
bool lossless;
int64_t input = info[0].As<BigInt>().Int64Value(&lossless);

return BigInt::New(info.Env(), input);
}

Value TestUint64(const CallbackInfo& info) {
bool lossless;
uint64_t input = info[0].As<BigInt>().Uint64Value(&lossless);

return BigInt::New(info.Env(), input);
}

Value TestWords(const CallbackInfo& info) {
BigInt big = info[0].As<BigInt>();

size_t expected_word_count = big.WordCount();

int sign_bit;
size_t word_count = 10;
uint64_t words[10];

big.ToWords(&sign_bit, &word_count, words);

if (word_count != expected_word_count) {
Error::New(info.Env(), "word count did not match").ThrowAsJavaScriptException();
return BigInt();
}

return BigInt::New(info.Env(), sign_bit, word_count, words);
}

Value TestTooBigBigInt(const CallbackInfo& info) {
int sign_bit = 0;
size_t word_count = SIZE_MAX;
uint64_t words[10];

return BigInt::New(info.Env(), sign_bit, word_count, words);
}

} // anonymous namespace

Object InitBigInt(Env env) {
Object exports = Object::New(env);
exports["IsLossless"] = Function::New(env, IsLossless);
exports["TestInt64"] = Function::New(env, TestInt64);
exports["TestUint64"] = Function::New(env, TestUint64);
exports["TestWords"] = Function::New(env, TestWords);
exports["TestTooBigBigInt"] = Function::New(env, TestTooBigBigInt);

return exports;
}
Loading

0 comments on commit 2009c01

Please sign in to comment.