Skip to content

Commit d546f85

Browse files
committed
src: handle no support for external buffers
1 parent 35d9d66 commit d546f85

9 files changed

+355
-0
lines changed

doc/array_buffer.md

+10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ Returns a new `Napi::ArrayBuffer` instance.
2323
2424
### New
2525
26+
> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined, this method is not available.
27+
> See [External Buffer][] for more information.
28+
2629
Wraps the provided external data into a new `Napi::ArrayBuffer` instance.
2730
2831
The `Napi::ArrayBuffer` instance does not assume ownership for the data and
@@ -48,6 +51,9 @@ Returns a new `Napi::ArrayBuffer` instance.
4851

4952
### New
5053

54+
> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined, this method is not available.
55+
> See [External Buffer][] for more information.
56+
5157
Wraps the provided external data into a new `Napi::ArrayBuffer` instance.
5258

5359
The `Napi::ArrayBuffer` instance does not assume ownership for the data and
@@ -74,6 +80,9 @@ Returns a new `Napi::ArrayBuffer` instance.
7480
7581
### New
7682
83+
> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined, this method is not available.
84+
> See [External Buffer][] for more information.
85+
7786
Wraps the provided external data into a new `Napi::ArrayBuffer` instance.
7887
7988
The `Napi::ArrayBuffer` instance does not assume ownership for the data and expects it
@@ -153,3 +162,4 @@ bool Napi::ArrayBuffer::IsDetached() const;
153162
Returns `true` if this `ArrayBuffer` has been detached.
154163

155164
[`Napi::Object`]: ./object.md
165+
[External Buffer]: ./external_buffer.md

doc/buffer.md

+97
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Returns a new `Napi::Buffer` object.
2222
2323
### New
2424
25+
> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined, this method is not available.
26+
> See [External Buffer][] for more information.
27+
2528
Wraps the provided external data into a new `Napi::Buffer` object.
2629
2730
The `Napi::Buffer` object does not assume ownership for the data and expects it to be
@@ -47,6 +50,9 @@ Returns a new `Napi::Buffer` object.
4750

4851
### New
4952

53+
> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined, this method is not available.
54+
> See [External Buffer][] for more information.
55+
5056
Wraps the provided external data into a new `Napi::Buffer` object.
5157

5258
The `Napi::Buffer` object does not assume ownership for the data and expects it
@@ -72,6 +78,9 @@ Returns a new `Napi::Buffer` object.
7278
7379
### New
7480
81+
> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined, this method is not available.
82+
> See [External Buffer][] for more information.
83+
7584
Wraps the provided external data into a new `Napi::Buffer` object.
7685
7786
The `Napi::Buffer` object does not assume ownership for the data and expects it to be
@@ -98,6 +107,93 @@ static Napi::Buffer<T> Napi::Buffer::New(napi_env env,
98107

99108
Returns a new `Napi::Buffer` object.
100109

110+
### NewOrCopy
111+
112+
Wraps the provided external data into a new `Napi::Buffer` object. When the
113+
[external buffer][] is not supported, allocates a new `Napi::Buffer` object and
114+
copies the provided external data into it.
115+
116+
The `Napi::Buffer` object does not assume ownership for the data and expects it to be
117+
valid for the lifetime of the object. Since the `Napi::Buffer` is subject to garbage
118+
collection this overload is only suitable for data which is static and never
119+
needs to be freed.
120+
121+
This factory method will not provide the caller with an opportunity to free the
122+
data when the `Napi::Buffer` gets garbage-collected. If you need to free the
123+
data retained by the `Napi::Buffer` object please use other variants of the
124+
`Napi::Buffer::New` factory method that accept `Napi::Finalizer`, which is a
125+
function that will be invoked when the `Napi::Buffer` object has been
126+
destroyed.
127+
128+
```cpp
129+
static Napi::Buffer<T> Napi::Buffer::NewOrCopy(napi_env env, T* data, size_t length);
130+
```
131+
132+
- `[in] env`: The environment in which to create the `Napi::Buffer` object.
133+
- `[in] data`: The pointer to the external data to expose.
134+
- `[in] length`: The number of `T` elements in the external data.
135+
136+
Returns a new `Napi::Buffer` object.
137+
138+
### NewOrCopy
139+
140+
Wraps the provided external data into a new `Napi::Buffer` object. When the
141+
[external buffer][] is not supported, allocates a new `Napi::Buffer` object and
142+
copies the provided external data into it and the `finalizeCallback` is invoked
143+
immediately.
144+
145+
The `Napi::Buffer` object does not assume ownership for the data and expects it
146+
to be valid for the lifetime of the object. The data can only be freed once the
147+
`finalizeCallback` is invoked to indicate that the `Napi::Buffer` has been released.
148+
149+
```cpp
150+
template <typename Finalizer>
151+
static Napi::Buffer<T> Napi::Buffer::NewOrCopy(napi_env env,
152+
T* data,
153+
size_t length,
154+
Finalizer finalizeCallback);
155+
```
156+
157+
- `[in] env`: The environment in which to create the `Napi::Buffer` object.
158+
- `[in] data`: The pointer to the external data to expose.
159+
- `[in] length`: The number of `T` elements in the external data.
160+
- `[in] finalizeCallback`: The function to be called when the `Napi::Buffer` is
161+
destroyed. It must implement `operator()`, accept an Napi::Env, a `T*` (which is the
162+
external data pointer), and return `void`.
163+
164+
Returns a new `Napi::Buffer` object.
165+
166+
### NewOrCopy
167+
168+
Wraps the provided external data into a new `Napi::Buffer` object. When the
169+
[external buffer][] is not supported, allocates a new `Napi::Buffer` object and
170+
copies the provided external data into it and the `finalizeCallback` is invoked
171+
immediately.
172+
173+
The `Napi::Buffer` object does not assume ownership for the data and expects it to be
174+
valid for the lifetime of the object. The data can only be freed once the
175+
`finalizeCallback` is invoked to indicate that the `Napi::Buffer` has been released.
176+
177+
```cpp
178+
template <typename Finalizer, typename Hint>
179+
static Napi::Buffer<T> Napi::Buffer::NewOrCopy(napi_env env,
180+
T* data,
181+
size_t length,
182+
Finalizer finalizeCallback,
183+
Hint* finalizeHint);
184+
```
185+
186+
- `[in] env`: The environment in which to create the `Napi::Buffer` object.
187+
- `[in] data`: The pointer to the external data to expose.
188+
- `[in] length`: The number of `T` elements in the external data.
189+
- `[in] finalizeCallback`: The function to be called when the `Napi::Buffer` is
190+
destroyed. It must implement `operator()`, accept an Napi::Env, a `T*` (which is the
191+
external data pointer) and `Hint*`, and return `void`.
192+
- `[in] finalizeHint`: The hint to be passed as the second parameter of the
193+
finalize callback.
194+
195+
Returns a new `Napi::Buffer` object.
196+
101197
### Copy
102198
103199
Allocates a new `Napi::Buffer` object and copies the provided external data into it.
@@ -148,3 +244,4 @@ size_t Napi::Buffer::Length() const;
148244
Returns the number of `T` elements in the external data.
149245

150246
[`Napi::Uint8Array`]: ./typed_array_of.md
247+
[External Buffer]: ./external_buffer.md

doc/external_buffer.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# External Buffer
2+
3+
**Some runtimes other than Node.js have dropped support for external buffers**.
4+
On runtimes other than Node.js, node-api methods may return
5+
`napi_no_external_buffers_allowed` to indicate that external
6+
buffers are not supported. One such runtime is Electron as
7+
described in this issue
8+
[electron/issues/35801](https://github.com/electron/electron/issues/35801).
9+
10+
In order to maintain broadest compatibility with all runtimes,
11+
you may define `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` in your addon before
12+
includes for the node-api and node-addon-api headers. Doing so will hide the
13+
functions that create external buffers. This will ensure a compilation error
14+
occurs if you accidentally use one of these methods.
15+
16+
In node-addon-api, the `Napi::Buffer::NewOrCopy` provides a convenient way to
17+
create an external buffer, or allocate a new buffer and copy the data when the
18+
external buffer is not supported.

napi-inl.h

+78
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env, size_t byteLength) {
17561756
return ArrayBuffer(env, value);
17571757
}
17581758

1759+
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
17591760
inline ArrayBuffer ArrayBuffer::New(napi_env env,
17601761
void* externalData,
17611762
size_t byteLength) {
@@ -1815,6 +1816,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
18151816

18161817
return ArrayBuffer(env, value);
18171818
}
1819+
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
18181820

18191821
inline ArrayBuffer::ArrayBuffer() : Object() {}
18201822

@@ -2434,6 +2436,7 @@ inline Buffer<T> Buffer<T>::New(napi_env env, size_t length) {
24342436
return Buffer(env, value, length, static_cast<T*>(data));
24352437
}
24362438

2439+
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
24372440
template <typename T>
24382441
inline Buffer<T> Buffer<T>::New(napi_env env, T* data, size_t length) {
24392442
napi_value value;
@@ -2491,6 +2494,81 @@ inline Buffer<T> Buffer<T>::New(napi_env env,
24912494
}
24922495
return Buffer(env, value, length, data);
24932496
}
2497+
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
2498+
2499+
template <typename T>
2500+
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env, T* data, size_t length) {
2501+
napi_value value;
2502+
napi_status status = napi_create_external_buffer(
2503+
env, length * sizeof(T), data, nullptr, nullptr, &value);
2504+
if (status == napi_no_external_buffers_allowed) {
2505+
// If we can't create an external buffer, we'll just copy the data.
2506+
return Buffer<T>::Copy(env, data, length);
2507+
}
2508+
NAPI_THROW_IF_FAILED(env, status, Buffer<T>());
2509+
return Buffer(env, value, length, data);
2510+
}
2511+
2512+
template <typename T>
2513+
template <typename Finalizer>
2514+
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2515+
T* data,
2516+
size_t length,
2517+
Finalizer finalizeCallback) {
2518+
napi_value value;
2519+
details::FinalizeData<T, Finalizer>* finalizeData =
2520+
new details::FinalizeData<T, Finalizer>(
2521+
{std::move(finalizeCallback), nullptr});
2522+
napi_status status =
2523+
napi_create_external_buffer(env,
2524+
length * sizeof(T),
2525+
data,
2526+
details::FinalizeData<T, Finalizer>::Wrapper,
2527+
finalizeData,
2528+
&value);
2529+
if (status == napi_no_external_buffers_allowed) {
2530+
// If we can't create an external buffer, we'll just copy the data.
2531+
Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2532+
delete finalizeData;
2533+
return ret;
2534+
}
2535+
if (status != napi_ok) {
2536+
delete finalizeData;
2537+
NAPI_THROW_IF_FAILED(env, status, Buffer());
2538+
}
2539+
return Buffer(env, value, length, data);
2540+
}
2541+
2542+
template <typename T>
2543+
template <typename Finalizer, typename Hint>
2544+
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
2545+
T* data,
2546+
size_t length,
2547+
Finalizer finalizeCallback,
2548+
Hint* finalizeHint) {
2549+
napi_value value;
2550+
details::FinalizeData<T, Finalizer, Hint>* finalizeData =
2551+
new details::FinalizeData<T, Finalizer, Hint>(
2552+
{std::move(finalizeCallback), finalizeHint});
2553+
napi_status status = napi_create_external_buffer(
2554+
env,
2555+
length * sizeof(T),
2556+
data,
2557+
details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint,
2558+
finalizeData,
2559+
&value);
2560+
if (status == napi_no_external_buffers_allowed) {
2561+
// If we can't create an external buffer, we'll just copy the data.
2562+
Buffer<T> ret = Buffer<T>::Copy(env, data, length);
2563+
delete finalizeData;
2564+
return ret;
2565+
}
2566+
if (status != napi_ok) {
2567+
delete finalizeData;
2568+
NAPI_THROW_IF_FAILED(env, status, Buffer());
2569+
}
2570+
return Buffer(env, value, length, data);
2571+
}
24942572

24952573
template <typename T>
24962574
inline Buffer<T> Buffer<T>::Copy(napi_env env, const T* data, size_t length) {

napi.h

+19
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,7 @@ class ArrayBuffer : public Object {
10771077
size_t byteLength ///< Length of the buffer to be allocated, in bytes
10781078
);
10791079

1080+
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
10801081
/// Creates a new ArrayBuffer instance, using an external buffer with
10811082
/// specified byte length.
10821083
static ArrayBuffer New(
@@ -1120,6 +1121,7 @@ class ArrayBuffer : public Object {
11201121
Hint* finalizeHint ///< Hint (second parameter) to be passed to the
11211122
///< finalize callback
11221123
);
1124+
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
11231125

11241126
ArrayBuffer(); ///< Creates a new _empty_ ArrayBuffer instance.
11251127
ArrayBuffer(napi_env env,
@@ -1432,6 +1434,7 @@ template <typename T>
14321434
class Buffer : public Uint8Array {
14331435
public:
14341436
static Buffer<T> New(napi_env env, size_t length);
1437+
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
14351438
static Buffer<T> New(napi_env env, T* data, size_t length);
14361439

14371440
// Finalizer must implement `void operator()(Env env, T* data)`.
@@ -1447,6 +1450,22 @@ class Buffer : public Uint8Array {
14471450
size_t length,
14481451
Finalizer finalizeCallback,
14491452
Hint* finalizeHint);
1453+
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
1454+
1455+
static Buffer<T> NewOrCopy(napi_env env, T* data, size_t length);
1456+
// Finalizer must implement `void operator()(Env env, T* data)`.
1457+
template <typename Finalizer>
1458+
static Buffer<T> NewOrCopy(napi_env env,
1459+
T* data,
1460+
size_t length,
1461+
Finalizer finalizeCallback);
1462+
// Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`.
1463+
template <typename Finalizer, typename Hint>
1464+
static Buffer<T> NewOrCopy(napi_env env,
1465+
T* data,
1466+
size_t length,
1467+
Finalizer finalizeCallback,
1468+
Hint* finalizeHint);
14501469

14511470
static Buffer<T> Copy(napi_env env, const T* data, size_t length);
14521471

test/binding.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'callbackInfo.cc',
2121
'date.cc',
2222
'binding.cc',
23+
'buffer_no_external.cc',
2324
'buffer.cc',
2425
'callbackscope.cc',
2526
'dataview/dataview.cc',

0 commit comments

Comments
 (0)