diff --git a/index.js b/index.js index b0b5129..6ec8e92 100644 --- a/index.js +++ b/index.js @@ -245,6 +245,41 @@ Buffer.allocUnsafeSlow = function (size) { return allocUnsafe(size) } +/** + * Copies the underlying memory of `view` into a new `Buffer`. + */ +Buffer.copyBytesFrom = function copyBytesFrom (view, offset, length) { + if (!ArrayBuffer.isView(view) || !view.subarray) { + throw new errors.ERR_INVALID_ARG_TYPE('view', 'TypedArray', view) + } + + if (view.length === 0) return createBuffer(0) + + if (offset !== undefined || length !== undefined) { + if (offset !== undefined) { + validateInteger(offset, 'offset') + if (offset >= view.length) return createBuffer(0) + } else { + offset = 0 + } + + let end + + if (length !== undefined) { + validateInteger(length, 'length') + end = offset + length + } else { + end = view.length + } + + view = view.subarray(offset, end) + } + + view = new Uint8Array(view.buffer, view.byteOffset, view.byteLength) + + return fromArrayView(view) +} + function fromString (string, encoding) { if (typeof encoding !== 'string' || encoding === '') { encoding = 'utf8' @@ -1878,8 +1913,8 @@ E('ERR_BUFFER_OUT_OF_BOUNDS', return 'Attempt to access memory outside buffer bounds' }, RangeError) E('ERR_INVALID_ARG_TYPE', - function (name, actual) { - return `The "${name}" argument must be of type number. Received type ${typeof actual}` + function (name, type, actual) { + return `The "${name}" argument must be of type ${type}. Received type ${typeof actual}` }, TypeError) E('ERR_OUT_OF_RANGE', function (str, range, input) { @@ -1943,6 +1978,13 @@ function validateNumber (value, name) { } } +function validateInteger (value, name) { + validateNumber(value, name) + if ((value >>> 0) !== value) { + throw new errors.ERR_BUFFER_OUT_OF_BOUNDS(name) + } +} + function boundsError (value, length, type) { if (Math.floor(value) !== value) { validateNumber(value, type) diff --git a/test/node/test-buffer-from.js b/test/node/test-buffer-from.js index 6857ba3..3deade6 100644 --- a/test/node/test-buffer-from.js +++ b/test/node/test-buffer-from.js @@ -2,7 +2,8 @@ var Buffer = require('../../').Buffer; const common = require('./common'); -const { deepStrictEqual, throws } = require('assert'); +const assert = require('assert'); +const { deepStrictEqual, throws } = assert; const { runInNewContext } = require('vm'); const checkString = 'test'; @@ -66,3 +67,73 @@ deepStrictEqual( throws(() => Buffer.from(input), errMsg); }); +{ + const u16 = new Uint16Array([0xffff]); + const b16 = Buffer.copyBytesFrom(u16); + u16[0] = 0; + assert.strictEqual(b16.length, 2); + assert.strictEqual(b16[0], 255); + assert.strictEqual(b16[1], 255); +} + +{ + const u16 = new Uint16Array([0, 0xffff]); + const b16 = Buffer.copyBytesFrom(u16, 1, 5); + u16[0] = 0xffff; + u16[1] = 0; + assert.strictEqual(b16.length, 2); + assert.strictEqual(b16[0], 255); + assert.strictEqual(b16[1], 255); +} + +{ + const u32 = new Uint32Array([0xffffffff]); + const b32 = Buffer.copyBytesFrom(u32); + u32[0] = 0; + assert.strictEqual(b32.length, 4); + assert.strictEqual(b32[0], 255); + assert.strictEqual(b32[1], 255); + assert.strictEqual(b32[2], 255); + assert.strictEqual(b32[3], 255); +} + +assert.throws(() => { + Buffer.copyBytesFrom(); +}, TypeError); + +{ + const dv = new DataView(new ArrayBuffer(1)); + assert.throws(() => { + Buffer.copyBytesFrom(dv); + }, TypeError); +} + +['', Symbol(), true, false, {}, [], () => {}, 1, 1n, null, undefined].forEach( + notTypedArray => assert.throws(() => { + Buffer.copyBytesFrom(notTypedArray); + }, TypeError) +); + +['', Symbol(), true, false, {}, [], () => {}, 1n].forEach(notANumber => + assert.throws(() => { + Buffer.copyBytesFrom(new Uint8Array(1), notANumber); + }, TypeError) +); + +[-1, NaN, 1.1, -Infinity].forEach(outOfRange => + assert.throws(() => { + Buffer.copyBytesFrom(new Uint8Array(1), outOfRange); + }, RangeError) +); + +['', Symbol(), true, false, {}, [], () => {}, 1n].forEach(notANumber => + assert.throws(() => { + Buffer.copyBytesFrom(new Uint8Array(1), 0, notANumber); + }, TypeError) +); + +[-1, NaN, 1.1, -Infinity].forEach(outOfRange => + assert.throws(() => { + Buffer.copyBytesFrom(new Uint8Array(1), 0, outOfRange); + }, RangeError) +);