Skip to content

Commit

Permalink
node-api: add napi_create_buffer_from_arraybuffer method
Browse files Browse the repository at this point in the history
PR-URL: #54505
Fixes: #54440
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Gabriel Schulhof <[email protected]>
Reviewed-By: Chengzhong Wu <[email protected]>
Reviewed-By: Vladimir Morozov <[email protected]>
  • Loading branch information
mertcanaltin authored and ruyadorno committed Nov 27, 2024
1 parent d8c552a commit a559008
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 12 deletions.
31 changes: 31 additions & 0 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2696,6 +2696,37 @@ is raised.
JavaScript `TypedArray` objects are described in
[Section 22.2][] of the ECMAScript Language Specification.

#### `node_api_create_buffer_from_arraybuffer`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
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)
```

* **`[in] env`**: The environment that the API is invoked under.
* **`[in] arraybuffer`**: The `ArrayBuffer` from which the buffer will be created.
* **`[in] byte_offset`**: The byte offset within the `ArrayBuffer` from which to start creating the buffer.
* **`[in] byte_length`**: The length in bytes of the buffer to be created from the `ArrayBuffer`.
* **`[out] result`**: A `napi_value` representing the created JavaScript `Buffer` object.

Returns `napi_ok` if the API succeeded.

This API creates a JavaScript `Buffer` object from an existing `ArrayBuffer`.
The `Buffer` object is a Node.js-specific class that provides a way to work with binary data directly in JavaScript.

The byte range `[byte_offset, byte_offset + byte_length)`
must be within the bounds of the `ArrayBuffer`. If `byte_offset + byte_length`
exceeds the size of the `ArrayBuffer`, a `RangeError` exception is raised.

#### `napi_create_dataview`

<!-- YAML
Expand Down
35 changes: 35 additions & 0 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1427,3 +1427,38 @@ napi_status NAPI_CDECL node_api_get_module_file_name(
*result = static_cast<node_napi_env>(env)->GetFilename();
return napi_clear_last_error(env);
}

#ifdef NAPI_EXPERIMENTAL

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) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, arraybuffer);
CHECK_ARG(env, result);

v8::Local<v8::Value> arraybuffer_value =
v8impl::V8LocalValueFromJsValue(arraybuffer);
if (!arraybuffer_value->IsArrayBuffer()) {
return napi_invalid_arg;
}

v8::Local<v8::ArrayBuffer> arraybuffer_obj =
arraybuffer_value.As<v8::ArrayBuffer>();
if (byte_offset + byte_length > arraybuffer_obj->ByteLength()) {
return napi_throw_range_error(
env, "ERR_OUT_OF_RANGE", "The byte offset + length is out of range");
}

v8::Local<v8::Object> buffer =
node::Buffer::New(env->isolate, arraybuffer_obj, byte_offset, byte_length)
.ToLocalChecked();

*result = v8impl::JsValueFromV8LocalValue(buffer);
return napi_ok;
}

#endif
12 changes: 12 additions & 0 deletions src/node_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,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
3 changes: 3 additions & 0 deletions test/node-api/test_buffer/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"targets": [
{
"target_name": "test_buffer",
"defines": [
'NAPI_EXPERIMENTAL'
],
"sources": [ "test_buffer.c" ]
},
{
Expand Down
3 changes: 3 additions & 0 deletions test/node-api/test_buffer/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ const tick = require('util').promisify(require('../../common/tick'));

// To test this doesn't crash
binding.invalidObjectAsBuffer({});

const testBuffer = binding.bufferFromArrayBuffer();
assert(testBuffer instanceof Buffer, 'Expected a Buffer');
})().then(common.mustCall());
62 changes: 50 additions & 12 deletions test/node-api/test_buffer/test_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ static const char theText[] =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";

static int deleterCallCount = 0;
static void deleteTheText(napi_env env, void* data, void* finalize_hint) {
NODE_API_ASSERT_RETURN_VOID(
env, data != NULL && strcmp(data, theText) == 0, "invalid data");

static void deleteTheText(node_api_basic_env env,
void* data,
void* finalize_hint) {
NODE_API_BASIC_ASSERT_RETURN_VOID(data != NULL && strcmp(data, theText) == 0,
"invalid data");

(void)finalize_hint;
free(data);
deleterCallCount++;
}

static void noopDeleter(napi_env env, void* data, void* finalize_hint) {
NODE_API_ASSERT_RETURN_VOID(
env, data != NULL && strcmp(data, theText) == 0, "invalid data");
static void noopDeleter(node_api_basic_env env,
void* data,
void* finalize_hint) {
NODE_API_BASIC_ASSERT_RETURN_VOID(data != NULL && strcmp(data, theText) == 0,
"invalid data");
(void)finalize_hint;
deleterCallCount++;
}
Expand All @@ -42,9 +48,12 @@ static napi_value newExternalBuffer(napi_env env, napi_callback_info info) {
NODE_API_ASSERT(
env, theCopy, "Failed to copy static text for newExternalBuffer");
NODE_API_CALL(env,
napi_create_external_buffer(
env, sizeof(theText), theCopy, deleteTheText,
NULL /* finalize_hint */, &theBuffer));
napi_create_external_buffer(env,
sizeof(theText),
theCopy,
deleteTheText,
NULL /* finalize_hint */,
&theBuffer));

return theBuffer;
}
Expand Down Expand Up @@ -101,9 +110,12 @@ static napi_value bufferInfo(napi_env env, napi_callback_info info) {
static napi_value staticBuffer(napi_env env, napi_callback_info info) {
napi_value theBuffer;
NODE_API_CALL(env,
napi_create_external_buffer(
env, sizeof(theText), (void*)theText, noopDeleter,
NULL /* finalize_hint */, &theBuffer));
napi_create_external_buffer(env,
sizeof(theText),
(void*)theText,
noopDeleter,
NULL /* finalize_hint */,
&theBuffer));
return theBuffer;
}

Expand All @@ -123,6 +135,31 @@ static napi_value invalidObjectAsBuffer(napi_env env, napi_callback_info info) {
return notTheBuffer;
}

static napi_value bufferFromArrayBuffer(napi_env env, napi_callback_info info) {
napi_status status;
napi_value arraybuffer;
napi_value buffer;
size_t byte_length = 1024;
void* data = NULL;
size_t buffer_length = 0;
void* buffer_data = NULL;

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, 0, byte_length, &buffer);
NODE_API_ASSERT(
env, status == napi_ok, "Failed to create buffer from arraybuffer");

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");

return buffer;
}

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

Expand All @@ -140,6 +177,7 @@ static napi_value Init(napi_env env, napi_value exports) {
DECLARE_NODE_API_PROPERTY("bufferInfo", bufferInfo),
DECLARE_NODE_API_PROPERTY("staticBuffer", staticBuffer),
DECLARE_NODE_API_PROPERTY("invalidObjectAsBuffer", invalidObjectAsBuffer),
DECLARE_NODE_API_PROPERTY("bufferFromArrayBuffer", bufferFromArrayBuffer),
};

NODE_API_CALL(env, napi_define_properties(
Expand Down

0 comments on commit a559008

Please sign in to comment.