diff --git a/benchmark/buffers/buffer-tostring.js b/benchmark/buffers/buffer-tostring.js index 99ae0ec077957e..49916fca4023ca 100644 --- a/benchmark/buffers/buffer-tostring.js +++ b/benchmark/buffers/buffer-tostring.js @@ -3,25 +3,47 @@ const common = require('../common.js'); 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], n: [1e7] }); function main(conf) { - const arg = conf.arg === 'true'; + var encoding = conf.encoding; + const args = conf.args | 0; const len = conf.len | 0; const n = conf.n | 0; const buf = Buffer.alloc(len, 42); + if (encoding.length === 0) + encoding = undefined; + var i; - bench.start(); - if (arg) { - for (i = 0; i < n; i += 1) - buf.toString('utf8'); - } else { - for (i = 0; i < n; i += 1) - buf.toString(); + switch (args) { + case 1: + bench.start(); + for (i = 0; i < n; i += 1) + buf.toString(encoding); + bench.end(n); + 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); } diff --git a/lib/buffer.js b/lib/buffer.js index 0469f4147ec986..1e3478909bd38c 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -483,82 +483,85 @@ Object.defineProperty(Buffer.prototype, 'offset', { }); -function slowToString(buf, encoding, start, end) { - var loweredCase = false; - - // 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. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) - start = 0; - // Return early if start > buf.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > buf.length) - return ''; - - if (end === undefined || end > buf.length) - end = buf.length; - - if (end <= 0) - return ''; - - // 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': +function stringSlice(buf, encoding, start, end) { + if (encoding === undefined) return buf.utf8Slice(start, end); + encoding += ''; + switch (encoding.length) { + case 4: + if (encoding === 'utf8') return buf.utf8Slice(start, end); + if (encoding === 'ucs2') return buf.ucs2Slice(start, end); + encoding = encoding.toLowerCase(); + if (encoding === 'utf8') return buf.utf8Slice(start, end); + if (encoding === 'ucs2') return buf.ucs2Slice(start, end); + break; + case 5: + if (encoding === 'utf-8') return buf.utf8Slice(start, end); + if (encoding === 'ascii') return buf.asciiSlice(start, end); + if (encoding === 'ucs-2') return buf.ucs2Slice(start, end); + encoding = encoding.toLowerCase(); + if (encoding === 'utf-8') return buf.utf8Slice(start, end); + if (encoding === 'ascii') return buf.asciiSlice(start, end); + if (encoding === 'ucs-2') return buf.ucs2Slice(start, end); + break; + case 6: + if (encoding === 'latin1' || encoding === 'binary') return buf.latin1Slice(start, end); - - case 'base64': - return buf.base64Slice(start, end); - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': + if (encoding === 'base64') return buf.base64Slice(start, end); + encoding = encoding.toLowerCase(); + if (encoding === 'latin1' || encoding === 'binary') + return buf.latin1Slice(start, end); + if (encoding === 'base64') return buf.base64Slice(start, end); + break; + case 3: + 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); - - default: - if (loweredCase) - throw new TypeError('Unknown encoding: ' + encoding); - encoding = (encoding + '').toLowerCase(); - loweredCase = true; - } + break; + case 8: + if (encoding === 'utf-16le' || encoding.toLowerCase() === 'utf-16le') + return buf.ucs2Slice(start, end); + break; } + throw new TypeError('Unknown encoding: ' + encoding); } + Buffer.prototype.copy = function(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) { - let result; + var result; if (arguments.length === 0) { result = this.utf8Slice(0, this.length); } 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) throw new Error('"toString()" failed');