Skip to content
Closed
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
7 changes: 4 additions & 3 deletions benchmark/buffers/buffer-copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ const common = require('../common.js');
const bench = common.createBenchmark(main, {
bytes: [8, 128, 1024],
partial: ['true', 'false'],
shared: ['true', 'false'],
n: [6e6],
});

function main({ n, bytes, partial }) {
const source = Buffer.allocUnsafe(bytes);
const target = Buffer.allocUnsafe(bytes);
function main({ n, bytes, partial, shared }) {
const source = shared === 'true' ? Buffer.from(new SharedArrayBuffer(bytes)) : Buffer.allocUnsafe(bytes);
const target = shared === 'true' ? Buffer.from(new SharedArrayBuffer(bytes)) : Buffer.allocUnsafe(bytes);
const sourceStart = (partial === 'true' ? Math.floor(bytes / 2) : 0);
bench.start();
for (let i = 0; i < n; i++) {
Expand Down
10 changes: 10 additions & 0 deletions deps/v8/include/v8-array-buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,16 @@ class V8_EXPORT ArrayBufferView : public Object {
*/
bool HasBuffer() const;

/**
* Copies |count| bytes from |source| starting at |source_start| into
* |target| starting at |target_start| using memmove semantics. Unlike
* CopyContents, this method handles both source and destination, supports
* byte offsets, and requires no HandleScope.
*/
static void FastCopy(Local<ArrayBufferView> source, size_t source_start,
Local<ArrayBufferView> target, size_t target_start,
size_t count);

V8_INLINE static ArrayBufferView* Cast(Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
Expand Down
33 changes: 33 additions & 0 deletions deps/v8/src/api/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9044,6 +9044,39 @@ size_t v8::ArrayBufferView::CopyContents(void* dest, size_t byte_length) {
return bytes_to_copy;
}

// static
void v8::ArrayBufferView::FastCopy(Local<ArrayBufferView> source,
size_t source_start,
Local<ArrayBufferView> target,
size_t target_start, size_t count) {
i::DisallowGarbageCollection no_gc;
auto src = Utils::OpenDirectHandle(*source);
auto dst = Utils::OpenDirectHandle(*target);

if (V8_UNLIKELY(src->IsDetachedOrOutOfBounds() ||
dst->IsDetachedOrOutOfBounds())) {
return;
}

char* src_data;
if (V8_LIKELY(i::IsJSTypedArray(*src))) {
src_data = reinterpret_cast<char*>(i::Cast<i::JSTypedArray>(*src)->DataPtr());
} else {
src_data = reinterpret_cast<char*>(
i::Cast<i::JSDataViewOrRabGsabDataView>(*src)->data_pointer());
}

char* dst_data;
if (V8_LIKELY(i::IsJSTypedArray(*dst))) {
dst_data = reinterpret_cast<char*>(i::Cast<i::JSTypedArray>(*dst)->DataPtr());
} else {
dst_data = reinterpret_cast<char*>(
i::Cast<i::JSDataViewOrRabGsabDataView>(*dst)->data_pointer());
}

memmove(dst_data + target_start, src_data + source_start, count);
}

v8::MemorySpan<uint8_t> v8::ArrayBufferView::GetContents(
v8::MemorySpan<uint8_t> storage) {
internal::DisallowGarbageCollection no_gc;
Expand Down
12 changes: 9 additions & 3 deletions doc/api/buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -1767,9 +1767,14 @@ added: v0.1.90
Copies data from a region of `buf` to a region in `target`, even if the `target`
memory region overlaps with `buf`.

[`TypedArray.prototype.set()`][] performs the same operation, and is available
for all TypedArrays, including Node.js `Buffer`s, although it takes
different function arguments.
[`TypedArray.prototype.set()`][] performs a similar operation and is available
for all TypedArrays, including Node.js `Buffer`s, although it takes different
function arguments. It differs from `buf.copy()` in two ways:

1. If either buffer is detached, it throws a `TypeError` instead of performing
a no-op.
2. If either buffer is backed by a [`SharedArrayBuffer`][], it performs a
slower copy based on a stricter memory model.

```mjs
import { Buffer } from 'node:buffer';
Expand Down Expand Up @@ -5588,6 +5593,7 @@ introducing security vulnerabilities into an application.
[`ERR_OUT_OF_RANGE`]: errors.md#err_out_of_range
[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
[`Number.MAX_SAFE_INTEGER`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
[`String.prototype.indexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
[`String.prototype.lastIndexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
[`String.prototype.length`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length
Expand Down
8 changes: 2 additions & 6 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ function copyImpl(source, target, targetStart, sourceStart, sourceEnd) {
return _copyActual(source, target, targetStart, sourceStart, sourceEnd);
}

function _copyActual(source, target, targetStart, sourceStart, sourceEnd, isUint8Copy = false) {
function _copyActual(source, target, targetStart, sourceStart, sourceEnd) {
if (sourceEnd - sourceStart > target.byteLength - targetStart)
sourceEnd = sourceStart + target.byteLength - targetStart;

Expand All @@ -279,11 +279,7 @@ function _copyActual(source, target, targetStart, sourceStart, sourceEnd, isUint
if (nb <= 0)
return 0;

if (sourceStart === 0 && nb === sourceLen && (isUint8Copy || isUint8Array(target))) {
TypedArrayPrototypeSet(target, source, targetStart);
} else {
_copy(source, target, targetStart, sourceStart, nb);
}
_copy(source, target, targetStart, sourceStart, nb);

return nb;
}
Expand Down
29 changes: 11 additions & 18 deletions src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -585,26 +585,17 @@ void StringSlice(const FunctionCallbackInfo<Value>& args) {
}
}

void CopyImpl(Local<Value> source_obj,
Local<Value> target_obj,
const uint32_t target_start,
const uint32_t source_start,
const uint32_t to_copy) {
ArrayBufferViewContents<char> source(source_obj);
SPREAD_BUFFER_ARG(target_obj, target);

memmove(target_data + target_start, source.data() + source_start, to_copy);
}

// Assume caller has properly validated args.
void SlowCopy(const FunctionCallbackInfo<Value>& args) {
Local<Value> source_obj = args[0];
Local<Value> target_obj = args[1];
const uint32_t target_start = args[2].As<Uint32>()->Value();
const uint32_t source_start = args[3].As<Uint32>()->Value();
const uint32_t to_copy = args[4].As<Uint32>()->Value();

CopyImpl(source_obj, target_obj, target_start, source_start, to_copy);
ArrayBufferView::FastCopy(args[0].As<ArrayBufferView>(),
source_start,
args[1].As<ArrayBufferView>(),
target_start,
to_copy);

args.GetReturnValue().Set(to_copy);
}
Expand All @@ -618,10 +609,12 @@ uint32_t FastCopy(Local<Value> receiver,
uint32_t to_copy,
// NOLINTNEXTLINE(runtime/references)
FastApiCallbackOptions& options) {
HandleScope scope(options.isolate);

CopyImpl(source_obj, target_obj, target_start, source_start, to_copy);

TRACK_V8_FAST_API_CALL("buffer.copy");
ArrayBufferView::FastCopy(source_obj.As<ArrayBufferView>(),
source_start,
target_obj.As<ArrayBufferView>(),
target_start,
to_copy);
return to_copy;
}

Expand Down
Loading