Skip to content

Commit c6e722a

Browse files
jasnelldanielleadams
authored andcommitted
buffer: add Buffer.copyBytesFrom(...)
Fixes: #43862 PR-URL: #46500 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent c742493 commit c6e722a

File tree

3 files changed

+146
-1
lines changed

3 files changed

+146
-1
lines changed

doc/api/buffer.md

+22
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,28 @@ console.log(bufA.length);
10561056
`Buffer.concat()` may also use the internal `Buffer` pool like
10571057
[`Buffer.allocUnsafe()`][] does.
10581058

1059+
### Static method: `Buffer.copyBytesFrom(view[, offset[, length]])`
1060+
1061+
<!-- YAML
1062+
added: REPLACEME
1063+
-->
1064+
1065+
* `view` {TypedArray} The {TypedArray} to copy.
1066+
* `offset` {integer} The starting offset within `view`. **Default:**: `0`.
1067+
* `length` {integer} The number of elements from `view` to copy.
1068+
**Default:** `view.length - offset`.
1069+
1070+
Copies the underlying memory of `view` into a new `Buffer`.
1071+
1072+
```js
1073+
const u16 = new Uint16Array([0, 0xffff]);
1074+
const buf = Buffer.copyBytesFrom(u16, 0, 1);
1075+
u16[1] = 0;
1076+
console.log(buf.length); // 2
1077+
console.log(buf[0]); // 255
1078+
console.log(buf[1]); // 255
1079+
```
1080+
10591081
### Static method: `Buffer.from(array)`
10601082

10611083
<!-- YAML

lib/buffer.js

+46
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,13 @@ const {
4444
StringPrototypeTrim,
4545
SymbolSpecies,
4646
SymbolToPrimitive,
47+
TypedArrayPrototypeGetBuffer,
4748
TypedArrayPrototypeGetByteLength,
49+
TypedArrayPrototypeGetByteOffset,
4850
TypedArrayPrototypeFill,
51+
TypedArrayPrototypeGetLength,
4952
TypedArrayPrototypeSet,
53+
TypedArrayPrototypeSlice,
5054
Uint8Array,
5155
Uint8ArrayPrototype,
5256
} = primordials;
@@ -339,6 +343,48 @@ Buffer.from = function from(value, encodingOrOffset, length) {
339343
);
340344
};
341345

346+
/**
347+
* Creates the Buffer as a copy of the underlying ArrayBuffer of the view
348+
* rather than the contents of the view.
349+
* @param {TypedArray} view
350+
* @param {number} [offset]
351+
* @param {number} [length]
352+
* @returns {Buffer}
353+
*/
354+
Buffer.copyBytesFrom = function copyBytesFrom(view, offset, length) {
355+
if (!isTypedArray(view)) {
356+
throw new ERR_INVALID_ARG_TYPE('view', [ 'TypedArray' ], view);
357+
}
358+
359+
const viewLength = TypedArrayPrototypeGetLength(view);
360+
if (viewLength === 0) {
361+
return Buffer.alloc(0);
362+
}
363+
364+
if (offset !== undefined || length !== undefined) {
365+
if (offset !== undefined) {
366+
validateInteger(offset, 'offset', 0);
367+
if (offset >= viewLength) return Buffer.alloc(0);
368+
} else {
369+
offset = 0;
370+
}
371+
let end;
372+
if (length !== undefined) {
373+
validateInteger(length, 'length', 0);
374+
end = offset + length;
375+
} else {
376+
end = viewLength;
377+
}
378+
379+
view = TypedArrayPrototypeSlice(view, offset, end);
380+
}
381+
382+
return fromArrayLike(new Uint8Array(
383+
TypedArrayPrototypeGetBuffer(view),
384+
TypedArrayPrototypeGetByteOffset(view),
385+
TypedArrayPrototypeGetByteLength(view)));
386+
};
387+
342388
// Identical to the built-in %TypedArray%.of(), but avoids using the deprecated
343389
// Buffer() constructor. Must use arrow function syntax to avoid automatically
344390
// adding a `prototype` property and making the function a constructor.

test/parallel/test-buffer-from.js

+78-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
const common = require('../common');
4-
const { deepStrictEqual, throws } = require('assert');
4+
const { deepStrictEqual, strictEqual, throws } = require('assert');
55
const { runInNewContext } = require('vm');
66

77
const checkString = 'test';
@@ -62,3 +62,80 @@ deepStrictEqual(
6262

6363
Buffer.allocUnsafe(10); // Should not throw.
6464
Buffer.from('deadbeaf', 'hex'); // Should not throw.
65+
66+
67+
{
68+
const u16 = new Uint16Array([0xffff]);
69+
const b16 = Buffer.copyBytesFrom(u16);
70+
u16[0] = 0;
71+
strictEqual(b16.length, 2);
72+
strictEqual(b16[0], 255);
73+
strictEqual(b16[1], 255);
74+
}
75+
76+
{
77+
const u16 = new Uint16Array([0, 0xffff]);
78+
const b16 = Buffer.copyBytesFrom(u16, 1, 5);
79+
u16[0] = 0xffff;
80+
u16[1] = 0;
81+
strictEqual(b16.length, 2);
82+
strictEqual(b16[0], 255);
83+
strictEqual(b16[1], 255);
84+
}
85+
86+
{
87+
const u32 = new Uint32Array([0xffffffff]);
88+
const b32 = Buffer.copyBytesFrom(u32);
89+
u32[0] = 0;
90+
strictEqual(b32.length, 4);
91+
strictEqual(b32[0], 255);
92+
strictEqual(b32[1], 255);
93+
strictEqual(b32[2], 255);
94+
strictEqual(b32[3], 255);
95+
}
96+
97+
throws(() => {
98+
Buffer.copyBytesFrom();
99+
}, {
100+
code: 'ERR_INVALID_ARG_TYPE',
101+
});
102+
103+
['', Symbol(), true, false, {}, [], () => {}, 1, 1n, null, undefined].forEach(
104+
(notTypedArray) => throws(() => {
105+
Buffer.copyBytesFrom('nope');
106+
}, {
107+
code: 'ERR_INVALID_ARG_TYPE',
108+
})
109+
);
110+
111+
['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
112+
throws(() => {
113+
Buffer.copyBytesFrom(new Uint8Array(1), notANumber);
114+
}, {
115+
code: 'ERR_INVALID_ARG_TYPE',
116+
})
117+
);
118+
119+
[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
120+
throws(() => {
121+
Buffer.copyBytesFrom(new Uint8Array(1), outOfRange);
122+
}, {
123+
code: 'ERR_OUT_OF_RANGE',
124+
})
125+
);
126+
127+
['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
128+
throws(() => {
129+
Buffer.copyBytesFrom(new Uint8Array(1), 0, notANumber);
130+
}, {
131+
code: 'ERR_INVALID_ARG_TYPE',
132+
})
133+
);
134+
135+
[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
136+
throws(() => {
137+
Buffer.copyBytesFrom(new Uint8Array(1), 0, outOfRange);
138+
}, {
139+
code: 'ERR_OUT_OF_RANGE',
140+
})
141+
);

0 commit comments

Comments
 (0)