diff --git a/benchmark/buffers/buffer-copy-static.js b/benchmark/buffers/buffer-copy-static.js new file mode 100644 index 00000000000000..883a0e1d7d3af7 --- /dev/null +++ b/benchmark/buffers/buffer-copy-static.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + bytes: [8, 128, 1024, 16384], + type: ['buffer', 'uint8array', 'arraybuffer'], + partial: ['true', 'false'], + n: [5e5], +}); + +function main({ n, bytes, type, partial }) { + let source, target; + + switch (type) { + case 'buffer': + source = Buffer.allocUnsafe(bytes); + target = Buffer.allocUnsafe(bytes); + break; + case 'uint8array': + source = new Uint8Array(bytes); + target = new Uint8Array(bytes); + break; + case 'arraybuffer': + source = new ArrayBuffer(bytes); + target = new ArrayBuffer(bytes); + break; + } + + const sourceStart = (partial === 'true' ? Math.floor(bytes / 2) : 0); + const sourceEnd = bytes; + + bench.start(); + for (let i = 0; i < n; i++) { + Buffer.copy(source, target, 0, sourceStart, sourceEnd); + } + bench.end(n); +} diff --git a/benchmark/buffers/buffer-copy-web-api.js b/benchmark/buffers/buffer-copy-web-api.js new file mode 100644 index 00000000000000..e2d49cdef14509 --- /dev/null +++ b/benchmark/buffers/buffer-copy-web-api.js @@ -0,0 +1,50 @@ +'use strict'; +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + bytes: [8, 128, 1024, 16384], + type: ['buffer', 'uint8array', 'arraybuffer'], + partial: ['true', 'false'], + n: [5e5], +}); + +function main({ n, bytes, type, partial }) { + let source, target, srcView, dstBuffer; + + switch (type) { + case 'buffer': + source = Buffer.allocUnsafe(bytes); + target = Buffer.allocUnsafe(bytes); + break; + case 'uint8array': + source = new Uint8Array(bytes); + target = new Uint8Array(bytes); + break; + case 'arraybuffer': + source = new ArrayBuffer(bytes); + target = new ArrayBuffer(bytes); + break; + } + + const srcOffset = (partial === 'true' ? Math.floor(bytes / 2) : 0); + const length = bytes - srcOffset; + const dstOffset = 0; + + if (type === 'arraybuffer') { + srcView = new Uint8Array(source, srcOffset, length); + dstBuffer = new Uint8Array(target); + } else { + srcView = new Uint8Array( + source.buffer || source, + source.byteOffset !== undefined ? source.byteOffset + srcOffset : srcOffset, + length + ); + dstBuffer = new Uint8Array(target.buffer || target, target.byteOffset || 0); + } + + bench.start(); + for (let i = 0; i < n; i++) { + dstBuffer.set(srcView, dstOffset); + } + bench.end(n); +} diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 981c053ac40f0c..83278360a791f8 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -919,6 +919,99 @@ socket.on('readable', () => { A `TypeError` will be thrown if `size` is not a number. +### Static method: `Buffer.copy(source, target, targetStart[, sourceStart[, sourceEnd]])` + + + +* `source` {Buffer|TypedArray|DataView|ArrayBuffer|SharedArrayBuffer} The source + to copy data from. +* `target` {Buffer|TypedArray|DataView|ArrayBuffer|SharedArrayBuffer} The target + to copy data to. +* `targetStart` {integer} The offset within `target` at which to begin writing. + **Default:** `0`. +* `sourceStart` {integer} The offset within `source` from which to begin copying. + **Default:** `0`. +* `sourceEnd` {integer} The offset within `source` at which to stop copying + (exclusive). **Default:** `source.byteLength`. +* Returns: {integer} The number of bytes copied. + +Copies data from `source` to `target`. This is a method that can copy data +between different types of binary data structures, including `Buffer`, +`TypedArray`, `DataView`, `ArrayBuffer`, and `SharedArrayBuffer` instances. + +```mjs +import { Buffer } from 'node:buffer'; + +const src = Buffer.from([1, 2, 3, 4]); +const dst = Buffer.alloc(4); + +const bytesCopied = Buffer.copy(src, dst, 0); +console.log(bytesCopied); // 4 +console.log(dst); // +``` + +```cjs +const { Buffer } = require('node:buffer'); + +const src = Buffer.from([1, 2, 3, 4]); +const dst = Buffer.alloc(4); + +const bytesCopied = Buffer.copy(src, dst, 0); +console.log(bytesCopied); // 4 +console.log(dst); // +``` + +The method can also copy between different types: + +```mjs +import { Buffer } from 'node:buffer'; + +// Copy from ArrayBuffer to Buffer +const ab = new Uint8Array([5, 6, 7, 8]).buffer; +const buf = Buffer.alloc(4); + +Buffer.copy(ab, buf, 0); +console.log(buf); // + +// Copy from Buffer to DataView +const src = Buffer.from([1, 2, 3]); +const targetAB = new ArrayBuffer(5); +const dv = new DataView(targetAB); + +Buffer.copy(src, dv, 2); +console.log(new Uint8Array(targetAB)); // Uint8Array(5) [ 0, 0, 1, 2, 3 ] +``` + +```cjs +const { Buffer } = require('node:buffer'); + +// Copy from ArrayBuffer to Buffer +const ab = new Uint8Array([5, 6, 7, 8]).buffer; +const buf = Buffer.alloc(4); + +Buffer.copy(ab, buf, 0); +console.log(buf); // + +// Copy from Buffer to DataView +const src = Buffer.from([1, 2, 3]); +const targetAB = new ArrayBuffer(5); +const dv = new DataView(targetAB); + +Buffer.copy(src, dv, 2); +console.log(new Uint8Array(targetAB)); // Uint8Array(5) [ 0, 0, 1, 2, 3 ] +``` + +If `targetStart` is negative or beyond the length of `target`, a \[`RangeError`]\[] +is thrown. If `sourceStart` is negative or beyond the length of `source`, a +\[`RangeError`]\[] is thrown. If `sourceEnd` is negative, a \[`RangeError`]\[] is +thrown. Values that exceed the respective buffer lengths are clamped to the +appropriate limits. + +If `sourceStart` is greater than or equal to `sourceEnd`, zero bytes are copied +and the method returns `0`. + ### Static method: `Buffer.byteLength(string[, encoding])`