Skip to content

Commit

Permalink
add napi_create_buffer_from_arraybuffer (nodejs/node#54505)
Browse files Browse the repository at this point in the history
  • Loading branch information
toyobayashi committed Oct 12, 2024
1 parent 34e2a22 commit bedaacf
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
12 changes: 12 additions & 0 deletions packages/emnapi/include/node/node_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ napi_create_external_buffer(napi_env env,
void* finalize_hint,
napi_value* result);
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED

#ifdef NAPI_EXPERIMENTAL
#define NODE_API_EXPERIMENTAL_HAS_CREATE_BUFFER_FROM_ARRAYBUFFER

NAPI_EXTERN napi_status NAPI_CDECL
node_api_create_buffer_from_arraybuffer(napi_env env,
napi_value arraybuffer,
size_t byte_offset,
size_t byte_length,
napi_value* result);
#endif // NAPI_EXPERIMENTAL

NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,
size_t length,
const void* data,
Expand Down
57 changes: 57 additions & 0 deletions packages/emnapi/src/value/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,63 @@ export function napi_create_external_buffer (
)
}

/**
* @__sig ippppp
*/
export function node_api_create_buffer_from_arraybuffer (
env: napi_env,
arraybuffer: napi_value,
byte_offset: size_t,
byte_length: size_t,
result: Pointer<napi_value>
): napi_status {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let value: number

return $PREAMBLE!(env, (envObject) => {
$CHECK_ARG!(envObject, arraybuffer)
$CHECK_ARG!(envObject, result)
from64('byte_offset')
from64('byte_length')
byte_offset = byte_offset >>> 0
byte_length = byte_length >>> 0
const handle = emnapiCtx.handleStore.get(arraybuffer)!
const buffer = handle.value
if (!(buffer instanceof ArrayBuffer)) {
return envObject.setLastError(napi_status.napi_invalid_arg)
}

if ((byte_length + byte_offset) > buffer.byteLength) {
const err: RangeError & { code?: string } = new RangeError('The byte offset + length is out of range')
err.code = 'ERR_OUT_OF_RANGE'
throw err
}

const Buffer = emnapiCtx.feature.Buffer!
if (!Buffer) {
throw emnapiCtx.createNotSupportBufferError('node_api_create_buffer_from_arraybuffer', '')
}
const out = Buffer.from(buffer, byte_offset, byte_length)
if (buffer === wasmMemory.buffer) {
if (!emnapiExternalMemory.wasmMemoryViewTable.has(out)) {
emnapiExternalMemory.wasmMemoryViewTable.set(out, {
Ctor: Buffer,
address: byte_offset,
length: byte_length,
ownership: ReferenceOwnership.kUserland,
runtimeAllocated: 0
})
}
}
from64('result')

// eslint-disable-next-line @typescript-eslint/no-unused-vars
value = emnapiCtx.addToCurrentScope(out).id
makeSetValue('result', 0, 'value', '*')
return envObject.getReturnStatus()
})
}

/**
* @__sig ippppp
*/
Expand Down
60 changes: 60 additions & 0 deletions packages/test/buffer/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,64 @@ static napi_value getMemoryDataAsArray(napi_env env, napi_callback_info info) {
return ret;
}

NAPI_EXTERN napi_status NAPI_CDECL
node_api_create_buffer_from_arraybuffer(napi_env env,
napi_value arraybuffer,
size_t byte_offset,
size_t byte_length,
napi_value* result);

static napi_value testBufferFromArrayBuffer(napi_env env,
napi_callback_info info) {
napi_status status;
napi_value arraybuffer;
void* data;
napi_value buffer;
size_t byte_length = 1024;
size_t byte_offset = 0;

status = napi_create_arraybuffer(env, byte_length, &data, &arraybuffer);
NODE_API_ASSERT(env, status == napi_ok, "Failed to create arraybuffer");

status = node_api_create_buffer_from_arraybuffer(
env, arraybuffer, byte_offset, byte_length, &buffer);
NODE_API_ASSERT(
env, status == napi_ok, "Failed to create buffer from arraybuffer");

void* buffer_data;
size_t buffer_length;
status = napi_get_buffer_info(env, buffer, &buffer_data, &buffer_length);
NODE_API_ASSERT(env, status == napi_ok, "Failed to get buffer info");
NODE_API_ASSERT(env, buffer_length == byte_length, "Buffer length mismatch");

bool is_buffer;
status = napi_is_buffer(env, buffer, &is_buffer);
NODE_API_ASSERT(env, status == napi_ok, "Failed to check if value is buffer");
NODE_API_ASSERT(env, is_buffer, "Expected a Buffer but did not get one");

status = node_api_create_buffer_from_arraybuffer(
env, arraybuffer, byte_length, byte_length + 1, &buffer);
NODE_API_ASSERT(env,
status == 10,
"Expected range error for invalid byte offset");
napi_value last_error;
status = napi_get_and_clear_last_exception(env, &last_error);
NODE_API_ASSERT(env, status == napi_ok, "Failed to call napi_get_and_clear_last_exception");

napi_value non_arraybuffer;
status = napi_create_uint32(env, 123, &non_arraybuffer);
NODE_API_ASSERT(
env, status == napi_ok, "Failed to create non-arraybuffer value");

status = node_api_create_buffer_from_arraybuffer(
env, non_arraybuffer, byte_offset, byte_length, &buffer);
NODE_API_ASSERT(env,
status == napi_invalid_arg,
"Expected invalid arg error for non-arraybuffer input");

return arraybuffer;
}

static napi_value Init(napi_env env, napi_value exports) {
napi_value theValue;

Expand All @@ -208,6 +266,8 @@ static napi_value Init(napi_env env, napi_value exports) {
DECLARE_NODE_API_PROPERTY("staticBuffer", staticBuffer),
DECLARE_NODE_API_PROPERTY("invalidObjectAsBuffer", invalidObjectAsBuffer),
DECLARE_NODE_API_PROPERTY("getMemoryDataAsArray", getMemoryDataAsArray),
DECLARE_NODE_API_PROPERTY("testBufferFromArrayBuffer",
testBufferFromArrayBuffer),
};

NODE_API_CALL(env, napi_define_properties(
Expand Down
2 changes: 2 additions & 0 deletions packages/test/buffer/buffer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ module.exports = load('buffer').then(async binding => {
assert.deepStrictEqual(binding.getMemoryDataAsArray(buffer), Array(6).fill(99))
assert.deepStrictEqual(binding.getMemoryDataAsArray(typedArray), Array(6).fill(99))
assert.deepStrictEqual(binding.getMemoryDataAsArray(dataView), Array(6).fill(99))

binding.testBufferFromArrayBuffer()
})

0 comments on commit bedaacf

Please sign in to comment.