Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit beca324

Browse filesBrowse files
committed
buffer: allow Uint8Array input to methods
Allow all methods on `buffer` and `Buffer` to take `Uint8Array` arguments where it makes sense. On the native side, there is effectively no difference, and as a bonus the `isUint8Array` check is faster than `instanceof Buffer`. PR-URL: #10236 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
1 parent 6cb33c0 commit beca324
Copy full SHA for beca324

File tree

Expand file treeCollapse file tree

10 files changed

+81
-45
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

10 files changed

+81
-45
lines changed
Open diff view settings
Collapse file

‎doc/api/buffer.md‎

Copy file name to clipboardExpand all lines: doc/api/buffer.md
+12-12Lines changed: 12 additions & 12 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -635,8 +635,8 @@ actual byte length is returned.
635635
added: v0.11.13
636636
-->
637637

638-
* `buf1` {Buffer}
639-
* `buf2` {Buffer}
638+
* `buf1` {Buffer|Uint8Array}
639+
* `buf2` {Buffer|Uint8Array}
640640
* Returns: {Integer}
641641

642642
Compares `buf1` to `buf2` typically for the purpose of sorting arrays of
@@ -660,7 +660,7 @@ console.log(arr.sort(Buffer.compare));
660660
added: v0.7.11
661661
-->
662662

663-
* `list` {Array} List of `Buffer` instances to concat
663+
* `list` {Array} List of `Buffer` or [`Uint8Array`] instances to concat
664664
* `totalLength` {Integer} Total length of the `Buffer` instances in `list`
665665
when concatenated
666666
* Returns: {Buffer}
@@ -882,7 +882,7 @@ console.log(buf.toString('ascii'));
882882
added: v0.11.13
883883
-->
884884

885-
* `target` {Buffer} A `Buffer` to compare to
885+
* `target` {Buffer|Uint8Array} A `Buffer` or [`Uint8Array`] to compare to
886886
* `targetStart` {Integer} The offset within `target` at which to begin
887887
comparison. **Default:** `0`
888888
* `targetEnd` {Integer} The offset with `target` at which to end comparison
@@ -1037,7 +1037,7 @@ for (const pair of buf.entries()) {
10371037
added: v0.11.13
10381038
-->
10391039

1040-
* `otherBuffer` {Buffer} A `Buffer` to compare to
1040+
* `otherBuffer` {Buffer} A `Buffer` or [`Uint8Array`] to compare to
10411041
* Returns: {Boolean}
10421042

10431043
Returns `true` if both `buf` and `otherBuffer` have exactly the same bytes,
@@ -1099,7 +1099,7 @@ console.log(Buffer.allocUnsafe(3).fill('\u0222'));
10991099
added: v1.5.0
11001100
-->
11011101

1102-
* `value` {String | Buffer | Integer} What to search for
1102+
* `value` {String | Buffer | Uint8Array | Integer} What to search for
11031103
* `byteOffset` {Integer} Where to begin searching in `buf`. **Default:** `0`
11041104
* `encoding` {String} If `value` is a string, this is its encoding.
11051105
**Default:** `'utf8'`
@@ -1110,8 +1110,8 @@ If `value` is:
11101110

11111111
* a string, `value` is interpreted according to the character encoding in
11121112
`encoding`.
1113-
* a `Buffer`, `value` will be used in its entirety. To compare a partial
1114-
`Buffer` use [`buf.slice()`].
1113+
* a `Buffer` or [`Uint8Array`], `value` will be used in its entirety.
1114+
To compare a partial `Buffer`, use [`buf.slice()`].
11151115
* a number, `value` will be interpreted as an unsigned 8-bit integer
11161116
value between `0` and `255`.
11171117

@@ -1221,7 +1221,7 @@ for (const key of buf.keys()) {
12211221
added: v6.0.0
12221222
-->
12231223

1224-
* `value` {String | Buffer | Integer} What to search for
1224+
* `value` {String | Buffer | Uint8Array | Integer} What to search for
12251225
* `byteOffset` {Integer} Where to begin searching in `buf`.
12261226
**Default:** [`buf.length`]` - 1`
12271227
* `encoding` {String} If `value` is a string, this is its encoding.
@@ -2313,12 +2313,12 @@ Note that this is a property on the `buffer` module returned by
23132313
added: v7.1.0
23142314
-->
23152315

2316-
* `source` {Buffer} A `Buffer` instance
2316+
* `source` {Buffer|Uint8Array} A `Buffer` or `Uint8Array` instance
23172317
* `fromEnc` {String} The current encoding
23182318
* `toEnc` {String} To target encoding
23192319

2320-
Re-encodes the given `Buffer` instance from one character encoding to another.
2321-
Returns a new `Buffer` instance.
2320+
Re-encodes the given `Buffer` or `Uint8Array` instance from one character
2321+
encoding to another. Returns a new `Buffer` instance.
23222322

23232323
Throws if the `fromEnc` or `toEnc` specify invalid character encodings or if
23242324
conversion from `fromEnc` to `toEnc` is not permitted.
Collapse file

‎lib/buffer.js‎

Copy file name to clipboardExpand all lines: lib/buffer.js
+25-18Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
'use strict';
33

44
const binding = process.binding('buffer');
5-
const { isArrayBuffer, isSharedArrayBuffer } = process.binding('util');
5+
const { isArrayBuffer, isSharedArrayBuffer, isUint8Array } =
6+
process.binding('util');
67
const bindingObj = {};
78
const internalUtil = require('internal/util');
89

@@ -251,13 +252,13 @@ function fromArrayBuffer(obj, byteOffset, length) {
251252
}
252253

253254
function fromObject(obj) {
254-
if (obj instanceof Buffer) {
255+
if (isUint8Array(obj)) {
255256
const b = allocate(obj.length);
256257

257258
if (b.length === 0)
258259
return b;
259260

260-
obj.copy(b, 0, 0, obj.length);
261+
binding.copy(obj, b, 0, 0, obj.length);
261262
return b;
262263
}
263264

@@ -287,9 +288,8 @@ Buffer.isBuffer = function isBuffer(b) {
287288

288289

289290
Buffer.compare = function compare(a, b) {
290-
if (!(a instanceof Buffer) ||
291-
!(b instanceof Buffer)) {
292-
throw new TypeError('Arguments must be Buffers');
291+
if (!isUint8Array(a) || !isUint8Array(b)) {
292+
throw new TypeError('Arguments must be Buffers or Uint8Arrays');
293293
}
294294

295295
if (a === b) {
@@ -306,10 +306,13 @@ Buffer.isEncoding = function(encoding) {
306306
};
307307
Buffer[internalUtil.kIsEncodingSymbol] = Buffer.isEncoding;
308308

309+
const kConcatErrMsg = '"list" argument must be an Array ' +
310+
'of Buffer or Uint8Array instances';
311+
309312
Buffer.concat = function(list, length) {
310313
var i;
311314
if (!Array.isArray(list))
312-
throw new TypeError('"list" argument must be an Array of Buffers');
315+
throw new TypeError(kConcatErrMsg);
313316

314317
if (list.length === 0)
315318
return new FastBuffer();
@@ -326,9 +329,9 @@ Buffer.concat = function(list, length) {
326329
var pos = 0;
327330
for (i = 0; i < list.length; i++) {
328331
var buf = list[i];
329-
if (!Buffer.isBuffer(buf))
330-
throw new TypeError('"list" argument must be an Array of Buffers');
331-
buf.copy(buffer, pos);
332+
if (!isUint8Array(buf))
333+
throw new TypeError(kConcatErrMsg);
334+
binding.copy(buf, buffer, pos);
332335
pos += buf.length;
333336
}
334337

@@ -495,6 +498,9 @@ function slowToString(encoding, start, end) {
495498
}
496499
}
497500

501+
Buffer.prototype.copy = function(target, targetStart, sourceStart, sourceEnd) {
502+
return binding.copy(this, target, targetStart, sourceStart, sourceEnd);
503+
};
498504

499505
Buffer.prototype.toString = function() {
500506
let result;
@@ -510,8 +516,8 @@ Buffer.prototype.toString = function() {
510516

511517

512518
Buffer.prototype.equals = function equals(b) {
513-
if (!(b instanceof Buffer))
514-
throw new TypeError('Argument must be a Buffer');
519+
if (!isUint8Array(b))
520+
throw new TypeError('Argument must be a Buffer or Uint8Array');
515521

516522
if (this === b)
517523
return true;
@@ -539,8 +545,8 @@ Buffer.prototype.compare = function compare(target,
539545
thisStart,
540546
thisEnd) {
541547

542-
if (!(target instanceof Buffer))
543-
throw new TypeError('Argument must be a Buffer');
548+
if (!isUint8Array(target))
549+
throw new TypeError('Argument must be a Buffer or Uint8Array');
544550

545551
if (start === undefined)
546552
start = 0;
@@ -604,13 +610,14 @@ function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) {
604610
return binding.indexOfString(buffer, val, byteOffset, encoding, dir);
605611
}
606612
return slowIndexOf(buffer, val, byteOffset, encoding, dir);
607-
} else if (val instanceof Buffer) {
613+
} else if (isUint8Array(val)) {
608614
return binding.indexOfBuffer(buffer, val, byteOffset, encoding, dir);
609615
} else if (typeof val === 'number') {
610616
return binding.indexOfNumber(buffer, val, byteOffset, dir);
611617
}
612618

613-
throw new TypeError('"val" argument must be string, number or Buffer');
619+
throw new TypeError('"val" argument must be string, number, Buffer ' +
620+
'or Uint8Array');
614621
}
615622

616623

@@ -1037,8 +1044,8 @@ Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) {
10371044

10381045

10391046
function checkInt(buffer, value, offset, ext, max, min) {
1040-
if (!(buffer instanceof Buffer))
1041-
throw new TypeError('"buffer" argument must be a Buffer instance');
1047+
if (!isUint8Array(buffer))
1048+
throw new TypeError('"buffer" argument must be a Buffer or Uint8Array');
10421049
if (value > max || value < min)
10431050
throw new TypeError('"value" argument is out of bounds');
10441051
if (offset + ext > buffer.length)
Collapse file

‎lib/internal/buffer.js‎

Copy file name to clipboardExpand all lines: lib/internal/buffer.js
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@ const normalizeEncoding = require('internal/util').normalizeEncoding;
88
const Buffer = require('buffer').Buffer;
99

1010
const icu = process.binding('icu');
11+
const { isUint8Array } = process.binding('util');
1112

1213
// Transcodes the Buffer from one encoding to another, returning a new
1314
// Buffer instance.
1415
exports.transcode = function transcode(source, fromEncoding, toEncoding) {
15-
if (!Buffer.isBuffer(source))
16-
throw new TypeError('"source" argument must be a Buffer');
16+
if (!isUint8Array(source))
17+
throw new TypeError('"source" argument must be a Buffer or Uint8Array');
1718
if (source.length === 0) return Buffer.alloc(0);
1819

1920
fromEncoding = normalizeEncoding(fromEncoding) || fromEncoding;
2021
toEncoding = normalizeEncoding(toEncoding) || toEncoding;
2122
const result = icu.transcode(source, fromEncoding, toEncoding);
22-
if (Buffer.isBuffer(result))
23+
if (typeof result !== 'number')
2324
return result;
2425

2526
const code = icu.icuErrName(result);
Collapse file

‎src/node_buffer.cc‎

Copy file name to clipboardExpand all lines: src/node_buffer.cc
+9-9Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -519,23 +519,24 @@ void Base64Slice(const FunctionCallbackInfo<Value>& args) {
519519
}
520520

521521

522-
// bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]);
522+
// bytesCopied = copy(buffer, target[, targetStart][, sourceStart][, sourceEnd])
523523
void Copy(const FunctionCallbackInfo<Value> &args) {
524524
Environment* env = Environment::GetCurrent(args);
525525

526-
THROW_AND_RETURN_UNLESS_BUFFER(env, args.This());
527526
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
528-
Local<Object> target_obj = args[0].As<Object>();
529-
SPREAD_BUFFER_ARG(args.This(), ts_obj);
527+
THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]);
528+
Local<Object> buffer_obj = args[0].As<Object>();
529+
Local<Object> target_obj = args[1].As<Object>();
530+
SPREAD_BUFFER_ARG(buffer_obj, ts_obj);
530531
SPREAD_BUFFER_ARG(target_obj, target);
531532

532533
size_t target_start;
533534
size_t source_start;
534535
size_t source_end;
535536

536-
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[1], 0, &target_start));
537-
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], 0, &source_start));
538-
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[3], ts_obj_length, &source_end));
537+
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[2], 0, &target_start));
538+
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[3], 0, &source_start));
539+
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(args[4], ts_obj_length, &source_end));
539540

540541
// Copy 0 bytes; we're done
541542
if (target_start >= target_length || source_start >= source_end)
@@ -1203,8 +1204,6 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
12031204
env->SetMethod(proto, "ucs2Write", Ucs2Write);
12041205
env->SetMethod(proto, "utf8Write", Utf8Write);
12051206

1206-
env->SetMethod(proto, "copy", Copy);
1207-
12081207
if (auto zero_fill_field = env->isolate_data()->zero_fill_field()) {
12091208
CHECK(args[1]->IsObject());
12101209
auto binding_object = args[1].As<Object>();
@@ -1227,6 +1226,7 @@ void Initialize(Local<Object> target,
12271226
env->SetMethod(target, "createFromString", CreateFromString);
12281227

12291228
env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8);
1229+
env->SetMethod(target, "copy", Copy);
12301230
env->SetMethod(target, "compare", Compare);
12311231
env->SetMethod(target, "compareOffset", CompareOffset);
12321232
env->SetMethod(target, "fill", Fill);
Collapse file

‎src/node_util.cc‎

Copy file name to clipboardExpand all lines: src/node_util.cc
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ using v8::Value;
2929
V(isSet, IsSet) \
3030
V(isSetIterator, IsSetIterator) \
3131
V(isSharedArrayBuffer, IsSharedArrayBuffer) \
32-
V(isTypedArray, IsTypedArray)
32+
V(isTypedArray, IsTypedArray) \
33+
V(isUint8Array, IsUint8Array)
3334

3435

3536
#define V(_, ucname) \
Collapse file

‎test/parallel/test-buffer-compare.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-buffer-compare.js
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ const assert = require('assert');
66
const b = Buffer.alloc(1, 'a');
77
const c = Buffer.alloc(1, 'c');
88
const d = Buffer.alloc(2, 'aa');
9+
const e = new Uint8Array([ 0x61, 0x61 ]); // ASCII 'aa', same as d
910

1011
assert.strictEqual(b.compare(c), -1);
1112
assert.strictEqual(c.compare(d), 1);
1213
assert.strictEqual(d.compare(b), 1);
14+
assert.strictEqual(d.compare(e), 0);
1315
assert.strictEqual(b.compare(d), -1);
1416
assert.strictEqual(b.compare(b), 0);
1517

@@ -18,6 +20,9 @@ assert.strictEqual(Buffer.compare(c, d), 1);
1820
assert.strictEqual(Buffer.compare(d, b), 1);
1921
assert.strictEqual(Buffer.compare(b, d), -1);
2022
assert.strictEqual(Buffer.compare(c, c), 0);
23+
assert.strictEqual(Buffer.compare(e, e), 0);
24+
assert.strictEqual(Buffer.compare(d, e), 0);
25+
assert.strictEqual(Buffer.compare(d, b), 1);
2126

2227
assert.strictEqual(Buffer.compare(Buffer.alloc(0), Buffer.alloc(0)), 0);
2328
assert.strictEqual(Buffer.compare(Buffer.alloc(0), Buffer.alloc(1)), -1);
Collapse file

‎test/parallel/test-buffer-concat.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-buffer-concat.js
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ function assertWrongList(value) {
3535
Buffer.concat(value);
3636
}, function(err) {
3737
return err instanceof TypeError &&
38-
err.message === '"list" argument must be an Array of Buffers';
38+
err.message === '"list" argument must be an Array of Buffer ' +
39+
'or Uint8Array instances';
3940
});
4041
}
4142

@@ -60,3 +61,7 @@ assert.deepStrictEqual(Buffer.concat([empty], 4096), Buffer.alloc(4096));
6061
assert.deepStrictEqual(
6162
Buffer.concat([random10], 40),
6263
Buffer.concat([random10, Buffer.alloc(30)]));
64+
65+
assert.deepStrictEqual(Buffer.concat([new Uint8Array([0x41, 0x42]),
66+
new Uint8Array([0x43, 0x44])]),
67+
Buffer.from('ABCD'));
Collapse file

‎test/parallel/test-buffer-equals.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-buffer-equals.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ assert.ok(b.equals(c));
1212
assert.ok(!c.equals(d));
1313
assert.ok(!d.equals(e));
1414
assert.ok(d.equals(d));
15+
assert.ok(d.equals(new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65])));
1516

1617
assert.throws(() => Buffer.alloc(1).equals('abc'));
Collapse file

‎test/parallel/test-buffer-indexof.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-buffer-indexof.js
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,3 +524,11 @@ assert.equal(0, reallyLong.lastIndexOf(pattern));
524524
assert.strictEqual(buf.indexOf(0xff), -1);
525525
assert.strictEqual(buf.indexOf(0xffff), -1);
526526
}
527+
528+
// Test that Uint8Array arguments are okay.
529+
{
530+
const needle = new Uint8Array([ 0x66, 0x6f, 0x6f ]);
531+
const haystack = Buffer.from('a foo b foo');
532+
assert.strictEqual(haystack.indexOf(needle), 2);
533+
assert.strictEqual(haystack.lastIndexOf(needle), haystack.length - 3);
534+
}
Collapse file

‎test/parallel/test-icu-transcode.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-icu-transcode.js
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ for (const test in tests) {
4040

4141
assert.throws(
4242
() => buffer.transcode(null, 'utf8', 'ascii'),
43-
/^TypeError: "source" argument must be a Buffer$/
43+
/^TypeError: "source" argument must be a Buffer or Uint8Array$/
4444
);
4545

4646
assert.throws(
@@ -62,3 +62,11 @@ assert.deepStrictEqual(
6262
assert.deepStrictEqual(
6363
buffer.transcode(Buffer.from('hä', 'latin1'), 'latin1', 'utf16le'),
6464
Buffer.from('hä', 'utf16le'));
65+
66+
// Test that Uint8Array arguments are okay.
67+
{
68+
const uint8array = new Uint8Array([...Buffer.from('hä', 'latin1')]);
69+
assert.deepStrictEqual(
70+
buffer.transcode(uint8array, 'latin1', 'utf16le'),
71+
Buffer.from('hä', 'utf16le'));
72+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.