Skip to content

Commit

Permalink
buffer: add Buffer.copyBytesFrom(...)
Browse files Browse the repository at this point in the history
Fixes: #43862
PR-URL: #46500
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
  • Loading branch information
jasnell authored and targos committed Mar 14, 2023
1 parent e11f08e commit 2fece54
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 1 deletion.
22 changes: 22 additions & 0 deletions doc/api/buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,28 @@ console.log(bufA.length);
`Buffer.concat()` may also use the internal `Buffer` pool like
[`Buffer.allocUnsafe()`][] does.

### Static method: `Buffer.copyBytesFrom(view[, offset[, length]])`

<!-- YAML
added: REPLACEME
-->

* `view` {TypedArray} The {TypedArray} to copy.
* `offset` {integer} The starting offset within `view`. **Default:**: `0`.
* `length` {integer} The number of elements from `view` to copy.
**Default:** `view.length - offset`.

Copies the underlying memory of `view` into a new `Buffer`.

```js
const u16 = new Uint16Array([0, 0xffff]);
const buf = Buffer.copyBytesFrom(u16, 0, 1);
u16[1] = 0;
console.log(buf.length); // 2
console.log(buf[0]); // 255
console.log(buf[1]); // 255
```

### Static method: `Buffer.from(array)`

<!-- YAML
Expand Down
46 changes: 46 additions & 0 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ const {
StringPrototypeTrim,
SymbolSpecies,
SymbolToPrimitive,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetByteOffset,
TypedArrayPrototypeFill,
TypedArrayPrototypeGetLength,
TypedArrayPrototypeSet,
TypedArrayPrototypeSlice,
Uint8Array,
Uint8ArrayPrototype,
} = primordials;
Expand Down Expand Up @@ -330,6 +334,48 @@ Buffer.from = function from(value, encodingOrOffset, length) {
);
};

/**
* Creates the Buffer as a copy of the underlying ArrayBuffer of the view
* rather than the contents of the view.
* @param {TypedArray} view
* @param {number} [offset]
* @param {number} [length]
* @returns {Buffer}
*/
Buffer.copyBytesFrom = function copyBytesFrom(view, offset, length) {
if (!isTypedArray(view)) {
throw new ERR_INVALID_ARG_TYPE('view', [ 'TypedArray' ], view);
}

const viewLength = TypedArrayPrototypeGetLength(view);
if (viewLength === 0) {
return Buffer.alloc(0);
}

if (offset !== undefined || length !== undefined) {
if (offset !== undefined) {
validateInteger(offset, 'offset', 0);
if (offset >= viewLength) return Buffer.alloc(0);
} else {
offset = 0;
}
let end;
if (length !== undefined) {
validateInteger(length, 'length', 0);
end = offset + length;
} else {
end = viewLength;
}

view = TypedArrayPrototypeSlice(view, offset, end);
}

return fromArrayLike(new Uint8Array(
TypedArrayPrototypeGetBuffer(view),
TypedArrayPrototypeGetByteOffset(view),
TypedArrayPrototypeGetByteLength(view)));
};

// Identical to the built-in %TypedArray%.of(), but avoids using the deprecated
// Buffer() constructor. Must use arrow function syntax to avoid automatically
// adding a `prototype` property and making the function a constructor.
Expand Down
79 changes: 78 additions & 1 deletion test/parallel/test-buffer-from.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const common = require('../common');
const { deepStrictEqual, throws } = require('assert');
const { deepStrictEqual, strictEqual, throws } = require('assert');
const { runInNewContext } = require('vm');

const checkString = 'test';
Expand Down Expand Up @@ -62,3 +62,80 @@ deepStrictEqual(

Buffer.allocUnsafe(10); // Should not throw.
Buffer.from('deadbeaf', 'hex'); // Should not throw.


{
const u16 = new Uint16Array([0xffff]);
const b16 = Buffer.copyBytesFrom(u16);
u16[0] = 0;
strictEqual(b16.length, 2);
strictEqual(b16[0], 255);
strictEqual(b16[1], 255);
}

{
const u16 = new Uint16Array([0, 0xffff]);
const b16 = Buffer.copyBytesFrom(u16, 1, 5);
u16[0] = 0xffff;
u16[1] = 0;
strictEqual(b16.length, 2);
strictEqual(b16[0], 255);
strictEqual(b16[1], 255);
}

{
const u32 = new Uint32Array([0xffffffff]);
const b32 = Buffer.copyBytesFrom(u32);
u32[0] = 0;
strictEqual(b32.length, 4);
strictEqual(b32[0], 255);
strictEqual(b32[1], 255);
strictEqual(b32[2], 255);
strictEqual(b32[3], 255);
}

throws(() => {
Buffer.copyBytesFrom();
}, {
code: 'ERR_INVALID_ARG_TYPE',
});

['', Symbol(), true, false, {}, [], () => {}, 1, 1n, null, undefined].forEach(
(notTypedArray) => throws(() => {
Buffer.copyBytesFrom('nope');
}, {
code: 'ERR_INVALID_ARG_TYPE',
})
);

['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), notANumber);
}, {
code: 'ERR_INVALID_ARG_TYPE',
})
);

[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), outOfRange);
}, {
code: 'ERR_OUT_OF_RANGE',
})
);

['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), 0, notANumber);
}, {
code: 'ERR_INVALID_ARG_TYPE',
})
);

[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), 0, outOfRange);
}, {
code: 'ERR_OUT_OF_RANGE',
})
);

0 comments on commit 2fece54

Please sign in to comment.