-
Notifications
You must be signed in to change notification settings - Fork 470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
src: handle no support for external buffers #1273
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# External Buffer | ||
|
||
**Some runtimes other than Node.js have dropped support for external buffers**. | ||
On runtimes other than Node.js, node-api methods may return | ||
`napi_no_external_buffers_allowed` to indicate that external | ||
buffers are not supported. One such runtime is Electron as | ||
described in this issue | ||
[electron/issues/35801](https://github.com/electron/electron/issues/35801). | ||
|
||
In order to maintain broadest compatibility with all runtimes, | ||
you may define `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` in your addon before | ||
includes for the node-api and node-addon-api headers. Doing so will hide the | ||
functions that create external buffers. This will ensure a compilation error | ||
occurs if you accidentally use one of these methods. | ||
|
||
In node-addon-api, the `Napi::Buffer::NewOrCopy` provides a convenient way to | ||
create an external buffer, or allocate a new buffer and copy the data when the | ||
external buffer is not supported. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,9 +22,13 @@ namespace Napi { | |
namespace NAPI_CPP_CUSTOM_NAMESPACE { | ||
#endif | ||
|
||
// Helpers to handle functions exposed from C++. | ||
// Helpers to handle functions exposed from C++ and internal constants. | ||
namespace details { | ||
|
||
// New napi_status constants not yet available in all supported versions of | ||
// Node.js releases. Only necessary when they are used in napi.h and napi-inl.h. | ||
constexpr int napi_no_external_buffers_allowed = 22; | ||
|
||
// Attach a data item to an object and delete it when the object gets | ||
// garbage-collected. | ||
// TODO: Replace this code with `napi_add_finalizer()` whenever it becomes | ||
|
@@ -1756,6 +1760,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env, size_t byteLength) { | |
return ArrayBuffer(env, value); | ||
} | ||
|
||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
inline ArrayBuffer ArrayBuffer::New(napi_env env, | ||
void* externalData, | ||
size_t byteLength) { | ||
|
@@ -1815,6 +1820,7 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env, | |
|
||
return ArrayBuffer(env, value); | ||
} | ||
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
|
||
inline ArrayBuffer::ArrayBuffer() : Object() {} | ||
|
||
|
@@ -2434,6 +2440,7 @@ inline Buffer<T> Buffer<T>::New(napi_env env, size_t length) { | |
return Buffer(env, value, length, static_cast<T*>(data)); | ||
} | ||
|
||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
template <typename T> | ||
inline Buffer<T> Buffer<T>::New(napi_env env, T* data, size_t length) { | ||
napi_value value; | ||
|
@@ -2491,6 +2498,94 @@ inline Buffer<T> Buffer<T>::New(napi_env env, | |
} | ||
return Buffer(env, value, length, data); | ||
} | ||
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
|
||
template <typename T> | ||
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env, T* data, size_t length) { | ||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
napi_value value; | ||
napi_status status = napi_create_external_buffer( | ||
env, length * sizeof(T), data, nullptr, nullptr, &value); | ||
if (status == details::napi_no_external_buffers_allowed) { | ||
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
// If we can't create an external buffer, we'll just copy the data. | ||
return Buffer<T>::Copy(env, data, length); | ||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
} | ||
NAPI_THROW_IF_FAILED(env, status, Buffer<T>()); | ||
return Buffer(env, value, length, data); | ||
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
} | ||
|
||
template <typename T> | ||
template <typename Finalizer> | ||
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env, | ||
T* data, | ||
size_t length, | ||
Finalizer finalizeCallback) { | ||
details::FinalizeData<T, Finalizer>* finalizeData = | ||
new details::FinalizeData<T, Finalizer>( | ||
{std::move(finalizeCallback), nullptr}); | ||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
napi_value value; | ||
napi_status status = | ||
napi_create_external_buffer(env, | ||
length * sizeof(T), | ||
data, | ||
details::FinalizeData<T, Finalizer>::Wrapper, | ||
finalizeData, | ||
&value); | ||
if (status == details::napi_no_external_buffers_allowed) { | ||
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
// If we can't create an external buffer, we'll just copy the data. | ||
Buffer<T> ret = Buffer<T>::Copy(env, data, length); | ||
details::FinalizeData<T, Finalizer>::Wrapper(env, data, finalizeData); | ||
return ret; | ||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
} | ||
if (status != napi_ok) { | ||
delete finalizeData; | ||
NAPI_THROW_IF_FAILED(env, status, Buffer()); | ||
} | ||
return Buffer(env, value, length, data); | ||
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
} | ||
|
||
template <typename T> | ||
template <typename Finalizer, typename Hint> | ||
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume its the templates that prevent us from having one function with all the parameters and the others calling that one with dummy/null parameters, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really. When the template parameter |
||
T* data, | ||
size_t length, | ||
Finalizer finalizeCallback, | ||
Hint* finalizeHint) { | ||
details::FinalizeData<T, Finalizer, Hint>* finalizeData = | ||
new details::FinalizeData<T, Finalizer, Hint>( | ||
{std::move(finalizeCallback), finalizeHint}); | ||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
napi_value value; | ||
napi_status status = napi_create_external_buffer( | ||
env, | ||
length * sizeof(T), | ||
data, | ||
details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint, | ||
finalizeData, | ||
&value); | ||
if (status == details::napi_no_external_buffers_allowed) { | ||
#endif | ||
// If we can't create an external buffer, we'll just copy the data. | ||
Buffer<T> ret = Buffer<T>::Copy(env, data, length); | ||
details::FinalizeData<T, Finalizer, Hint>::WrapperWithHint( | ||
env, data, finalizeData); | ||
return ret; | ||
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | ||
} | ||
if (status != napi_ok) { | ||
delete finalizeData; | ||
NAPI_THROW_IF_FAILED(env, status, Buffer()); | ||
} | ||
return Buffer(env, value, length, data); | ||
#endif | ||
} | ||
|
||
template <typename T> | ||
inline Buffer<T> Buffer<T>::Copy(napi_env env, const T* data, size_t length) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.