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
15 changes: 15 additions & 0 deletions doc/typed_array.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ Napi::ArrayBuffer Napi::TypedArray::ArrayBuffer() const;

Returns the backing array buffer.

**NOTE**: If the `Napi::TypedArray` is not backed by an `Napi::ArrayBuffer`,
this method will terminate the process with a fatal error when using
`NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` or exhibit undefined behavior
otherwise. Use `Buffer()` instead to get the backing buffer without assuming its
type.

### Buffer

```cpp
Napi::Value Napi::TypedArray::Buffer() const;
```

Returns the backing array buffer as a generic `Napi::Value`, allowing optional
type-checking with `Is*()` and type-casting with `As<>()` methods.

### ElementSize

```cpp
Expand Down
28 changes: 28 additions & 0 deletions doc/typed_array_of.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,34 @@ static Napi::TypedArrayOf Napi::TypedArrayOf::New(napi_env env,

Returns a new `Napi::TypedArrayOf` instance.

### New

Wraps the provided `Napi::SharedArrayBuffer` into a new `Napi::TypedArray` instance.

The array `type` parameter can normally be omitted (because it is inferred from
the template parameter `T`), except when creating a "clamped" array.

```cpp
static Napi::TypedArrayOf Napi::TypedArrayOf::New(napi_env env,
size_t elementLength,
Napi::SharedArrayBuffer arrayBuffer,
size_t bufferOffset,
napi_typedarray_type type);
```

- `[in] env`: The environment in which to create the `Napi::TypedArrayOf` instance.
- `[in] elementLength`: The length to array, in elements.
- `[in] arrayBuffer`: The backing `Napi::SharedArrayBuffer` instance.
- `[in] bufferOffset`: The offset into the `Napi::SharedArrayBuffer` where the array starts,
in bytes.
- `[in] type`: The type of array to allocate (optional).

Returns a new `Napi::TypedArrayOf` instance.

**NOTE**: The support for this overload of `Napi::TypedArrayOf::New()` is only
available when using `NAPI_EXPERIMENTAL` and building against Node.js headers
that supports this feature.

### Constructor

Initializes an empty instance of the `Napi::TypedArrayOf` class.
Expand Down
30 changes: 30 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2594,6 +2594,14 @@ inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const {
return Napi::ArrayBuffer(_env, arrayBuffer);
}

inline Napi::Value TypedArray::Buffer() const {
napi_value arrayBuffer;
napi_status status = napi_get_typedarray_info(
_env, _value, nullptr, nullptr, nullptr, &arrayBuffer, nullptr);
NAPI_THROW_IF_FAILED(_env, status, Napi::Value());
return Napi::Value(_env, arrayBuffer);
}

////////////////////////////////////////////////////////////////////////////////
// TypedArrayOf<T> class
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -2645,6 +2653,28 @@ inline TypedArrayOf<T> TypedArrayOf<T>::New(napi_env env,
bufferOffset));
}

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
template <typename T>
inline TypedArrayOf<T> TypedArrayOf<T>::New(napi_env env,
size_t elementLength,
Napi::SharedArrayBuffer arrayBuffer,
size_t bufferOffset,
napi_typedarray_type type) {
napi_value value;
napi_status status = napi_create_typedarray(
env, type, elementLength, arrayBuffer, bufferOffset, &value);
NAPI_THROW_IF_FAILED(env, status, TypedArrayOf<T>());

return TypedArrayOf<T>(
env,
value,
type,
elementLength,
reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(arrayBuffer.Data()) +
bufferOffset));
}
#endif

template <typename T>
inline TypedArrayOf<T>::TypedArrayOf() : TypedArray(), _data(nullptr) {}

Expand Down
42 changes: 41 additions & 1 deletion napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1339,7 +1339,21 @@ class TypedArray : public Object {

napi_typedarray_type TypedArrayType()
const; ///< Gets the type of this typed-array.
Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer.

// Gets the backing `ArrayBuffer`.
//
// If this `TypedArray` is not backed by an `ArrayBuffer`, this method will
// terminate the process with a fatal error when using
// `NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` or exhibit undefined behavior
// otherwise. Use `Buffer()` instead to get the backing buffer without
// assuming its type.
Napi::ArrayBuffer ArrayBuffer() const;

// Gets the backing buffer (an `ArrayBuffer` or `SharedArrayBuffer`).
//
// Use `IsArrayBuffer()` or `IsSharedArrayBuffer()` to check the type of the
// backing buffer prior to casting with `As<T>()`.
Napi::Value Buffer() const;

uint8_t ElementSize()
const; ///< Gets the size in bytes of one element in the array.
Expand Down Expand Up @@ -1433,6 +1447,32 @@ class TypedArrayOf : public TypedArray {
///< template parameter T.
);

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
/// Creates a new TypedArray instance over a provided SharedArrayBuffer.
///
/// The array type parameter can normally be omitted (because it is inferred
/// from the template parameter T), except when creating a "clamped" array:
///
/// Uint8Array::New(env, length, buffer, 0, napi_uint8_clamped_array)
static TypedArrayOf New(
napi_env env, ///< Node-API environment
size_t elementLength, ///< Length of the created array, as a number of
///< elements
Napi::SharedArrayBuffer
arrayBuffer, ///< Backing shared array buffer instance to use
size_t bufferOffset, ///< Offset into the array buffer where the
///< typed-array starts
#if defined(NAPI_HAS_CONSTEXPR)
napi_typedarray_type type =
TypedArray::TypedArrayTypeForPrimitiveType<T>()
#else
napi_typedarray_type type
#endif
///< Type of array, if different from the default array type for the
///< template parameter T.
);
#endif

static void CheckCast(napi_env env, napi_value value);

TypedArrayOf(); ///< Creates a new _empty_ TypedArrayOf instance.
Expand Down
25 changes: 25 additions & 0 deletions test/typedarray.cc
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ Value GetTypedArrayBuffer(const CallbackInfo& info) {
return array.ArrayBuffer();
}

Value GetTypedArrayBufferValue(const CallbackInfo& info) {
TypedArray array = info[0].As<TypedArray>();
return array.Buffer();
}

Value GetTypedArrayElement(const CallbackInfo& info) {
TypedArray array = info[0].As<TypedArray>();
size_t index = info[1].As<Number>().Uint32Value();
Expand Down Expand Up @@ -389,12 +394,30 @@ void SetTypedArrayElement(const CallbackInfo& info) {
}
}

#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
Value CreateInt8TypedArrayFromSharedArrayBuffer(const CallbackInfo& info) {
auto buffer = info[0].As<SharedArrayBuffer>();
size_t length = buffer.ByteLength();

return NAPI_TYPEDARRAY_NEW_BUFFER(Int8Array,
info.Env(),
length,
buffer.As<SharedArrayBuffer>(),
0,
napi_int8_array);
}
#endif

} // end anonymous namespace

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

exports["createTypedArray"] = Function::New(env, CreateTypedArray);
#ifdef NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
exports["createInt8TypedArrayFromSharedArrayBuffer"] =
Function::New(env, CreateInt8TypedArrayFromSharedArrayBuffer);
#endif
exports["createInvalidTypedArray"] =
Function::New(env, CreateInvalidTypedArray);
exports["getTypedArrayType"] = Function::New(env, GetTypedArrayType);
Expand All @@ -405,6 +428,8 @@ Object InitTypedArray(Env env) {
exports["getTypedArrayByteLength"] =
Function::New(env, GetTypedArrayByteLength);
exports["getTypedArrayBuffer"] = Function::New(env, GetTypedArrayBuffer);
exports["getTypedArrayBufferValue"] =
Function::New(env, GetTypedArrayBufferValue);
exports["getTypedArrayElement"] = Function::New(env, GetTypedArrayElement);
exports["setTypedArrayElement"] = Function::New(env, SetTypedArrayElement);
exports["checkBufferContent"] = Function::New(env, CheckBufferContent);
Expand Down
36 changes: 36 additions & 0 deletions test/typedarray.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const assert = require('assert');

let runSharedArrayBufferTests = true;

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

function test (binding) {
Expand Down Expand Up @@ -61,6 +63,9 @@ function test (binding) {

const b = binding.typedarray.getTypedArrayBuffer(t);
assert.ok(b instanceof ArrayBuffer);
const bAsValue = binding.typedarray.getTypedArrayBufferValue(t);
assert.ok(bAsValue instanceof ArrayBuffer);
assert.strictEqual(b, bAsValue);
} catch (e) {
console.log(data);
throw e;
Expand Down Expand Up @@ -100,4 +105,35 @@ function test (binding) {
assert.throws(() => {
binding.typedarray.createInvalidTypedArray();
}, /Invalid (pointer passed as )?argument/);

if (binding.hasSharedArrayBuffer && runSharedArrayBufferTests) {
const length = 4;
const sab = new SharedArrayBuffer(length);
/** @type {Int8Array<SharedArrayBuffer>} */
let t;

try {
t = binding.typedarray.createInt8TypedArrayFromSharedArrayBuffer(sab);
} catch (ex) {
if (ex.message === 'Invalid argument') {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this error is only expected on binding.typedarray.createInt8TypedArrayFromSharedArrayBuffer. Could this try...catch scope be limited to this single call instead?

console.warn(`The current version of Node.js (${process.version}) does not support creating TypedArrays on SharedArrayBuffers; skipping tests.`);
runSharedArrayBufferTests = false;
return;
}

throw ex;
}

assert.ok(t instanceof Int8Array);
assert.strictEqual(binding.typedarray.getTypedArrayType(t), 'int8');
assert.strictEqual(binding.typedarray.getTypedArrayLength(t), length);
for (let i = 0; i < length; i++) {
const value = 2 ** (i + 1);
t[i] = value;
assert.strictEqual(binding.typedarray.getTypedArrayElement(t, i), value);
}
const bAsValue = binding.typedarray.getTypedArrayBufferValue(t);
assert.ok(bAsValue instanceof SharedArrayBuffer);
assert.strictEqual(bAsValue, sab);
}
}
Loading