Route the native backing of Buffer.prototype.copy (CopyImpl, the `_copy` binding) through V8's new v8::ArrayBuffer::CopyArrayBufferBytes API (added in the preceding commit) instead of materializing an ArrayBufferViewContents and doing a manual memmove. This speeds up partial copies (sourceStart > 0). All copies now go through this binding: the previous %TypedArray%.prototype.set fast-path for whole-buffer copies is dropped, since it would throw on a detached or immutable target rather than report a 0-byte no-op, and the benchmarks below show the native path is comparable for that case. When both sides are backed by a SharedArrayBuffer the relaxed-atomic overload is used, which honors the SharedArrayBuffer memory model. Mixed copies (one SharedArrayBuffer, one ArrayBuffer) use the regular overload. copy() now reports the number of bytes actually copied, as returned by V8: 0 when the target is backed by a detached or immutable ArrayBuffer. The copy is then a no-op rather than a write to read-only memory. The native binding now plumbs byte offsets and the copied-byte count through as size_t, passed across the fast/slow API boundary as doubles (exact for integer values below 2^53), so copies that cross the 4 GiB boundary are no longer truncated to 32 bits. buffer-copy.js vs node v26.3.0 (x64, 30 runs): partial=false bytes=1024: +7.91% (***) partial=false bytes=128: +0.17% partial=false bytes=8: -0.89% partial=true bytes=1024: +22.33% (***) partial=true bytes=128: +19.58% (***) partial=true bytes=8: +18.70% (***) This supersedes the prototype in https://github.com/nodejs/node/pull/62491, which added a bespoke ArrayBufferView::FastCopy instead of using the upstream-friendly CopyArrayBufferBytes API. Adds SharedArrayBuffer and immutable-ArrayBuffer coverage to the buffer copy tests, plus a pummel regression test for copies larger than 2^32 bytes. Refs: https://github.com/nodejs/node/issues/55422 Signed-off-by: Robert Nagy <ronagy@icloud.com> Assisted-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> PR-URL: https://github.com/nodejs/node/pull/63828 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
48 lines
1.9 KiB
JavaScript
48 lines
1.9 KiB
JavaScript
// Flags: --js-immutable-arraybuffer
|
|
'use strict';
|
|
const common = require('../common');
|
|
const assert = require('assert');
|
|
|
|
// transferToImmutable is gated behind --js-immutable-arraybuffer (set above).
|
|
// Skip if this V8 build does not expose the API even with the flag set.
|
|
if (typeof ArrayBuffer.prototype.transferToImmutable !== 'function')
|
|
common.skip('ArrayBuffer.prototype.transferToImmutable is not available');
|
|
|
|
// Copying *into* a buffer backed by an immutable ArrayBuffer must not write to
|
|
// the read-only backing store. The copy is a no-op and reports 0 bytes copied.
|
|
{
|
|
const ab = new ArrayBuffer(8);
|
|
new Uint8Array(ab).set([1, 2, 3, 4, 5, 6, 7, 8]);
|
|
const target = Buffer.from(ab.transferToImmutable());
|
|
const source = Buffer.from([9, 9, 9, 9, 9, 9, 9, 9]);
|
|
|
|
assert.strictEqual(source.copy(target), 0);
|
|
assert.deepStrictEqual([...target], [1, 2, 3, 4, 5, 6, 7, 8]);
|
|
|
|
// A partial / offset copy is also a no-op.
|
|
assert.strictEqual(source.copy(target, 2, 0, 4), 0);
|
|
assert.deepStrictEqual([...target], [1, 2, 3, 4, 5, 6, 7, 8]);
|
|
}
|
|
|
|
// Copying *from* a buffer backed by an immutable ArrayBuffer is allowed (reads
|
|
// do not require a writable backing store) and reports the bytes copied.
|
|
{
|
|
const ab = new ArrayBuffer(8);
|
|
new Uint8Array(ab).set([10, 20, 30, 40, 50, 60, 70, 80]);
|
|
const source = Buffer.from(ab.transferToImmutable());
|
|
const target = Buffer.alloc(8);
|
|
|
|
assert.strictEqual(source.copy(target), 8);
|
|
assert.deepStrictEqual([...target], [10, 20, 30, 40, 50, 60, 70, 80]);
|
|
}
|
|
|
|
// A mutable Uint8Array view onto an immutable ArrayBuffer is still a read-only
|
|
// target through Buffer.prototype.copy.
|
|
{
|
|
const ab = new ArrayBuffer(4);
|
|
new Uint8Array(ab).set([100, 101, 102, 103]);
|
|
const target = new Uint8Array(ab.transferToImmutable());
|
|
|
|
assert.strictEqual(Buffer.from([1, 2, 3, 4]).copy(target), 0);
|
|
assert.deepStrictEqual([...target], [100, 101, 102, 103]);
|
|
}
|