Skip to content

Commit 90c30ad

Browse files
committed
buffer: cleanup handling different types buffers
PR-URL: #54439 Assisted-by: Claude Code:Opus 4.6
1 parent f1ed8d6 commit 90c30ad

File tree

5 files changed

+346
-64
lines changed

5 files changed

+346
-64
lines changed

lib/buffer.js

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ const {
104104
isAnyArrayBuffer,
105105
isArrayBufferView,
106106
isUint8Array,
107-
isTypedArray,
108107
} = require('internal/util/types');
109108
const {
110109
inspect: utilInspect,
@@ -154,6 +153,14 @@ FastBuffer.prototype.constructor = Buffer;
154153
Buffer.prototype = FastBuffer.prototype;
155154
addBufferPrototypeMethods(Buffer.prototype);
156155

156+
const BUFFER_NAMES = [
157+
'ArrayBuffer',
158+
'Buffer',
159+
'TypedArray',
160+
'DataView',
161+
'SharedArrayBuffer',
162+
];
163+
157164
const constants = ObjectDefineProperties({}, {
158165
MAX_LENGTH: {
159166
__proto__: null,
@@ -232,10 +239,18 @@ function toInteger(n, defaultVal) {
232239
}
233240

234241
function copyImpl(source, target, targetStart, sourceStart, sourceEnd) {
235-
if (!ArrayBufferIsView(source))
236-
throw new ERR_INVALID_ARG_TYPE('source', ['Buffer', 'Uint8Array'], source);
237-
if (!ArrayBufferIsView(target))
238-
throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'Uint8Array'], target);
242+
if (source?.constructor === DataView || isAnyArrayBuffer(source)) {
243+
source = new Uint8Array(source.buffer ?? source, source.byteOffset ?? 0,
244+
source.byteLength);
245+
} else if (!isArrayBufferView(source)) {
246+
throw new ERR_INVALID_ARG_TYPE('source', BUFFER_NAMES, source);
247+
}
248+
if (target?.constructor === DataView || isAnyArrayBuffer(target)) {
249+
target = new Uint8Array(target.buffer ?? target, target.byteOffset ?? 0,
250+
target.byteLength);
251+
} else if (!isArrayBufferView(target)) {
252+
throw new ERR_INVALID_ARG_TYPE('target', BUFFER_NAMES, target);
253+
}
239254

240255
if (targetStart === undefined) {
241256
targetStart = 0;
@@ -359,22 +374,29 @@ Buffer.from = function from(value, encodingOrOffset, length) {
359374

360375
throw new ERR_INVALID_ARG_TYPE(
361376
'first argument',
362-
['string', 'Buffer', 'ArrayBuffer', 'Array', 'Array-like Object'],
377+
['string', 'Array', 'Array-like Object', ...BUFFER_NAMES],
363378
value,
364379
);
365380
};
366381

367382
/**
368383
* Creates the Buffer as a copy of the underlying ArrayBuffer of the view
369384
* rather than the contents of the view.
370-
* @param {TypedArray} view
385+
* @param {Buffer|TypedArray|DataView} view
371386
* @param {number} [offset]
372387
* @param {number} [length]
373388
* @returns {Buffer}
374389
*/
375390
Buffer.copyBytesFrom = function copyBytesFrom(view, offset, length) {
376-
if (!isTypedArray(view)) {
377-
throw new ERR_INVALID_ARG_TYPE('view', [ 'TypedArray' ], view);
391+
if (view?.constructor === DataView) {
392+
// Normalize DataView to Uint8Array so we can use TypedArray primordials.
393+
view = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
394+
} else if (isArrayBufferView(view)) {
395+
// Do nothing..
396+
} else if (isAnyArrayBuffer(view)) {
397+
view = new Uint8Array(view);
398+
} else {
399+
throw new ERR_INVALID_ARG_TYPE('view', BUFFER_NAMES, view);
378400
}
379401

380402
const viewLength = TypedArrayPrototypeGetLength(view);
@@ -399,16 +421,11 @@ Buffer.copyBytesFrom = function copyBytesFrom(view, offset, length) {
399421

400422
if (end <= start) return new FastBuffer();
401423

402-
const viewByteLength = TypedArrayPrototypeGetByteLength(view);
403-
const elementSize = viewByteLength / viewLength;
404-
const srcByteOffset = TypedArrayPrototypeGetByteOffset(view) +
405-
start * elementSize;
406-
const srcByteLength = (end - start) * elementSize;
407-
424+
const srcView = TypedArrayPrototypeSubarray(view, start, end);
408425
return fromArrayLike(new Uint8Array(
409-
TypedArrayPrototypeGetBuffer(view),
410-
srcByteOffset,
411-
srcByteLength));
426+
TypedArrayPrototypeGetBuffer(srcView),
427+
TypedArrayPrototypeGetByteOffset(srcView),
428+
TypedArrayPrototypeGetByteLength(srcView)));
412429
};
413430

414431
// Identical to the built-in %TypedArray%.of(), but avoids using the deprecated
@@ -620,12 +637,17 @@ Buffer.concat = function concat(list, length) {
620637
if (length === undefined) {
621638
length = 0;
622639
for (let i = 0; i < list.length; i++) {
623-
const buf = list[i];
624-
if (!isUint8Array(buf)) {
640+
let buf = list[i];
641+
if (buf?.constructor === DataView || isAnyArrayBuffer(buf)) {
642+
buf = list[i] = new Uint8Array(buf.buffer ?? buf, buf.byteOffset ?? 0,
643+
buf.byteLength);
644+
} else if (isArrayBufferView(buf)) {
645+
// Do nothing..
646+
} else {
625647
// TODO(BridgeAR): This should not be of type ERR_INVALID_ARG_TYPE.
626648
// Instead, find the proper error code for this.
627649
throw new ERR_INVALID_ARG_TYPE(
628-
`list[${i}]`, ['Buffer', 'Uint8Array'], buf);
650+
`list[${i}]`, BUFFER_NAMES, buf);
629651
}
630652
length += TypedArrayPrototypeGetByteLength(buf);
631653
}
@@ -647,11 +669,17 @@ Buffer.concat = function concat(list, length) {
647669

648670
validateOffset(length, 'length');
649671
for (let i = 0; i < list.length; i++) {
650-
if (!isUint8Array(list[i])) {
672+
if (list[i]?.constructor === DataView || isAnyArrayBuffer(list[i])) {
673+
const buf = list[i];
674+
list[i] = new Uint8Array(buf.buffer ?? buf, buf.byteOffset ?? 0,
675+
buf.byteLength);
676+
} else if (isArrayBufferView(list[i])) {
677+
// Do nothing..
678+
} else {
651679
// TODO(BridgeAR): This should not be of type ERR_INVALID_ARG_TYPE.
652680
// Instead, find the proper error code for this.
653681
throw new ERR_INVALID_ARG_TYPE(
654-
`list[${i}]`, ['Buffer', 'Uint8Array'], list[i]);
682+
`list[${i}]`, BUFFER_NAMES, list[i]);
655683
}
656684
}
657685

@@ -834,10 +862,7 @@ function byteLength(string, encoding) {
834862
if (isArrayBufferView(string) || isAnyArrayBuffer(string)) {
835863
return string.byteLength;
836864
}
837-
838-
throw new ERR_INVALID_ARG_TYPE(
839-
'string', ['string', 'Buffer', 'ArrayBuffer'], string,
840-
);
865+
throw new ERR_INVALID_ARG_TYPE('string', ['string', ...BUFFER_NAMES], string);
841866
}
842867

843868
const len = string.length;
@@ -926,15 +951,18 @@ Buffer.prototype.toString = function toString(encoding, start, end) {
926951
};
927952

928953
Buffer.prototype.equals = function equals(otherBuffer) {
929-
if (!isUint8Array(otherBuffer)) {
930-
throw new ERR_INVALID_ARG_TYPE(
931-
'otherBuffer', ['Buffer', 'Uint8Array'], otherBuffer);
954+
if (isArrayBufferView(otherBuffer)) {
955+
// Do nothing..
956+
} else if (isAnyArrayBuffer(otherBuffer)) {
957+
otherBuffer = new Uint8Array(otherBuffer);
958+
} else {
959+
throw new ERR_INVALID_ARG_TYPE('otherBuffer', BUFFER_NAMES, otherBuffer);
932960
}
933961

934962
if (this === otherBuffer)
935963
return true;
936964
const len = TypedArrayPrototypeGetByteLength(this);
937-
if (len !== TypedArrayPrototypeGetByteLength(otherBuffer))
965+
if (len !== otherBuffer.byteLength)
938966
return false;
939967

940968
return len === 0 || _compare(this, otherBuffer) === 0;
@@ -988,9 +1016,14 @@ Buffer.prototype.compare = function compare(target,
9881016
targetEnd,
9891017
sourceStart,
9901018
sourceEnd) {
991-
if (!isUint8Array(target)) {
992-
throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'Uint8Array'], target);
1019+
if (isArrayBufferView(target)) {
1020+
// Do nothing..
1021+
} else if (isAnyArrayBuffer(target)) {
1022+
target = new Uint8Array(target);
1023+
} else {
1024+
throw new ERR_INVALID_ARG_TYPE('target', BUFFER_NAMES, target);
9931025
}
1026+
9941027
if (arguments.length === 1)
9951028
return _compare(this, target);
9961029

@@ -1066,14 +1099,14 @@ function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) {
10661099
return ops.indexOf(buffer, val, byteOffset, dir);
10671100
}
10681101

1069-
if (isUint8Array(val)) {
1102+
if (isArrayBufferView(val) || isAnyArrayBuffer(val)) {
10701103
const encodingVal =
10711104
(ops === undefined ? encodingsMap.utf8 : ops.encodingVal);
10721105
return indexOfBuffer(buffer, val, byteOffset, encodingVal, dir);
10731106
}
10741107

10751108
throw new ERR_INVALID_ARG_TYPE(
1076-
'value', ['number', 'string', 'Buffer', 'Uint8Array'], val,
1109+
'value', ['number', 'string', ...BUFFER_NAMES], val,
10771110
);
10781111
}
10791112

@@ -1244,7 +1277,9 @@ Buffer.prototype.subarray = function subarray(start, end) {
12441277
start = adjustOffset(start, srcLength);
12451278
end = end !== undefined ? adjustOffset(end, srcLength) : srcLength;
12461279
const newLength = end > start ? end - start : 0;
1247-
return new FastBuffer(this.buffer, this.byteOffset + start, newLength);
1280+
return new FastBuffer(TypedArrayPrototypeGetBuffer(this),
1281+
TypedArrayPrototypeGetByteOffset(this) + start,
1282+
newLength);
12481283
};
12491284

12501285
Buffer.prototype.slice = function slice(start, end) {
@@ -1328,9 +1363,8 @@ if (internalBinding('config').hasIntl) {
13281363
// Transcodes the Buffer from one encoding to another, returning a new
13291364
// Buffer instance.
13301365
transcode = function transcode(source, fromEncoding, toEncoding) {
1331-
if (!isUint8Array(source)) {
1332-
throw new ERR_INVALID_ARG_TYPE('source',
1333-
['Buffer', 'Uint8Array'], source);
1366+
if (!ArrayBufferIsView(source) && !isAnyArrayBuffer(source)) {
1367+
throw new ERR_INVALID_ARG_TYPE('source', BUFFER_NAMES, source);
13341368
}
13351369
if (source.length === 0) return new FastBuffer();
13361370

@@ -1386,19 +1420,19 @@ function atob(input) {
13861420
}
13871421

13881422
function isUtf8(input) {
1389-
if (isTypedArray(input) || isAnyArrayBuffer(input)) {
1423+
if (isArrayBufferView(input) || isAnyArrayBuffer(input)) {
13901424
return bindingIsUtf8(input);
13911425
}
13921426

1393-
throw new ERR_INVALID_ARG_TYPE('input', ['ArrayBuffer', 'Buffer', 'TypedArray'], input);
1427+
throw new ERR_INVALID_ARG_TYPE('input', BUFFER_NAMES, input);
13941428
}
13951429

13961430
function isAscii(input) {
1397-
if (isTypedArray(input) || isAnyArrayBuffer(input)) {
1431+
if (isArrayBufferView(input) || isAnyArrayBuffer(input)) {
13981432
return bindingIsAscii(input);
13991433
}
14001434

1401-
throw new ERR_INVALID_ARG_TYPE('input', ['ArrayBuffer', 'Buffer', 'TypedArray'], input);
1435+
throw new ERR_INVALID_ARG_TYPE('input', BUFFER_NAMES, input);
14021436
}
14031437

14041438
module.exports = {

test/parallel/test-buffer-compare.js

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,54 @@ assert.strictEqual(Buffer.compare(Buffer.alloc(1), Buffer.alloc(0)), 1);
3030

3131
assert.throws(() => Buffer.compare(Buffer.alloc(1), 'abc'), {
3232
code: 'ERR_INVALID_ARG_TYPE',
33-
message: 'The "buf2" argument must be an instance of Buffer or Uint8Array. ' +
34-
"Received type string ('abc')"
3533
});
3634
assert.throws(() => Buffer.compare('abc', Buffer.alloc(1)), {
3735
code: 'ERR_INVALID_ARG_TYPE',
38-
message: 'The "buf1" argument must be an instance of Buffer or Uint8Array. ' +
39-
"Received type string ('abc')"
4036
});
4137

4238
assert.throws(() => Buffer.alloc(1).compare('abc'), {
4339
code: 'ERR_INVALID_ARG_TYPE',
44-
name: 'TypeError',
45-
message: 'The "target" argument must be an instance of ' +
46-
"Buffer or Uint8Array. Received type string ('abc')"
4740
});
41+
42+
// compare works with ArrayBuffer
43+
{
44+
const buf = Buffer.from([1, 2, 3]);
45+
const ab = new ArrayBuffer(3);
46+
new Uint8Array(ab).set([1, 2, 3]);
47+
assert.strictEqual(buf.compare(ab), 0);
48+
49+
const ab2 = new ArrayBuffer(3);
50+
new Uint8Array(ab2).set([4, 5, 6]);
51+
assert.strictEqual(buf.compare(ab2), -1);
52+
53+
const ab3 = new ArrayBuffer(3);
54+
new Uint8Array(ab3).set([0, 0, 0]);
55+
assert.strictEqual(buf.compare(ab3), 1);
56+
}
57+
58+
// compare works with DataView
59+
{
60+
const buf = Buffer.from([1, 2, 3]);
61+
const dv = new DataView(new ArrayBuffer(3));
62+
new Uint8Array(dv.buffer).set([1, 2, 3]);
63+
assert.strictEqual(buf.compare(dv), 0);
64+
65+
const dv2 = new DataView(new ArrayBuffer(3));
66+
new Uint8Array(dv2.buffer).set([4, 5, 6]);
67+
assert.strictEqual(buf.compare(dv2), -1);
68+
}
69+
70+
// compare works with SharedArrayBuffer
71+
{
72+
const buf = Buffer.from([1, 2, 3]);
73+
const sab = new SharedArrayBuffer(3);
74+
new Uint8Array(sab).set([1, 2, 3]);
75+
assert.strictEqual(buf.compare(sab), 0);
76+
}
77+
78+
// compare works with other TypedArrays
79+
{
80+
const buf = Buffer.from([0x01, 0x00, 0x02, 0x00]);
81+
const u16 = new Uint16Array([1, 2]);
82+
assert.strictEqual(buf.compare(u16), 0);
83+
}

0 commit comments

Comments
 (0)