Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2278,6 +2278,33 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,

return ArrayBuffer(env, value);
}

#if defined(__cpp_lib_span)
template <typename T, size_t Extent>
inline ArrayBuffer ArrayBuffer::New(napi_env env, std::span<T, Extent> span) {
return ArrayBuffer::New(env, span.data(), span.size_bytes());
}

template <typename T, size_t Extent, typename Finalizer>
inline ArrayBuffer ArrayBuffer::New(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback) {
return ArrayBuffer::New(
env, span.data(), span.size_bytes(), std::move(finalizeCallback));
}

template <typename T, size_t Extent, typename Finalizer, typename Hint>
inline ArrayBuffer ArrayBuffer::New(napi_env env,
std::span<T, Extent> 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) {
Expand Down Expand Up @@ -2311,6 +2338,13 @@ inline size_t ArrayBuffer::ByteLength() {
return length;
}

#if defined(__cpp_lib_span)
template <typename T>
inline std::span<T> ArrayBuffer::Span() {
return std::span<T>(reinterpret_cast<T*>(Data()), ByteLength() / sizeof(T));
}
#endif // defined(__cpp_lib_span)

#if NAPI_VERSION >= 7
inline bool ArrayBuffer::IsDetached() const {
bool detached;
Expand Down Expand Up @@ -2764,6 +2798,18 @@ inline const T* TypedArrayOf<T>::Data() const {
return _data;
}

#if defined(__cpp_lib_span)
template <typename T>
inline std::span<T> TypedArrayOf<T>::Span() {
return std::span<T>(Data(), ElementLength());
}

template <typename T>
inline std::span<const T> TypedArrayOf<T>::Span() const {
return std::span<const T>(Data(), ElementLength());
}
#endif // defined(__cpp_lib_span)

////////////////////////////////////////////////////////////////////////////////
// Function class
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -3176,6 +3222,34 @@ inline Buffer<T> Buffer<T>::New(napi_env env,
}
return Buffer(env, value);
}

#if defined(__cpp_lib_span)
template <typename T>
template <size_t Extent>
inline Buffer<T> Buffer<T>::New(napi_env env, std::span<T, Extent> span) {
return Buffer<T>::New(env, span.data(), span.size());
}

template <typename T>
template <size_t Extent, typename Finalizer>
inline Buffer<T> Buffer<T>::New(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback) {
return Buffer<T>::New(
env, span.data(), span.size(), std::move(finalizeCallback));
}

template <typename T>
template <size_t Extent, typename Finalizer, typename Hint>
inline Buffer<T> Buffer<T>::New(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback,
Hint* finalizeHint) {
return Buffer<T>::New(
env, span.data(), span.size(), std::move(finalizeCallback), finalizeHint);
}
#endif // defined(__cpp_lib_span)

#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED

template <typename T>
Expand Down Expand Up @@ -3274,6 +3348,46 @@ inline Buffer<T> Buffer<T>::Copy(napi_env env, const T* data, size_t length) {
return Buffer<T>(env, value);
}

#if defined(__cpp_lib_span)
template <typename T>
template <size_t Extent>
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env, std::span<T, Extent> span) {
return Buffer<T>::NewOrCopy(env, span.data(), span.size());
}

template <typename T>
template <size_t Extent, typename Finalizer>
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback) {
return Buffer<T>::NewOrCopy(
env, span.data(), span.size(), std::move(finalizeCallback));
}

template <typename T>
template <size_t Extent, typename Finalizer, typename Hint>
inline Buffer<T> Buffer<T>::NewOrCopy(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback,
Hint* finalizeHint) {
return Buffer<T>::NewOrCopy(
env, span.data(), span.size(), std::move(finalizeCallback), finalizeHint);
}

template <typename T>
template <size_t Extent>
inline Buffer<T> Buffer<T>::Copy(napi_env env,
std::span<const T, Extent> span) {
return Buffer<T>::Copy(env, span.data(), span.size());
}

template <typename T>
template <size_t Extent>
inline Buffer<T> Buffer<T>::Copy(napi_env env, std::span<T, Extent> span) {
return Buffer<T>::Copy(env, span.data(), span.size());
}
#endif // defined(__cpp_lib_span)

template <typename T>
inline void Buffer<T>::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Buffer::CheckCast", "empty value");
Expand Down Expand Up @@ -3301,6 +3415,13 @@ inline T* Buffer<T>::Data() const {
return reinterpret_cast<T*>(const_cast<uint8_t*>(Uint8Array::Data()));
}

#if defined(__cpp_lib_span)
template <typename T>
inline std::span<T> Buffer<T>::Span() const {
return std::span<T>(Data(), Length());
}
#endif // defined(__cpp_lib_span)

////////////////////////////////////////////////////////////////////////////////
// Error class
////////////////////////////////////////////////////////////////////////////////
Expand Down
73 changes: 73 additions & 0 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
#include <type_traits>
#include <utility>
#include <vector>
#if defined(__has_include)
#if __has_include(<span>) && (__cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L))
#include <span>
#endif
#endif

// VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known
// good version)
Expand Down Expand Up @@ -1364,6 +1369,22 @@ class ArrayBuffer : public Object {
Hint* finalizeHint ///< Hint (second parameter) to be passed to the
///< finalize callback
);

#if defined(__cpp_lib_span)
template <typename T, size_t Extent = std::dynamic_extent>
static ArrayBuffer New(napi_env env, std::span<T, Extent> span);

template <typename T, size_t Extent, typename Finalizer>
static ArrayBuffer New(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback);

template <typename T, size_t Extent, typename Finalizer, typename Hint>
static ArrayBuffer New(napi_env env,
std::span<T, Extent> 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);
Expand All @@ -1375,6 +1396,11 @@ 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)
template <typename T = uint8_t>
std::span<T> Span();
#endif // defined(__cpp_lib_span)

#if NAPI_VERSION >= 7
bool IsDetached() const;
void Detach();
Expand Down Expand Up @@ -1557,6 +1583,11 @@ class TypedArrayOf : public TypedArray {
/// `ArrayBuffer`.
const T* Data() const;

#if defined(__cpp_lib_span)
std::span<T> Span();
std::span<const T> Span() const;
#endif // defined(__cpp_lib_span)

private:
T* _data;

Expand Down Expand Up @@ -1781,6 +1812,22 @@ class Buffer : public Uint8Array {
size_t length,
Finalizer finalizeCallback,
Hint* finalizeHint);

#if defined(__cpp_lib_span)
template <size_t Extent = std::dynamic_extent>
static Buffer<T> New(napi_env env, std::span<T, Extent> span);

template <size_t Extent, typename Finalizer>
static Buffer<T> New(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback);

template <size_t Extent, typename Finalizer, typename Hint>
static Buffer<T> New(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback,
Hint* finalizeHint);
#endif // defined(__cpp_lib_span)
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED

static Buffer<T> NewOrCopy(napi_env env, T* data, size_t length);
Expand All @@ -1799,6 +1846,28 @@ class Buffer : public Uint8Array {
Finalizer finalizeCallback,
Hint* finalizeHint);

#if defined(__cpp_lib_span)
template <size_t Extent = std::dynamic_extent>
static Buffer<T> NewOrCopy(napi_env env, std::span<T, Extent> span);

template <size_t Extent, typename Finalizer>
static Buffer<T> NewOrCopy(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback);

template <size_t Extent, typename Finalizer, typename Hint>
static Buffer<T> NewOrCopy(napi_env env,
std::span<T, Extent> span,
Finalizer finalizeCallback,
Hint* finalizeHint);

template <size_t Extent = std::dynamic_extent>
static Buffer<T> Copy(napi_env env, std::span<const T, Extent> span);

template <size_t Extent = std::dynamic_extent>
static Buffer<T> Copy(napi_env env, std::span<T, Extent> span);
#endif // defined(__cpp_lib_span)

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

static void CheckCast(napi_env env, napi_value value);
Expand All @@ -1808,6 +1877,10 @@ class Buffer : public Uint8Array {
size_t Length() const;
T* Data() const;

#if defined(__cpp_lib_span)
std::span<T> Span() const;
#endif // defined(__cpp_lib_span)

private:
};

Expand Down
45 changes: 45 additions & 0 deletions test/array_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,42 @@ void CheckDetachUpdatesData(const CallbackInfo& info) {
}
}

#if defined(__cpp_lib_span)
Value CreateBufferFromSpan(const CallbackInfo& info) {
std::span<uint8_t> 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<ArrayBuffer>();
std::span<uint8_t> 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<Uint8Array>();
std::span<uint8_t> 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) {
Expand All @@ -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;
}
13 changes: 13 additions & 0 deletions test/array_buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
]);
}
15 changes: 15 additions & 0 deletions test/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,21 @@
'sources': ['>@(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)'],
'defines': ['NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS'],
'cflags_cc': ['-std=c++20'],
'msvs_settings': {
'VCCLCompilerTool': {
'AdditionalOptions': ['/std:c++20']
}
},
'xcode_settings': {
'CLANG_CXX_LANGUAGE_STANDARD': 'c++20'
}
},
{
'target_name': 'binding_except_all',
'dependencies': ['../node_addon_api.gyp:node_addon_api_except_all'],
Expand Down
Loading