Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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);
}
}
}