Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ The following is the documentation for node-addon-api.
- [ClassPropertyDescriptor](class_property_descriptor.md)
- [Buffer](buffer.md)
- [ArrayBuffer](array_buffer.md)
- [SharedArrayBuffer](shared_array_buffer.md)
- [TypedArray](typed_array.md)
- [TypedArrayOf](typed_array_of.md)
- [DataView](dataview.md)
Expand Down
65 changes: 65 additions & 0 deletions doc/shared_array_buffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# SharedArrayBuffer

Class `Napi::SharedArrayBuffer` inherits from class [`Napi::Object`][].

The `Napi::SharedArrayBuffer` class corresponds to the
[JavaScript `SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)
class.

**NOTE**: The support for `Napi::SharedArrayBuffer` is only available when using
`NAPI_EXPERIMENTAL` and building against Node.js headers that support this
feature.

## Methods

### New

Allocates a new `Napi::SharedArrayBuffer` instance with a given length.

```cpp
static Napi::SharedArrayBuffer Napi::SharedArrayBuffer::New(napi_env env, size_t byteLength);
```

- `[in] env`: The environment in which to create the `Napi::SharedArrayBuffer`
instance.
- `[in] byteLength`: The length to be allocated, in bytes.

Returns a new `Napi::SharedArrayBuffer` instance.

### Constructor

Initializes an empty instance of the `Napi::SharedArrayBuffer` class.

```cpp
Napi::SharedArrayBuffer::SharedArrayBuffer();
```

### Constructor

Initializes a wrapper instance of an existing `Napi::SharedArrayBuffer` object.

```cpp
Napi::SharedArrayBuffer::SharedArrayBuffer(napi_env env, napi_value value);
```

- `[in] env`: The environment in which to create the `Napi::SharedArrayBuffer`
instance.
- `[in] value`: The `Napi::SharedArrayBuffer` reference to wrap.

### ByteLength

```cpp
size_t Napi::SharedArrayBuffer::ByteLength() const;
```

Returns the length of the wrapped data, in bytes.

### Data

```cpp
void* Napi::SharedArrayBuffer::Data() const;
```

Returns a pointer the wrapped data.

[`Napi::Object`]: ./object.md
13 changes: 13 additions & 0 deletions doc/value.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,19 @@ bool Napi::Value::IsPromise() const;
Returns `true` if the underlying value is a JavaScript `Napi::Promise` or
`false` otherwise.

### IsSharedArrayBuffer

```cpp
bool Napi::Value::IsSharedArrayBuffer() const;
```

Returns `true` if the underlying value is a JavaScript
`Napi::IsSharedArrayBuffer` or `false` otherwise.

**NOTE**: The support for `Napi::SharedArrayBuffer` is only available when using
`NAPI_EXPERIMENTAL` and building against Node.js headers that support this
feature.

### IsString

```cpp
Expand Down
62 changes: 62 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,19 @@ inline bool Value::IsExternal() const {
return Type() == napi_external;
}

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
inline bool Value::IsSharedArrayBuffer() const {
if (IsEmpty()) {
return false;
}

bool result;
napi_status status = node_api_is_sharedarraybuffer(_env, _value, &result);
NAPI_THROW_IF_FAILED(_env, status, false);
return result;
}
#endif

template <typename T>
inline T Value::As() const {
#ifdef NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS
Expand Down Expand Up @@ -2068,6 +2081,55 @@ inline uint32_t Array::Length() const {
return result;
}

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
////////////////////////////////////////////////////////////////////////////////
// SharedArrayBuffer class
////////////////////////////////////////////////////////////////////////////////

inline SharedArrayBuffer::SharedArrayBuffer() : Object() {}

inline SharedArrayBuffer::SharedArrayBuffer(napi_env env, napi_value value)
: Object(env, value) {}

inline void SharedArrayBuffer::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "SharedArrayBuffer::CheckCast", "empty value");

bool result;
napi_status status = node_api_is_sharedarraybuffer(env, value, &result);
NAPI_CHECK(status == napi_ok,
"SharedArrayBuffer::CheckCast",
"node_api_is_sharedarraybuffer failed");
NAPI_CHECK(
result, "SharedArrayBuffer::CheckCast", "value is not sharedarraybuffer");
}

inline SharedArrayBuffer SharedArrayBuffer::New(napi_env env,
size_t byteLength) {
napi_value value;
void* data;
napi_status status =
node_api_create_sharedarraybuffer(env, byteLength, &data, &value);
NAPI_THROW_IF_FAILED(env, status, SharedArrayBuffer());

return SharedArrayBuffer(env, value);
}

inline void* SharedArrayBuffer::Data() {
void* data;
napi_status status = napi_get_arraybuffer_info(_env, _value, &data, nullptr);
NAPI_THROW_IF_FAILED(_env, status, nullptr);
return data;
}

inline size_t SharedArrayBuffer::ByteLength() {
size_t length;
napi_status status =
napi_get_arraybuffer_info(_env, _value, nullptr, &length);
NAPI_THROW_IF_FAILED(_env, status, 0);
return length;
}
#endif // NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER

////////////////////////////////////////////////////////////////////////////////
// ArrayBuffer class
////////////////////////////////////////////////////////////////////////////////
Expand Down
18 changes: 18 additions & 0 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,9 @@ class Value {
bool IsDataView() const; ///< Tests if a value is a JavaScript data view.
bool IsBuffer() const; ///< Tests if a value is a Node buffer.
bool IsExternal() const; ///< Tests if a value is a pointer to external data.
#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
bool IsSharedArrayBuffer() const;
#endif

/// Casts to another type of `Napi::Value`, when the actual type is known or
/// assumed.
Expand Down Expand Up @@ -1202,6 +1205,21 @@ class Object::iterator {
};
#endif // NODE_ADDON_API_CPP_EXCEPTIONS

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
class SharedArrayBuffer : public Object {
public:
SharedArrayBuffer();
SharedArrayBuffer(napi_env env, napi_value value);

static SharedArrayBuffer New(napi_env env, size_t byteLength);

static void CheckCast(napi_env env, napi_value value);

void* Data();
size_t ByteLength();
};
#endif

/// A JavaScript array buffer value.
class ArrayBuffer : public Object {
public:
Expand Down
8 changes: 8 additions & 0 deletions test/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Object InitTypedThreadSafeFunctionSum(Env env);
Object InitTypedThreadSafeFunctionUnref(Env env);
Object InitTypedThreadSafeFunction(Env env);
#endif
Object InitSharedArrayBuffer(Env env);
Object InitSymbol(Env env);
Object InitTypedArray(Env env);
Object InitGlobalObject(Env env);
Expand Down Expand Up @@ -140,6 +141,7 @@ Object Init(Env env, Object exports) {
exports.Set("promise", InitPromise(env));
exports.Set("run_script", InitRunScript(env));
exports.Set("symbol", InitSymbol(env));
exports.Set("sharedarraybuffer", InitSharedArrayBuffer(env));
#if (NAPI_VERSION > 3)
exports.Set("threadsafe_function_ctx", InitThreadSafeFunctionCtx(env));
exports.Set("threadsafe_function_exception",
Expand Down Expand Up @@ -194,6 +196,12 @@ Object Init(Env env, Object exports) {
"isExperimental",
Napi::Boolean::New(env, NAPI_VERSION == NAPI_VERSION_EXPERIMENTAL));

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
exports.Set("hasSharedArrayBuffer", Napi::Boolean::New(env, true));
#else
exports.Set("hasSharedArrayBuffer", Napi::Boolean::New(env, false));
#endif

return exports;
}

Expand Down
1 change: 1 addition & 0 deletions test/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
'object/subscript_operator.cc',
'promise.cc',
'run_script.cc',
'shared_array_buffer.cc',
'symbol.cc',
'threadsafe_function/threadsafe_function_ctx.cc',
'threadsafe_function/threadsafe_function_exception.cc',
Expand Down
104 changes: 104 additions & 0 deletions test/shared_array_buffer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "napi.h"

using namespace Napi;

namespace {

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
Value TestIsSharedArrayBuffer(const CallbackInfo& info) {
if (info.Length() < 1) {
Error::New(info.Env(), "Wrong number of arguments")
.ThrowAsJavaScriptException();
return Value();
}

return Boolean::New(info.Env(), info[0].IsSharedArrayBuffer());
}

Value TestCreateSharedArrayBuffer(const CallbackInfo& info) {
if (info.Length() < 1) {
Error::New(info.Env(), "Wrong number of arguments")
.ThrowAsJavaScriptException();
return Value();
} else if (!info[0].IsNumber()) {
Error::New(info.Env(),
"Wrong type of arguments. Expects a number as first argument.")
.ThrowAsJavaScriptException();
return Value();
}

auto byte_length = info[0].As<Number>().Uint32Value();
if (byte_length == 0) {
Error::New(info.Env(),
"Invalid byte length. Expects a non-negative integer.")
.ThrowAsJavaScriptException();
return Value();
}

return SharedArrayBuffer::New(info.Env(), byte_length);
}

Value TestGetSharedArrayBufferInfo(const CallbackInfo& info) {
if (info.Length() < 1) {
Error::New(info.Env(), "Wrong number of arguments")
.ThrowAsJavaScriptException();
return Value();
} else if (!info[0].IsSharedArrayBuffer()) {
Error::New(info.Env(),
"Wrong type of arguments. Expects a SharedArrayBuffer as first "
"argument.")
.ThrowAsJavaScriptException();
return Value();
}

auto byte_length = info[0].As<SharedArrayBuffer>().ByteLength();

return Number::New(info.Env(), byte_length);
}

Value TestSharedArrayBufferData(const CallbackInfo& info) {
if (info.Length() < 1) {
Error::New(info.Env(), "Wrong number of arguments")
.ThrowAsJavaScriptException();
return Value();
} else if (!info[0].IsSharedArrayBuffer()) {
Error::New(info.Env(),
"Wrong type of arguments. Expects a SharedArrayBuffer as first "
"argument.")
.ThrowAsJavaScriptException();
return Value();
}

auto byte_length = info[0].As<SharedArrayBuffer>().ByteLength();
void* data = info[0].As<SharedArrayBuffer>().Data();

if (byte_length > 0 && data != nullptr) {
uint8_t* bytes = static_cast<uint8_t*>(data);
for (size_t i = 0; i < byte_length; i++) {
bytes[i] = i % 256;
}

return Boolean::New(info.Env(), true);
}

return Boolean::New(info.Env(), false);
}
#endif
} // end anonymous namespace

Object InitSharedArrayBuffer(Env env) {
Object exports = Object::New(env);

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
exports["testIsSharedArrayBuffer"] =
Function::New(env, TestIsSharedArrayBuffer);
exports["testCreateSharedArrayBuffer"] =
Function::New(env, TestCreateSharedArrayBuffer);
exports["testGetSharedArrayBufferInfo"] =
Function::New(env, TestGetSharedArrayBufferInfo);
exports["testSharedArrayBufferData"] =
Function::New(env, TestSharedArrayBufferData);
#endif

return exports;
}
55 changes: 55 additions & 0 deletions test/shared_array_buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';

const assert = require('assert');

module.exports = require('./common').runTest(test);

let skippedMessageShown = false;

function test ({ hasSharedArrayBuffer, sharedarraybuffer }) {
if (!hasSharedArrayBuffer) {
if (!skippedMessageShown) {
console.log(' >Skipped (no SharedArrayBuffer support)');
skippedMessageShown = true;
}
return;
}

{
const sab = new SharedArrayBuffer(16);
const ab = new ArrayBuffer(16);
const obj = {};
const arr = [];

assert.strictEqual(sharedarraybuffer.testIsSharedArrayBuffer(sab), true);
assert.strictEqual(sharedarraybuffer.testIsSharedArrayBuffer(ab), false);
assert.strictEqual(sharedarraybuffer.testIsSharedArrayBuffer(obj), false);
assert.strictEqual(sharedarraybuffer.testIsSharedArrayBuffer(arr), false);
assert.strictEqual(sharedarraybuffer.testIsSharedArrayBuffer(null), false);
assert.strictEqual(sharedarraybuffer.testIsSharedArrayBuffer(undefined), false);
}

{
const sab = sharedarraybuffer.testCreateSharedArrayBuffer(16);
assert(sab instanceof SharedArrayBuffer);
assert.strictEqual(sab.byteLength, 16);
}

{
const sab = new SharedArrayBuffer(32);
const byteLength = sharedarraybuffer.testGetSharedArrayBufferInfo(sab);
assert.strictEqual(byteLength, 32);
}

{
const sab = new SharedArrayBuffer(8);
const result = sharedarraybuffer.testSharedArrayBufferData(sab);
assert.strictEqual(result, true);

// Check if data was written correctly
const view = new Uint8Array(sab);
for (let i = 0; i < 8; i++) {
assert.strictEqual(view[i], i % 256);
}
}
}