buffer: optimize toString()

PR-URL: https://github.com/nodejs/node/pull/12361
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
Brian White 2017-04-10 05:05:58 -04:00 committed by Anna Henningsen
parent 4a86803f60
commit 3ee4a1a281
No known key found for this signature in database
GPG Key ID: D8B9F5AEAE84E4CF
2 changed files with 98 additions and 73 deletions

View File

@ -3,25 +3,47 @@
const common = require('../common.js'); const common = require('../common.js');
const bench = common.createBenchmark(main, { const bench = common.createBenchmark(main, {
arg: ['true', 'false'], encoding: ['', 'utf8', 'ascii', 'latin1', 'binary', 'hex', 'UCS-2'],
args: [0, 1, 2, 3],
len: [0, 1, 64, 1024], len: [0, 1, 64, 1024],
n: [1e7] n: [1e7]
}); });
function main(conf) { function main(conf) {
const arg = conf.arg === 'true'; var encoding = conf.encoding;
const args = conf.args | 0;
const len = conf.len | 0; const len = conf.len | 0;
const n = conf.n | 0; const n = conf.n | 0;
const buf = Buffer.alloc(len, 42); const buf = Buffer.alloc(len, 42);
if (encoding.length === 0)
encoding = undefined;
var i; var i;
bench.start(); switch (args) {
if (arg) { case 1:
for (i = 0; i < n; i += 1) bench.start();
buf.toString('utf8'); for (i = 0; i < n; i += 1)
} else { buf.toString(encoding);
for (i = 0; i < n; i += 1) bench.end(n);
buf.toString(); break;
case 2:
bench.start();
for (i = 0; i < n; i += 1)
buf.toString(encoding, 0);
bench.end(n);
break;
case 3:
bench.start();
for (i = 0; i < n; i += 1)
buf.toString(encoding, 0, len);
bench.end(n);
break;
default:
bench.start();
for (i = 0; i < n; i += 1)
buf.toString();
bench.end(n);
break;
} }
bench.end(n);
} }

View File

@ -483,82 +483,85 @@ Object.defineProperty(Buffer.prototype, 'offset', {
}); });
function slowToString(buf, encoding, start, end) { function stringSlice(buf, encoding, start, end) {
var loweredCase = false; if (encoding === undefined) return buf.utf8Slice(start, end);
encoding += '';
// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only switch (encoding.length) {
// property of a typed array. case 4:
if (encoding === 'utf8') return buf.utf8Slice(start, end);
// This behaves neither like String nor Uint8Array in that we set start/end if (encoding === 'ucs2') return buf.ucs2Slice(start, end);
// to their upper/lower bounds if the value passed is out of range. encoding = encoding.toLowerCase();
// undefined is handled specially as per ECMA-262 6th Edition, if (encoding === 'utf8') return buf.utf8Slice(start, end);
// Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. if (encoding === 'ucs2') return buf.ucs2Slice(start, end);
if (start === undefined || start < 0) break;
start = 0; case 5:
// Return early if start > buf.length. Done here to prevent potential uint32 if (encoding === 'utf-8') return buf.utf8Slice(start, end);
// coercion fail below. if (encoding === 'ascii') return buf.asciiSlice(start, end);
if (start > buf.length) if (encoding === 'ucs-2') return buf.ucs2Slice(start, end);
return ''; encoding = encoding.toLowerCase();
if (encoding === 'utf-8') return buf.utf8Slice(start, end);
if (end === undefined || end > buf.length) if (encoding === 'ascii') return buf.asciiSlice(start, end);
end = buf.length; if (encoding === 'ucs-2') return buf.ucs2Slice(start, end);
break;
if (end <= 0) case 6:
return ''; if (encoding === 'latin1' || encoding === 'binary')
// Force coersion to uint32. This will also coerce falsey/NaN values to 0.
end >>>= 0;
start >>>= 0;
if (end <= start)
return '';
if (encoding === undefined) encoding = 'utf8';
while (true) {
switch (encoding) {
case 'hex':
return buf.hexSlice(start, end);
case 'utf8':
case 'utf-8':
return buf.utf8Slice(start, end);
case 'ascii':
return buf.asciiSlice(start, end);
case 'latin1':
case 'binary':
return buf.latin1Slice(start, end); return buf.latin1Slice(start, end);
if (encoding === 'base64') return buf.base64Slice(start, end);
case 'base64': encoding = encoding.toLowerCase();
return buf.base64Slice(start, end); if (encoding === 'latin1' || encoding === 'binary')
return buf.latin1Slice(start, end);
case 'ucs2': if (encoding === 'base64') return buf.base64Slice(start, end);
case 'ucs-2': break;
case 'utf16le': case 3:
case 'utf-16le': if (encoding === 'hex' || encoding.toLowerCase() === 'hex')
return buf.hexSlice(start, end);
break;
case 7:
if (encoding === 'utf16le' || encoding.toLowerCase() === 'utf16le')
return buf.ucs2Slice(start, end); return buf.ucs2Slice(start, end);
break;
default: case 8:
if (loweredCase) if (encoding === 'utf-16le' || encoding.toLowerCase() === 'utf-16le')
throw new TypeError('Unknown encoding: ' + encoding); return buf.ucs2Slice(start, end);
encoding = (encoding + '').toLowerCase(); break;
loweredCase = true;
}
} }
throw new TypeError('Unknown encoding: ' + encoding);
} }
Buffer.prototype.copy = function(target, targetStart, sourceStart, sourceEnd) { Buffer.prototype.copy = function(target, targetStart, sourceStart, sourceEnd) {
return binding.copy(this, target, targetStart, sourceStart, sourceEnd); return binding.copy(this, target, targetStart, sourceStart, sourceEnd);
}; };
// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only
// property of a typed array.
// This behaves neither like String nor Uint8Array in that we set start/end
// to their upper/lower bounds if the value passed is out of range.
Buffer.prototype.toString = function(encoding, start, end) { Buffer.prototype.toString = function(encoding, start, end) {
let result; var result;
if (arguments.length === 0) { if (arguments.length === 0) {
result = this.utf8Slice(0, this.length); result = this.utf8Slice(0, this.length);
} else { } else {
result = slowToString(this, encoding, start, end); const len = this.length;
if (len === 0)
return '';
if (!start || start < 0)
start = 0;
else if (start >= len)
return '';
if (end === undefined || end > len)
end = len;
else if (end <= 0)
return '';
start |= 0;
end |= 0;
if (end <= start)
return '';
result = stringSlice(this, encoding, start, end);
} }
if (result === undefined) if (result === undefined)
throw new Error('"toString()" failed'); throw new Error('"toString()" failed');