Skip to content

Commit 3ee4a1a

Browse files
mscdexaddaleax
authored andcommitted
buffer: optimize toString()
PR-URL: #12361 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 4a86803 commit 3ee4a1a

File tree

2 files changed

+98
-73
lines changed

2 files changed

+98
-73
lines changed

benchmark/buffers/buffer-tostring.js

+32-10
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,47 @@
33
const common = require('../common.js');
44

55
const bench = common.createBenchmark(main, {
6-
arg: ['true', 'false'],
6+
encoding: ['', 'utf8', 'ascii', 'latin1', 'binary', 'hex', 'UCS-2'],
7+
args: [0, 1, 2, 3],
78
len: [0, 1, 64, 1024],
89
n: [1e7]
910
});
1011

1112
function main(conf) {
12-
const arg = conf.arg === 'true';
13+
var encoding = conf.encoding;
14+
const args = conf.args | 0;
1315
const len = conf.len | 0;
1416
const n = conf.n | 0;
1517
const buf = Buffer.alloc(len, 42);
1618

19+
if (encoding.length === 0)
20+
encoding = undefined;
21+
1722
var i;
18-
bench.start();
19-
if (arg) {
20-
for (i = 0; i < n; i += 1)
21-
buf.toString('utf8');
22-
} else {
23-
for (i = 0; i < n; i += 1)
24-
buf.toString();
23+
switch (args) {
24+
case 1:
25+
bench.start();
26+
for (i = 0; i < n; i += 1)
27+
buf.toString(encoding);
28+
bench.end(n);
29+
break;
30+
case 2:
31+
bench.start();
32+
for (i = 0; i < n; i += 1)
33+
buf.toString(encoding, 0);
34+
bench.end(n);
35+
break;
36+
case 3:
37+
bench.start();
38+
for (i = 0; i < n; i += 1)
39+
buf.toString(encoding, 0, len);
40+
bench.end(n);
41+
break;
42+
default:
43+
bench.start();
44+
for (i = 0; i < n; i += 1)
45+
buf.toString();
46+
bench.end(n);
47+
break;
2548
}
26-
bench.end(n);
2749
}

lib/buffer.js

+66-63
Original file line numberDiff line numberDiff line change
@@ -483,82 +483,85 @@ Object.defineProperty(Buffer.prototype, 'offset', {
483483
});
484484

485485

486-
function slowToString(buf, encoding, start, end) {
487-
var loweredCase = false;
488-
489-
// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only
490-
// property of a typed array.
491-
492-
// This behaves neither like String nor Uint8Array in that we set start/end
493-
// to their upper/lower bounds if the value passed is out of range.
494-
// undefined is handled specially as per ECMA-262 6th Edition,
495-
// Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
496-
if (start === undefined || start < 0)
497-
start = 0;
498-
// Return early if start > buf.length. Done here to prevent potential uint32
499-
// coercion fail below.
500-
if (start > buf.length)
501-
return '';
502-
503-
if (end === undefined || end > buf.length)
504-
end = buf.length;
505-
506-
if (end <= 0)
507-
return '';
508-
509-
// Force coersion to uint32. This will also coerce falsey/NaN values to 0.
510-
end >>>= 0;
511-
start >>>= 0;
512-
513-
if (end <= start)
514-
return '';
515-
516-
if (encoding === undefined) encoding = 'utf8';
517-
518-
while (true) {
519-
switch (encoding) {
520-
case 'hex':
521-
return buf.hexSlice(start, end);
522-
523-
case 'utf8':
524-
case 'utf-8':
525-
return buf.utf8Slice(start, end);
526-
527-
case 'ascii':
528-
return buf.asciiSlice(start, end);
529-
530-
case 'latin1':
531-
case 'binary':
486+
function stringSlice(buf, encoding, start, end) {
487+
if (encoding === undefined) return buf.utf8Slice(start, end);
488+
encoding += '';
489+
switch (encoding.length) {
490+
case 4:
491+
if (encoding === 'utf8') return buf.utf8Slice(start, end);
492+
if (encoding === 'ucs2') return buf.ucs2Slice(start, end);
493+
encoding = encoding.toLowerCase();
494+
if (encoding === 'utf8') return buf.utf8Slice(start, end);
495+
if (encoding === 'ucs2') return buf.ucs2Slice(start, end);
496+
break;
497+
case 5:
498+
if (encoding === 'utf-8') return buf.utf8Slice(start, end);
499+
if (encoding === 'ascii') return buf.asciiSlice(start, end);
500+
if (encoding === 'ucs-2') return buf.ucs2Slice(start, end);
501+
encoding = encoding.toLowerCase();
502+
if (encoding === 'utf-8') return buf.utf8Slice(start, end);
503+
if (encoding === 'ascii') return buf.asciiSlice(start, end);
504+
if (encoding === 'ucs-2') return buf.ucs2Slice(start, end);
505+
break;
506+
case 6:
507+
if (encoding === 'latin1' || encoding === 'binary')
532508
return buf.latin1Slice(start, end);
533-
534-
case 'base64':
535-
return buf.base64Slice(start, end);
536-
537-
case 'ucs2':
538-
case 'ucs-2':
539-
case 'utf16le':
540-
case 'utf-16le':
509+
if (encoding === 'base64') return buf.base64Slice(start, end);
510+
encoding = encoding.toLowerCase();
511+
if (encoding === 'latin1' || encoding === 'binary')
512+
return buf.latin1Slice(start, end);
513+
if (encoding === 'base64') return buf.base64Slice(start, end);
514+
break;
515+
case 3:
516+
if (encoding === 'hex' || encoding.toLowerCase() === 'hex')
517+
return buf.hexSlice(start, end);
518+
break;
519+
case 7:
520+
if (encoding === 'utf16le' || encoding.toLowerCase() === 'utf16le')
541521
return buf.ucs2Slice(start, end);
542-
543-
default:
544-
if (loweredCase)
545-
throw new TypeError('Unknown encoding: ' + encoding);
546-
encoding = (encoding + '').toLowerCase();
547-
loweredCase = true;
548-
}
522+
break;
523+
case 8:
524+
if (encoding === 'utf-16le' || encoding.toLowerCase() === 'utf-16le')
525+
return buf.ucs2Slice(start, end);
526+
break;
549527
}
528+
throw new TypeError('Unknown encoding: ' + encoding);
550529
}
551530

531+
552532
Buffer.prototype.copy = function(target, targetStart, sourceStart, sourceEnd) {
553533
return binding.copy(this, target, targetStart, sourceStart, sourceEnd);
554534
};
555535

536+
// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only
537+
// property of a typed array.
538+
// This behaves neither like String nor Uint8Array in that we set start/end
539+
// to their upper/lower bounds if the value passed is out of range.
556540
Buffer.prototype.toString = function(encoding, start, end) {
557-
let result;
541+
var result;
558542
if (arguments.length === 0) {
559543
result = this.utf8Slice(0, this.length);
560544
} else {
561-
result = slowToString(this, encoding, start, end);
545+
const len = this.length;
546+
if (len === 0)
547+
return '';
548+
549+
if (!start || start < 0)
550+
start = 0;
551+
else if (start >= len)
552+
return '';
553+
554+
if (end === undefined || end > len)
555+
end = len;
556+
else if (end <= 0)
557+
return '';
558+
559+
start |= 0;
560+
end |= 0;
561+
562+
if (end <= start)
563+
return '';
564+
result = stringSlice(this, encoding, start, end);
562565
}
563566
if (result === undefined)
564567
throw new Error('"toString()" failed');

0 commit comments

Comments
 (0)