Skip to content

Commit aee10fa

Browse files
authored
add napi_create_buffer_from_arraybuffer (#126)
1 parent 34e2a22 commit aee10fa

File tree

5 files changed

+128
-16
lines changed

5 files changed

+128
-16
lines changed

packages/emnapi/include/node/node_api.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ napi_create_external_buffer(napi_env env,
145145
void* finalize_hint,
146146
napi_value* result);
147147
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
148+
149+
#ifdef NAPI_EXPERIMENTAL
150+
#define NODE_API_EXPERIMENTAL_HAS_CREATE_BUFFER_FROM_ARRAYBUFFER
151+
152+
NAPI_EXTERN napi_status NAPI_CDECL
153+
node_api_create_buffer_from_arraybuffer(napi_env env,
154+
napi_value arraybuffer,
155+
size_t byte_offset,
156+
size_t byte_length,
157+
napi_value* result);
158+
#endif // NAPI_EXPERIMENTAL
159+
148160
NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,
149161
size_t length,
150162
const void* data,

packages/emnapi/src/value/create.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@ export function napi_create_typedarray (
229229
$CHECK_ARG!(envObject, result)
230230

231231
const handle = emnapiCtx.handleStore.get(arraybuffer)!
232-
const buffer = handle.value
233-
if (!(buffer instanceof ArrayBuffer)) {
232+
if (!handle.isArrayBuffer()) {
234233
return envObject.setLastError(napi_status.napi_invalid_arg)
235234
}
235+
const buffer = handle.value
236236

237237
from64('byte_offset')
238238
from64('length')
@@ -412,6 +412,63 @@ export function napi_create_external_buffer (
412412
)
413413
}
414414

415+
/**
416+
* @__sig ippppp
417+
*/
418+
export function node_api_create_buffer_from_arraybuffer (
419+
env: napi_env,
420+
arraybuffer: napi_value,
421+
byte_offset: size_t,
422+
byte_length: size_t,
423+
result: Pointer<napi_value>
424+
): napi_status {
425+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
426+
let value: number
427+
428+
return $PREAMBLE!(env, (envObject) => {
429+
$CHECK_ARG!(envObject, arraybuffer)
430+
$CHECK_ARG!(envObject, result)
431+
from64('byte_offset')
432+
from64('byte_length')
433+
byte_offset = byte_offset >>> 0
434+
byte_length = byte_length >>> 0
435+
const handle = emnapiCtx.handleStore.get(arraybuffer)!
436+
if (!handle.isArrayBuffer()) {
437+
return envObject.setLastError(napi_status.napi_invalid_arg)
438+
}
439+
const buffer = handle.value
440+
441+
if ((byte_length + byte_offset) > buffer.byteLength) {
442+
const err: RangeError & { code?: string } = new RangeError('The byte offset + length is out of range')
443+
err.code = 'ERR_OUT_OF_RANGE'
444+
throw err
445+
}
446+
447+
const Buffer = emnapiCtx.feature.Buffer!
448+
if (!Buffer) {
449+
throw emnapiCtx.createNotSupportBufferError('node_api_create_buffer_from_arraybuffer', '')
450+
}
451+
const out = Buffer.from(buffer, byte_offset, byte_length)
452+
if (buffer === wasmMemory.buffer) {
453+
if (!emnapiExternalMemory.wasmMemoryViewTable.has(out)) {
454+
emnapiExternalMemory.wasmMemoryViewTable.set(out, {
455+
Ctor: Buffer,
456+
address: byte_offset,
457+
length: byte_length,
458+
ownership: ReferenceOwnership.kUserland,
459+
runtimeAllocated: 0
460+
})
461+
}
462+
}
463+
from64('result')
464+
465+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
466+
value = emnapiCtx.addToCurrentScope(out).id
467+
makeSetValue('result', 0, 'value', '*')
468+
return envObject.getReturnStatus()
469+
})
470+
}
471+
415472
/**
416473
* @__sig ippppp
417474
*/
@@ -433,10 +490,10 @@ export function napi_create_dataview (
433490
byte_length = byte_length >>> 0
434491
byte_offset = byte_offset >>> 0
435492
const handle = emnapiCtx.handleStore.get(arraybuffer)!
436-
const buffer = handle.value
437-
if (!(buffer instanceof ArrayBuffer)) {
493+
if (!handle.isArrayBuffer()) {
438494
return envObject.setLastError(napi_status.napi_invalid_arg)
439495
}
496+
const buffer = handle.value
440497

441498
if ((byte_length + byte_offset) > buffer.byteLength) {
442499
const err: RangeError & { code?: string } = new RangeError('byte_offset + byte_length should be less than or equal to the size in bytes of the array passed in')

packages/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ add_test("number" "./number/binding.c;./number/test_null.c" OFF)
294294
add_test("symbol" "./symbol/binding.c" OFF)
295295
add_test("typedarray" "./typedarray/binding.c" OFF)
296296
add_test("buffer" "./buffer/binding.c" OFF)
297+
target_compile_definitions("buffer" PRIVATE "NAPI_EXPERIMENTAL")
297298
add_test("buffer_finalizer" "./buffer_finalizer/binding.c" OFF)
298299
add_test("fatal_exception" "./fatal_exception/binding.c" OFF)
299300
add_test("cleanup_hook" "./cleanup_hook/binding.c" OFF)

packages/test/buffer/binding.c

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,23 @@ static const char theText[] =
3838
const unsigned int theTextSize = sizeof(theText);
3939

4040
static int deleterCallCount = 0;
41-
static void deleteTheText(napi_env env, void* data, void* finalize_hint) {
42-
NODE_API_ASSERT_RETURN_VOID(
43-
env, data != NULL && strcmp(data, theText) == 0, "invalid data");
41+
42+
static void deleteTheText(node_api_basic_env env,
43+
void* data,
44+
void* finalize_hint) {
45+
NODE_API_BASIC_ASSERT_RETURN_VOID(data != NULL && strcmp(data, theText) == 0,
46+
"invalid data");
47+
4448
(void)finalize_hint;
4549
free(data);
4650
deleterCallCount++;
4751
}
4852

49-
static void noopDeleter(napi_env env, void* data, void* finalize_hint) {
50-
NODE_API_ASSERT_RETURN_VOID(
51-
env, data != NULL && strcmp(data, theText) == 0, "invalid data");
53+
static void noopDeleter(node_api_basic_env env,
54+
void* data,
55+
void* finalize_hint) {
56+
NODE_API_BASIC_ASSERT_RETURN_VOID(data != NULL && strcmp(data, theText) == 0,
57+
"invalid data");
5258
(void)finalize_hint;
5359
deleterCallCount++;
5460
}
@@ -75,9 +81,12 @@ static napi_value newExternalBuffer(napi_env env, napi_callback_info info) {
7581
NODE_API_ASSERT(
7682
env, theCopy, "Failed to copy static text for newExternalBuffer");
7783
NODE_API_CALL(env,
78-
napi_create_external_buffer(
79-
env, theTextSize, theCopy, deleteTheText,
80-
NULL /* finalize_hint */, &theBuffer));
84+
napi_create_external_buffer(env,
85+
sizeof(theText),
86+
theCopy,
87+
deleteTheText,
88+
NULL /* finalize_hint */,
89+
&theBuffer));
8190

8291
return theBuffer;
8392
}
@@ -134,9 +143,12 @@ static napi_value bufferInfo(napi_env env, napi_callback_info info) {
134143
static napi_value staticBuffer(napi_env env, napi_callback_info info) {
135144
napi_value theBuffer;
136145
NODE_API_CALL(env,
137-
napi_create_external_buffer(
138-
env, sizeof(theText), (void*)theText, noopDeleter,
139-
NULL /* finalize_hint */, &theBuffer));
146+
napi_create_external_buffer(env,
147+
sizeof(theText),
148+
(void*)theText,
149+
noopDeleter,
150+
NULL /* finalize_hint */,
151+
&theBuffer));
140152
return theBuffer;
141153
}
142154

@@ -190,6 +202,32 @@ static napi_value getMemoryDataAsArray(napi_env env, napi_callback_info info) {
190202
return ret;
191203
}
192204

205+
static napi_value bufferFromArrayBuffer(napi_env env,
206+
napi_callback_info info) {
207+
napi_status status;
208+
napi_value arraybuffer;
209+
napi_value buffer;
210+
size_t byte_length = 1024;
211+
void* data = NULL;
212+
size_t buffer_length = 0;
213+
void* buffer_data = NULL;
214+
215+
status = napi_create_arraybuffer(env, byte_length, &data, &arraybuffer);
216+
NODE_API_ASSERT(env, status == napi_ok, "Failed to create arraybuffer");
217+
218+
status = node_api_create_buffer_from_arraybuffer(
219+
env, arraybuffer, 0, byte_length, &buffer);
220+
NODE_API_ASSERT(
221+
env, status == napi_ok, "Failed to create buffer from arraybuffer");
222+
223+
status = napi_get_buffer_info(env, buffer, &buffer_data, &buffer_length);
224+
NODE_API_ASSERT(env, status == napi_ok, "Failed to get buffer info");
225+
226+
NODE_API_ASSERT(env, buffer_length == byte_length, "Buffer length mismatch");
227+
228+
return buffer;
229+
}
230+
193231
static napi_value Init(napi_env env, napi_value exports) {
194232
napi_value theValue;
195233

@@ -208,6 +246,7 @@ static napi_value Init(napi_env env, napi_value exports) {
208246
DECLARE_NODE_API_PROPERTY("staticBuffer", staticBuffer),
209247
DECLARE_NODE_API_PROPERTY("invalidObjectAsBuffer", invalidObjectAsBuffer),
210248
DECLARE_NODE_API_PROPERTY("getMemoryDataAsArray", getMemoryDataAsArray),
249+
DECLARE_NODE_API_PROPERTY("bufferFromArrayBuffer", bufferFromArrayBuffer),
211250
};
212251

213252
NODE_API_CALL(env, napi_define_properties(

packages/test/buffer/buffer.test.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ module.exports = load('buffer').then(async binding => {
2121

2222
// To test this doesn't crash
2323
binding.invalidObjectAsBuffer({})
24+
25+
const testBuffer = binding.bufferFromArrayBuffer()
26+
assert(testBuffer instanceof Buffer, 'Expected a Buffer')
2427
})().then(common.mustCall())
2528

2629
process.externalBuffer = binding.newExternalBuffer()

0 commit comments

Comments
 (0)