diff --git a/doc/array_buffer.md b/doc/array_buffer.md index de05e55b3..db5e2dc62 100644 --- a/doc/array_buffer.md +++ b/doc/array_buffer.md @@ -109,6 +109,89 @@ static Napi::ArrayBuffer Napi::ArrayBuffer::New(napi_env env, Returns a new `Napi::ArrayBuffer` instance. +### New + +> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined or when C++20 `std::span` is not available, this method is not available. +> See [External Buffer][] for more information. + +Wraps the provided external data span into a new `Napi::ArrayBuffer` instance. + +The `Napi::ArrayBuffer` instance does not assume ownership for the data and +expects it to be valid for the lifetime of the instance. Since the +`Napi::ArrayBuffer` is subject to garbage collection this overload is only +suitable for data which is static and never needs to be freed. This factory +method will not provide the caller with an opportunity to free the data when the +`Napi::ArrayBuffer` gets garbage-collected. If you need to free the data +retained by the `Napi::ArrayBuffer` object please use other variants of the +`Napi::ArrayBuffer::New` factory method that accept `Napi::Finalizer`. +See [Finalization][] for more details. + +```cpp +template +static Napi::ArrayBuffer Napi::ArrayBuffer::New(napi_env env, std::span span); +``` + +- `[in] env`: The environment in which to create the `Napi::ArrayBuffer` instance. +- `[in] span`: The `std::span` referencing the external data to wrap. + +Returns a new `Napi::ArrayBuffer` instance. + +### New + +> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined or when C++20 `std::span` is not available, this method is not available. +> See [External Buffer][] for more information. + +Wraps the provided external data span into a new `Napi::ArrayBuffer` instance. + +The `Napi::ArrayBuffer` instance does not assume ownership for the data and +expects it to be valid for the lifetime of the instance. The data can only be +freed once the `finalizeCallback` is invoked to indicate that the +`Napi::ArrayBuffer` has been released. + +```cpp +template +static Napi::ArrayBuffer Napi::ArrayBuffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback); +``` + +- `[in] env`: The environment in which to create the `Napi::ArrayBuffer` instance. +- `[in] span`: The `std::span` referencing the external data to wrap. +- `[in] finalizeCallback`: A function called when the engine destroys the + `Napi::ArrayBuffer` object, implementing `operator()(Napi::BasicEnv, void*)`. + See [Finalization][] for more details. + +Returns a new `Napi::ArrayBuffer` instance. + +### New + +> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined or when C++20 `std::span` is not available, this method is not available. +> See [External Buffer][] for more information. + +Wraps the provided external data span into a new `Napi::ArrayBuffer` instance. + +The `Napi::ArrayBuffer` instance does not assume ownership for the data and expects it +to be valid for the lifetime of the instance. The data can only be freed once +the `finalizeCallback` is invoked to indicate that the `Napi::ArrayBuffer` has been +released. + +```cpp +template +static Napi::ArrayBuffer Napi::ArrayBuffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint); +``` + +- `[in] env`: The environment in which to create the `Napi::ArrayBuffer` instance. +- `[in] span`: The `std::span` referencing the external data to wrap. +- `[in] finalizeCallback`: A function called when the engine destroys the + `Napi::ArrayBuffer` object, implementing `operator()(Napi::BasicEnv, void*, + Hint*)`. See [Finalization][] for more details. +- `[in] finalizeHint`: The hint value passed to the `finalizeCallback` function. + +Returns a new `Napi::ArrayBuffer` instance. + ### Constructor Initializes an empty instance of the `Napi::ArrayBuffer` class. @@ -144,6 +227,16 @@ void* Napi::ArrayBuffer::Data() const; Returns a pointer the wrapped data. +### Span + +> When C++20 `std::span` is not available, this method is not available. + +```cpp +std::span Napi::ArrayBuffer::Span() const; +``` + +Returns a `std::span` view over the raw byte data. + ### Detach ```cpp diff --git a/doc/buffer.md b/doc/buffer.md index 548400481..28050165b 100644 --- a/doc/buffer.md +++ b/doc/buffer.md @@ -105,6 +105,86 @@ static Napi::Buffer Napi::Buffer::New(napi_env env, Returns a new `Napi::Buffer` object. +### New + +> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined or when C++20 `std::span` is not available, this method is not available. +> See [External Buffer][] for more information. + +Wraps the provided external data span into a new `Napi::Buffer` object. + +The `Napi::Buffer` object does not assume ownership for the data and expects it +to be valid for the lifetime of the object. Since the `Napi::Buffer` is subject +to garbage collection this overload is only suitable for data which is static +and never needs to be freed. This factory method will not provide the caller +with an opportunity to free the data when the `Napi::Buffer` gets +garbage-collected. If you need to free the data retained by the `Napi::Buffer` +object please use other variants of the `Napi::Buffer::New` factory method that +accept `Finalizer`. See [Finalization][] for more details. + +```cpp +template +static Napi::Buffer Napi::Buffer::New(napi_env env, std::span span); +``` + +- `[in] env`: The environment in which to create the `Napi::Buffer` object. +- `[in] span`: The `std::span` referencing the external data to expose. + +Returns a new `Napi::Buffer` object. + +### New + +> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined or when C++20 `std::span` is not available, this method is not available. +> See [External Buffer][] for more information. + +Wraps the provided external data span into a new `Napi::Buffer` object. + +The `Napi::Buffer` object does not assume ownership for the data and expects it +to be valid for the lifetime of the object. The data can only be freed once the +`finalizeCallback` is invoked to indicate that the `Napi::Buffer` has been released. + +```cpp +template +static Napi::Buffer Napi::Buffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback); +``` + +- `[in] env`: The environment in which to create the `Napi::Buffer` object. +- `[in] span`: The `std::span` referencing the external data to expose. +- `[in] finalizeCallback`: The function called when the engine destroys the + `Napi::Buffer` object, implementing `operator()(Napi::BasicEnv, T*)`. See + [Finalization][] for more details. + +Returns a new `Napi::Buffer` object. + +### New + +> When `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` is defined or when C++20 `std::span` is not available, this method is not available. +> See [External Buffer][] for more information. + +Wraps the provided external data span into a new `Napi::Buffer` object. + +The `Napi::Buffer` object does not assume ownership for the data and expects it to be +valid for the lifetime of the object. The data can only be freed once the +`finalizeCallback` is invoked to indicate that the `Napi::Buffer` has been released. + +```cpp +template +static Napi::Buffer Napi::Buffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint); +``` + +- `[in] env`: The environment in which to create the `Napi::Buffer` object. +- `[in] span`: The `std::span` referencing the external data to expose. +- `[in] finalizeCallback`: The function called when the engine destroys the + `Napi::Buffer` object, implementing `operator()(Napi::BasicEnv, T*, Hint*)`. + See [Finalization][] for more details. +- `[in] finalizeHint`: The hint value passed to the `finalizeCallback` function. + +Returns a new `Napi::Buffer` object. + ### NewOrCopy Wraps the provided external data into a new `Napi::Buffer` object. When the @@ -191,6 +271,87 @@ static Napi::Buffer Napi::Buffer::NewOrCopy(napi_env env, Returns a new `Napi::Buffer` object. +### NewOrCopy + +> When C++20 `std::span` is not available, this method is not available. + +Wraps the provided external data span into a new `Napi::Buffer` object. When the +[external buffer][] is not supported, allocates a new `Napi::Buffer` object and +copies the provided external data into it. + +The `Napi::Buffer` object does not assume ownership for the data and expects it to be +valid for the lifetime of the object. Since the `Napi::Buffer` is subject to garbage +collection this overload is only suitable for data which is static and never +needs to be freed. + +```cpp +template +static Napi::Buffer Napi::Buffer::NewOrCopy(napi_env env, std::span span); +``` + +- `[in] env`: The environment in which to create the `Napi::Buffer` object. +- `[in] span`: The `std::span` referencing the external data to expose. + +Returns a new `Napi::Buffer` object. + +### NewOrCopy + +> When C++20 `std::span` is not available, this method is not available. + +Wraps the provided external data span into a new `Napi::Buffer` object. When the +[external buffer][] is not supported, allocates a new `Napi::Buffer` object and +copies the provided external data into it and the `finalizeCallback` is invoked +immediately. + +The `Napi::Buffer` object does not assume ownership for the data and expects it +to be valid for the lifetime of the object. The data can only be freed once the +`finalizeCallback` is invoked to indicate that the `Napi::Buffer` has been released. + +```cpp +template +static Napi::Buffer Napi::Buffer::NewOrCopy(napi_env env, + std::span span, + Finalizer finalizeCallback); +``` + +- `[in] env`: The environment in which to create the `Napi::Buffer` object. +- `[in] span`: The `std::span` referencing the external data to expose. +- `[in] finalizeCallback`: The function called when the engine destroys the + `Napi::Buffer` object, implementing `operator()(Napi::BasicEnv, T*)`. See + [Finalization][] for more details. + +Returns a new `Napi::Buffer` object. + +### NewOrCopy + +> When C++20 `std::span` is not available, this method is not available. + +Wraps the provided external data span into a new `Napi::Buffer` object. When the +[external buffer][] is not supported, allocates a new `Napi::Buffer` object and +copies the provided external data into it and the `finalizeCallback` is invoked +immediately. + +The `Napi::Buffer` object does not assume ownership for the data and expects it to be +valid for the lifetime of the object. The data can only be freed once the +`finalizeCallback` is invoked to indicate that the `Napi::Buffer` has been released. + +```cpp +template +static Napi::Buffer Napi::Buffer::NewOrCopy(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint); +``` + +- `[in] env`: The environment in which to create the `Napi::Buffer` object. +- `[in] span`: The `std::span` referencing the external data to expose. +- `[in] finalizeCallback`: The function called when the engine destroys the + `Napi::Buffer` object, implementing `operator()(Napi::BasicEnv, T*, Hint*)`. + See [Finalization][] for more details. +- `[in] finalizeHint`: The hint value passed to the `finalizeCallback` function. + +Returns a new `Napi::Buffer` object. + ### Copy Allocates a new `Napi::Buffer` object and copies the provided external data into it. @@ -205,6 +366,25 @@ static Napi::Buffer Napi::Buffer::Copy(napi_env env, const T* data, size_t le Returns a new `Napi::Buffer` object containing a copy of the data. +### Copy + +> When C++20 `std::span` is not available, this method is not available. + +Allocates a new `Napi::Buffer` object and copies the provided external data span into it. + +```cpp +template +static Napi::Buffer Napi::Buffer::Copy(napi_env env, std::span span); + +template +static Napi::Buffer Napi::Buffer::Copy(napi_env env, std::span span); +``` + +- `[in] env`: The environment in which to create the `Napi::Buffer` object. +- `[in] span`: The `std::span` referencing the external data to copy. + +Returns a new `Napi::Buffer` object containing a copy of the data. + ### Constructor Initializes an empty instance of the `Napi::Buffer` class. @@ -240,6 +420,16 @@ size_t Napi::Buffer::Length() const; Returns the number of `T` elements in the external data. +### Span + +> When C++20 `std::span` is not available, this method is not available. + +```cpp +std::span Napi::Buffer::Span() const; +``` + +Returns a `std::span` view over the buffer data. + [`Napi::Uint8Array`]: ./typed_array_of.md [External Buffer]: ./external_buffer.md [Finalization]: ./finalization.md diff --git a/doc/shared_array_buffer.md b/doc/shared_array_buffer.md index 872dbb408..ae9b544a6 100644 --- a/doc/shared_array_buffer.md +++ b/doc/shared_array_buffer.md @@ -62,4 +62,14 @@ void* Napi::SharedArrayBuffer::Data() const; Returns a pointer the wrapped data. +### Span + +> When C++20 `std::span` is not available, this method is not available. + +```cpp +std::span Napi::SharedArrayBuffer::Span() const; +``` + +Returns a `std::span` view over the raw byte data. + [`Napi::Object`]: ./object.md diff --git a/doc/typed_array_of.md b/doc/typed_array_of.md index 4ced5841c..d73ccfd9d 100644 --- a/doc/typed_array_of.md +++ b/doc/typed_array_of.md @@ -162,4 +162,15 @@ const T* Napi::TypedArrayOf::Data() const Returns a pointer into the backing `Napi::ArrayBuffer` which is offset to point to the start of the array. +### Span + +> When C++20 `std::span` is not available, this method is not available. + +```cpp +std::span Napi::TypedArrayOf::Span(); +std::span Napi::TypedArrayOf::Span() const; +``` + +Returns a `std::span` view over the typed array data. + [`Napi::TypedArray`]: ./typed_array.md diff --git a/napi-inl.h b/napi-inl.h index 59f1ca14e..0f247099e 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -2203,6 +2203,16 @@ inline size_t SharedArrayBuffer::ByteLength() { NAPI_THROW_IF_FAILED(_env, status, 0); return length; } + +#if defined(__cpp_lib_span) +inline std::span SharedArrayBuffer::Span() const { + void* data; + size_t length; + napi_status status = napi_get_arraybuffer_info(_env, _value, &data, &length); + NAPI_THROW_IF_FAILED(_env, status, std::span()); + return std::span(static_cast(data), length); +} +#endif // defined(__cpp_lib_span) #endif // NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER //////////////////////////////////////////////////////////////////////////////// @@ -2278,6 +2288,33 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env, return ArrayBuffer(env, value); } + +#if defined(__cpp_lib_span) +template +inline ArrayBuffer ArrayBuffer::New(napi_env env, std::span span) { + return ArrayBuffer::New(env, span.data(), span.size_bytes()); +} + +template +inline ArrayBuffer ArrayBuffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback) { + return ArrayBuffer::New( + env, span.data(), span.size_bytes(), std::move(finalizeCallback)); +} + +template +inline ArrayBuffer ArrayBuffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint) { + return ArrayBuffer::New(env, + span.data(), + span.size_bytes(), + std::move(finalizeCallback), + finalizeHint); +} +#endif // defined(__cpp_lib_span) #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED inline void ArrayBuffer::CheckCast(napi_env env, napi_value value) { @@ -2311,6 +2348,16 @@ inline size_t ArrayBuffer::ByteLength() { return length; } +#if defined(__cpp_lib_span) +inline std::span ArrayBuffer::Span() const { + void* data; + size_t length; + napi_status status = napi_get_arraybuffer_info(_env, _value, &data, &length); + NAPI_THROW_IF_FAILED(_env, status, std::span()); + return std::span(static_cast(data), length); +} +#endif // defined(__cpp_lib_span) + #if NAPI_VERSION >= 7 inline bool ArrayBuffer::IsDetached() const { bool detached; @@ -2764,6 +2811,18 @@ inline const T* TypedArrayOf::Data() const { return _data; } +#if defined(__cpp_lib_span) +template +inline std::span TypedArrayOf::Span() { + return std::span(Data(), ElementLength()); +} + +template +inline std::span TypedArrayOf::Span() const { + return std::span(Data(), ElementLength()); +} +#endif // defined(__cpp_lib_span) + //////////////////////////////////////////////////////////////////////////////// // Function class //////////////////////////////////////////////////////////////////////////////// @@ -3176,6 +3235,34 @@ inline Buffer Buffer::New(napi_env env, } return Buffer(env, value); } + +#if defined(__cpp_lib_span) +template +template +inline Buffer Buffer::New(napi_env env, std::span span) { + return Buffer::New(env, span.data(), span.size()); +} + +template +template +inline Buffer Buffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback) { + return Buffer::New( + env, span.data(), span.size(), std::move(finalizeCallback)); +} + +template +template +inline Buffer Buffer::New(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint) { + return Buffer::New( + env, span.data(), span.size(), std::move(finalizeCallback), finalizeHint); +} +#endif // defined(__cpp_lib_span) + #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED template @@ -3274,6 +3361,46 @@ inline Buffer Buffer::Copy(napi_env env, const T* data, size_t length) { return Buffer(env, value); } +#if defined(__cpp_lib_span) +template +template +inline Buffer Buffer::NewOrCopy(napi_env env, std::span span) { + return Buffer::NewOrCopy(env, span.data(), span.size()); +} + +template +template +inline Buffer Buffer::NewOrCopy(napi_env env, + std::span span, + Finalizer finalizeCallback) { + return Buffer::NewOrCopy( + env, span.data(), span.size(), std::move(finalizeCallback)); +} + +template +template +inline Buffer Buffer::NewOrCopy(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint) { + return Buffer::NewOrCopy( + env, span.data(), span.size(), std::move(finalizeCallback), finalizeHint); +} + +template +template +inline Buffer Buffer::Copy(napi_env env, + std::span span) { + return Buffer::Copy(env, span.data(), span.size()); +} + +template +template +inline Buffer Buffer::Copy(napi_env env, std::span span) { + return Buffer::Copy(env, span.data(), span.size()); +} +#endif // defined(__cpp_lib_span) + template inline void Buffer::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Buffer::CheckCast", "empty value"); @@ -3301,6 +3428,13 @@ inline T* Buffer::Data() const { return reinterpret_cast(const_cast(Uint8Array::Data())); } +#if defined(__cpp_lib_span) +template +inline std::span Buffer::Span() const { + return std::span(Data(), Length()); +} +#endif // defined(__cpp_lib_span) + //////////////////////////////////////////////////////////////////////////////// // Error class //////////////////////////////////////////////////////////////////////////////// diff --git a/napi.h b/napi.h index 6b4692c7d..13f2ea55f 100644 --- a/napi.h +++ b/napi.h @@ -23,6 +23,11 @@ #include #include #include +#if defined(__has_include) +#if __has_include() && (__cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)) +#include +#endif +#endif // VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known // good version) @@ -1307,6 +1312,10 @@ class SharedArrayBuffer : public Object { void* Data(); size_t ByteLength(); + +#if defined(__cpp_lib_span) + std::span Span() const; +#endif // defined(__cpp_lib_span) }; #endif @@ -1364,6 +1373,22 @@ class ArrayBuffer : public Object { Hint* finalizeHint ///< Hint (second parameter) to be passed to the ///< finalize callback ); + +#if defined(__cpp_lib_span) + template + static ArrayBuffer New(napi_env env, std::span span); + + template + static ArrayBuffer New(napi_env env, + std::span span, + Finalizer finalizeCallback); + + template + static ArrayBuffer New(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint); +#endif // defined(__cpp_lib_span) #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED static void CheckCast(napi_env env, napi_value value); @@ -1375,6 +1400,10 @@ class ArrayBuffer : public Object { void* Data(); ///< Gets a pointer to the data buffer. size_t ByteLength(); ///< Gets the length of the array buffer in bytes. +#if defined(__cpp_lib_span) + std::span Span() const; +#endif // defined(__cpp_lib_span) + #if NAPI_VERSION >= 7 bool IsDetached() const; void Detach(); @@ -1557,6 +1586,11 @@ class TypedArrayOf : public TypedArray { /// `ArrayBuffer`. const T* Data() const; +#if defined(__cpp_lib_span) + std::span Span(); + std::span Span() const; +#endif // defined(__cpp_lib_span) + private: T* _data; @@ -1781,6 +1815,22 @@ class Buffer : public Uint8Array { size_t length, Finalizer finalizeCallback, Hint* finalizeHint); + +#if defined(__cpp_lib_span) + template + static Buffer New(napi_env env, std::span span); + + template + static Buffer New(napi_env env, + std::span span, + Finalizer finalizeCallback); + + template + static Buffer New(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint); +#endif // defined(__cpp_lib_span) #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED static Buffer NewOrCopy(napi_env env, T* data, size_t length); @@ -1799,6 +1849,28 @@ class Buffer : public Uint8Array { Finalizer finalizeCallback, Hint* finalizeHint); +#if defined(__cpp_lib_span) + template + static Buffer NewOrCopy(napi_env env, std::span span); + + template + static Buffer NewOrCopy(napi_env env, + std::span span, + Finalizer finalizeCallback); + + template + static Buffer NewOrCopy(napi_env env, + std::span span, + Finalizer finalizeCallback, + Hint* finalizeHint); + + template + static Buffer Copy(napi_env env, std::span span); + + template + static Buffer Copy(napi_env env, std::span span); +#endif // defined(__cpp_lib_span) + static Buffer Copy(napi_env env, const T* data, size_t length); static void CheckCast(napi_env env, napi_value value); @@ -1808,6 +1880,10 @@ class Buffer : public Uint8Array { size_t Length() const; T* Data() const; +#if defined(__cpp_lib_span) + std::span Span() const; +#endif // defined(__cpp_lib_span) + private: }; diff --git a/test/array_buffer.cc b/test/array_buffer.cc index 2b07bd250..5e26caef0 100644 --- a/test/array_buffer.cc +++ b/test/array_buffer.cc @@ -220,6 +220,42 @@ void CheckDetachUpdatesData(const CallbackInfo& info) { } } +#if defined(__cpp_lib_span) +Value CreateBufferFromSpan(const CallbackInfo& info) { + std::span span(testData, testLength); + ArrayBuffer buffer = ArrayBuffer::New(info.Env(), span); + if (buffer.ByteLength() != testLength || buffer.Data() != testData) { + Error::New(info.Env(), "Incorrect array buffer created from span.") + .ThrowAsJavaScriptException(); + return Value(); + } + InitData(testData, testLength); + return buffer; +} + +Value CheckArrayBufferSpan(const CallbackInfo& info) { + ArrayBuffer buffer = info[0].As(); + std::span span = buffer.Span(); + if (span.size() != testLength || span.data() != buffer.Data()) { + Error::New(info.Env(), "Incorrect array buffer span.") + .ThrowAsJavaScriptException(); + return Boolean::New(info.Env(), false); + } + return Boolean::New(info.Env(), true); +} + +Value CheckTypedArraySpan(const CallbackInfo& info) { + Uint8Array array = info[0].As(); + std::span span = array.Span(); + if (span.size() != testLength || span.data() != array.Data()) { + Error::New(info.Env(), "Incorrect typed array span.") + .ThrowAsJavaScriptException(); + return Boolean::New(info.Env(), false); + } + return Boolean::New(info.Env(), true); +} +#endif // defined(__cpp_lib_span) + } // end anonymous namespace Object InitArrayBuffer(Env env) { @@ -239,5 +275,14 @@ Object InitArrayBuffer(Env env) { exports["checkDetachUpdatesData"] = Function::New(env, CheckDetachUpdatesData); +#if defined(__cpp_lib_span) + exports["hasSpanSupport"] = Boolean::New(env, true); + exports["createBufferFromSpan"] = Function::New(env, CreateBufferFromSpan); + exports["checkArrayBufferSpan"] = Function::New(env, CheckArrayBufferSpan); + exports["checkTypedArraySpan"] = Function::New(env, CheckTypedArraySpan); +#else + exports["hasSpanSupport"] = Boolean::New(env, false); +#endif + return exports; } diff --git a/test/array_buffer.js b/test/array_buffer.js index 08547ed7e..d44107db5 100644 --- a/test/array_buffer.js +++ b/test/array_buffer.js @@ -65,6 +65,19 @@ function test (binding) { // Let C++ detach the ArrayBuffer. const extBuffer = binding.arraybuffer.createExternalBuffer(); binding.arraybuffer.checkDetachUpdatesData(extBuffer); + }, + + 'std::span ArrayBuffer and TypedArray tests', + () => { + if (!binding.arraybuffer.hasSpanSupport) return; + + const fromSpan = binding.arraybuffer.createBufferFromSpan(); + binding.arraybuffer.checkBuffer(fromSpan); + assert.ok(fromSpan instanceof ArrayBuffer); + assert.ok(binding.arraybuffer.checkArrayBufferSpan(fromSpan)); + + const uint8 = new Uint8Array(fromSpan); + assert.ok(binding.arraybuffer.checkTypedArraySpan(uint8)); } ]); } diff --git a/test/binding.gyp b/test/binding.gyp index 9ff334b64..c892b90bf 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -92,6 +92,10 @@ 'build_sources_type_check': [ 'value_type_cast.cc' ], + 'build_sources_cpp20': [ + 'buffer.cc', + 'buffer_no_external.cc' + ], 'want_coverage': '@(build_sources)'], 'defines': ['NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS'] }, + { + 'target_name': 'binding_cpp20', + 'dependencies': ['../node_addon_api.gyp:node_addon_api_except'], + 'sources': ['>@(build_sources_cpp20)'], + 'defines': ['BUILDING_CPP20_MODULE'], + 'cflags_cc': ['-std=c++20'], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['/std:c++20', '/Zc:__cplusplus'] + } + }, + 'xcode_settings': { + 'CLANG_CXX_LANGUAGE_STANDARD': 'c++20' + } + }, { 'target_name': 'binding_except_all', 'dependencies': ['../node_addon_api.gyp:node_addon_api_except_all'], diff --git a/test/buffer.cc b/test/buffer.cc index c10300dc7..9694ebe1d 100644 --- a/test/buffer.cc +++ b/test/buffer.cc @@ -160,6 +160,55 @@ Value GetFinalizeCount(const CallbackInfo& info) { return Number::New(info.Env(), finalizeCount); } +#if defined(__cpp_lib_span) +Value CreateBufferFromSpan(const CallbackInfo& info) { + std::span span(testData, testLength); + Buffer buffer = Buffer::New(info.Env(), span); + if (buffer.Length() != testLength || buffer.Data() != testData) { + Error::New(info.Env(), "Incorrect buffer created from span.") + .ThrowAsJavaScriptException(); + return Value(); + } + InitData(testData, testLength); + return buffer; +} + +Value CreateBufferCopyFromSpan(const CallbackInfo& info) { + InitData(testData, testLength); + std::span span(testData, testLength); + Buffer buffer = Buffer::Copy(info.Env(), span); + if (buffer.Length() != testLength) { + Error::New(info.Env(), "Incorrect buffer length from span copy.") + .ThrowAsJavaScriptException(); + return Value(); + } + return buffer; +} + +Value CreateBufferNewOrCopyFromSpan(const CallbackInfo& info) { + std::span span(testData, testLength); + Buffer buffer = Buffer::NewOrCopy(info.Env(), span); + if (buffer.Length() != testLength) { + Error::New(info.Env(), "Incorrect buffer length from NewOrCopy span.") + .ThrowAsJavaScriptException(); + return Value(); + } + InitData(testData, testLength); + return buffer; +} + +Value CheckBufferSpan(const CallbackInfo& info) { + Buffer buffer = info[0].As>(); + std::span span = buffer.Span(); + if (span.size() != testLength || span.data() != buffer.Data()) { + Error::New(info.Env(), "Incorrect buffer span.") + .ThrowAsJavaScriptException(); + return Boolean::New(info.Env(), false); + } + return Boolean::New(info.Env(), true); +} +#endif // defined(__cpp_lib_span) + } // end anonymous namespace Object InitBuffer(Env env) { @@ -181,5 +230,29 @@ Object InitBuffer(Env env) { exports["checkBuffer"] = Function::New(env, CheckBuffer); exports["getFinalizeCount"] = Function::New(env, GetFinalizeCount); +#if defined(__cpp_lib_span) + exports["hasSpanSupport"] = Boolean::New(env, true); + exports["createBufferFromSpan"] = Function::New(env, CreateBufferFromSpan); + exports["createBufferCopyFromSpan"] = + Function::New(env, CreateBufferCopyFromSpan); + exports["createBufferNewOrCopyFromSpan"] = + Function::New(env, CreateBufferNewOrCopyFromSpan); + exports["checkBufferSpan"] = Function::New(env, CheckBufferSpan); +#else + exports["hasSpanSupport"] = Boolean::New(env, false); +#endif + + return exports; +} + +#if defined(BUILDING_CPP20_MODULE) +Object InitBufferNoExternal(Env env); + +static Object InitBufferModule(Env env, Object exports) { + exports.Set("buffer", InitBuffer(env)); + exports.Set("bufferNoExternal", InitBufferNoExternal(env)); return exports; } + +NODE_API_MODULE(addon, InitBufferModule) +#endif diff --git a/test/buffer.js b/test/buffer.js index 3f49201a7..b0a808449 100644 --- a/test/buffer.js +++ b/test/buffer.js @@ -1,11 +1,27 @@ 'use strict'; const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const common = require('./common'); const testUtil = require('./testUtil'); -module.exports = require('./common').runTest(test); +module.exports = common.runTestWithBuildType(async (buildType) => { + await common.runTest(test, buildType); -function test (binding) { + const cpp20BindingPath = [ + path.resolve(__dirname, `build/${buildType}/binding_cpp20.node`), + path.resolve(__dirname, `../build/${buildType}/binding_cpp20.node`) + ].find(p => fs.existsSync(p)); + + if (cpp20BindingPath) { + await Promise.resolve( + test(require(cpp20BindingPath), { bindingPath: cpp20BindingPath }) + ).finally(common.mustCall()); + } +}); + +function test (binding, context) { return testUtil.runGCTests([ 'Internal Buffer', () => { @@ -25,6 +41,29 @@ function test (binding) { assert.ok(test instanceof Buffer); }, + 'std::span Buffer tests', + () => { + if (context && context.bindingPath && context.bindingPath.includes('binding_cpp20')) { + assert.strictEqual(binding.buffer.hasSpanSupport, true); + } + if (!binding.buffer.hasSpanSupport) return; + + const fromSpan = binding.buffer.createBufferFromSpan(); + binding.buffer.checkBuffer(fromSpan); + assert.ok(fromSpan instanceof Buffer); + assert.ok(binding.buffer.checkBufferSpan(fromSpan)); + + const copyFromSpan = binding.buffer.createBufferCopyFromSpan(); + binding.buffer.checkBuffer(copyFromSpan); + assert.ok(copyFromSpan instanceof Buffer); + assert.ok(binding.buffer.checkBufferSpan(copyFromSpan)); + + const newOrCopySpan = binding.buffer.createBufferNewOrCopyFromSpan(); + binding.buffer.checkBuffer(newOrCopySpan); + assert.ok(newOrCopySpan instanceof Buffer); + assert.ok(binding.buffer.checkBufferSpan(newOrCopySpan)); + }, + 'External Buffer', () => { const test = binding.buffer.createExternalBuffer();