Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
feat: add SharedArrayBuffer
  • Loading branch information
KevinEady committed Oct 28, 2025
commit 02cc8135f2e0378a8a1794f2dcf45d1eb3aa86e9
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
20 changes: 2 additions & 18 deletions doc/array_buffer.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ArrayBuffer

Class `Napi::ArrayBuffer` inherits from class [`Napi::Object`][].
Class `Napi::ArrayBuffer` inherits from class [`Napi::ArrayBufferLike`][].

The `Napi::ArrayBuffer` class corresponds to the
[JavaScript `ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
Expand Down Expand Up @@ -128,22 +128,6 @@ Napi::ArrayBuffer::ArrayBuffer(napi_env env, napi_value value);
- `[in] env`: The environment in which to create the `Napi::ArrayBuffer` instance.
- `[in] value`: The `Napi::ArrayBuffer` reference to wrap.

### ByteLength

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

Returns the length of the wrapped data, in bytes.

### Data

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

Returns a pointer the wrapped data.

### Detach

```cpp
Expand All @@ -160,6 +144,6 @@ bool Napi::ArrayBuffer::IsDetached() const;

Returns `true` if this `ArrayBuffer` has been detached.

[`Napi::Object`]: ./object.md
[`Napi::ArrayBufferLike`]: ./array_buffer_like.md
[External Buffer]: ./external_buffer.md
[Finalization]: ./finalization.md
27 changes: 27 additions & 0 deletions doc/array_buffer_like.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ArrayBufferLike

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

The `Napi::ArrayBufferLike` class is the base class for [`Napi::ArrayBuffer`][]
and [`Napi::SharedArrayBuffer`][]. It adds common array buffer capabilities to
both. It is an abstract-only base class.

### ByteLength

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

Returns the length of the wrapped data, in bytes.

### Data

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

Returns a pointer the wrapped data.

[`Napi::ArrayBuffer`]: ./array_buffer.md
[`Napi::Object`]: ./object.md
[`Napi::SharedArrayBuffer`]: ./shared_array_buffer.md
49 changes: 49 additions & 0 deletions doc/shared_array_buffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# SharedArrayBuffer

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

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.

[`Napi::ArrayBufferLike`]: ./array_buffer_like.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
86 changes: 69 additions & 17 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,60 @@ inline uint32_t Array::Length() const {
return result;
}

inline ArrayBufferLike::ArrayBufferLike() : Object() {}

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

inline void* ArrayBufferLike::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 ArrayBufferLike::ByteLength() {
size_t length;
napi_status status =
napi_get_arraybuffer_info(_env, _value, nullptr, &length);
NAPI_THROW_IF_FAILED(_env, status, 0);
return length;
}

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

inline SharedArrayBuffer::SharedArrayBuffer() : ArrayBufferLike() {}

inline SharedArrayBuffer::SharedArrayBuffer(napi_env env, napi_value value)
: ArrayBufferLike(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);
}
#endif // NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER

////////////////////////////////////////////////////////////////////////////////
// ArrayBuffer class
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -2154,25 +2221,10 @@ inline void ArrayBuffer::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(result, "ArrayBuffer::CheckCast", "value is not arraybuffer");
}

inline ArrayBuffer::ArrayBuffer() : Object() {}
inline ArrayBuffer::ArrayBuffer() : ArrayBufferLike() {}

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

inline void* ArrayBuffer::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 ArrayBuffer::ByteLength() {
size_t length;
napi_status status =
napi_get_arraybuffer_info(_env, _value, nullptr, &length);
NAPI_THROW_IF_FAILED(_env, status, 0);
return length;
}
: ArrayBufferLike(env, value) {}

#if NAPI_VERSION >= 7
inline bool ArrayBuffer::IsDetached() const {
Expand Down
30 changes: 26 additions & 4 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,8 +1205,30 @@ class Object::iterator {
};
#endif // NODE_ADDON_API_CPP_EXCEPTIONS

class ArrayBufferLike : public Object {
Comment thread
legendecas marked this conversation as resolved.
Outdated
public:
void* Data();
size_t ByteLength();

protected:
ArrayBufferLike();
ArrayBufferLike(napi_env env, napi_value value);
};

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
class SharedArrayBuffer : public ArrayBufferLike {
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);
};
#endif

/// A JavaScript array buffer value.
class ArrayBuffer : public Object {
class ArrayBuffer : public ArrayBufferLike {
public:
/// Creates a new ArrayBuffer instance over a new automatically-allocated
/// buffer.
Expand Down Expand Up @@ -1264,9 +1289,6 @@ class ArrayBuffer : public Object {
ArrayBuffer(napi_env env,
napi_value value); ///< Wraps a Node-API value primitive.

void* Data(); ///< Gets a pointer to the data buffer.
size_t ByteLength(); ///< Gets the length of the array buffer in bytes.

#if NAPI_VERSION >= 7
bool IsDetached() const;
void Detach();
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
Loading