From d513509fa437afe8d3ee115ab4f6eb22aa0d5d45 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 23 Jan 2024 12:43:37 -0500 Subject: [PATCH 1/7] Optimize hexWrite with an array lookup table --- index.js | 65 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/index.js b/index.js index dad9152..7c00394 100644 --- a/index.js +++ b/index.js @@ -855,19 +855,24 @@ function hexWrite (buf, string, offset, length) { const strLen = string.length - if (length > strLen / 2) { - length = strLen / 2 + if (length > (strLen >>> 1)) { + length = strLen >>> 1 } - let i - for (i = 0; i < length; ++i) { - const a = hexCharValueTable[string[i * 2]] - const b = hexCharValueTable[string[i * 2 + 1]] - if (a === undefined || b === undefined) { + + for (let i = 0; i < length; ++i) { + const a = string.charCodeAt(i * 2 + 0) + const b = string.charCodeAt(i * 2 + 1) + const hi = hexCharValueTable[a & 0x7f] + const lo = hexCharValueTable[b & 0x7f] + + if ((a | b | hi | lo) & ~0x7f) { return i } - buf[offset + i] = a << 4 | b + + buf[offset + i] = (hi << 4) | lo } - return i + + return length } function utf8Write (buf, string, offset, length) { @@ -2118,30 +2123,24 @@ const hexSliceLookupTable = (function () { })() // hex lookup table for Buffer.from(x, 'hex') -const hexCharValueTable = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - a: 10, - b: 11, - c: 12, - d: 13, - e: 14, - f: 15, - A: 10, - B: 11, - C: 12, - D: 13, - E: 14, - F: 15 -} +const hexCharValueTable = [ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +] // Return not function with Error if BigInt not supported function defineBigIntMethod (fn) { From 3288d7d2eae74c02bf7851b4efb1b8e65c9d28eb Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 23 Jan 2024 13:37:09 -0500 Subject: [PATCH 2/7] Add a benchmark for hexWrite --- package.json | 2 +- perf/write-hex.js | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 perf/write-hex.js diff --git a/package.json b/package.json index 536fe46..5ee6b8d 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ }, "scripts": { "perf": "browserify --debug perf/bracket-notation.js > perf/bundle.js && open perf/index.html", - "perf-node": "node perf/bracket-notation.js && node perf/concat.js && node perf/copy-big.js && node perf/copy.js && node perf/new-big.js && node perf/new.js && node perf/readDoubleBE.js && node perf/readFloatBE.js && node perf/readUInt32LE.js && node perf/slice.js && node perf/writeFloatBE.js", + "perf-node": "node perf/bracket-notation.js && node perf/concat.js && node perf/copy-big.js && node perf/copy.js && node perf/new-big.js && node perf/new.js && node perf/readDoubleBE.js && node perf/readFloatBE.js && node perf/readUInt32LE.js && node perf/slice.js && node perf/writeFloatBE.js && node perf/write-hex.js", "size": "browserify -r ./ | uglifyjs -c -m | gzip | wc -c", "standard": "standard", "test": "tape test/*.js test/node/*.js", diff --git a/perf/write-hex.js b/perf/write-hex.js new file mode 100644 index 0000000..76b03be --- /dev/null +++ b/perf/write-hex.js @@ -0,0 +1,24 @@ +const BrowserBuffer = require('../').Buffer // (this module) +const util = require('./util') +const suite = util.suite() + +const LENGTH = 4096 +const browserSubject = BrowserBuffer.alloc(LENGTH) +const nodeSubject = Buffer.alloc(LENGTH) + +const charset = '0123456789abcdef' + +let str = '' + +for (let i = 0; i < LENGTH * 2; i++) + str += charset[Math.random() * charset.length | 0] + +suite + .add('BrowserBuffer#write(' + LENGTH + ', "hex")', function () { + browserSubject.write(str, 'hex') + }) + +if (!process.browser) suite + .add('NodeBuffer#write(' + LENGTH + ', "hex")', function () { + nodeSubject.write(str, 'hex') + }) From e320c87c637adca486f2237630d459753449fa0b Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 31 Jan 2024 14:33:39 -0500 Subject: [PATCH 3/7] Ignore linting errors for hex table --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index 7c00394..b0b5129 100644 --- a/index.js +++ b/index.js @@ -2123,6 +2123,7 @@ const hexSliceLookupTable = (function () { })() // hex lookup table for Buffer.from(x, 'hex') +/* eslint-disable no-multi-spaces, indent */ const hexCharValueTable = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -2141,6 +2142,7 @@ const hexCharValueTable = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ] +/* eslint-enable no-multi-spaces, indent */ // Return not function with Error if BigInt not supported function defineBigIntMethod (fn) { From 02c5846e457794d62c473ab9bc717e2053807308 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 31 Jan 2024 18:46:06 -0500 Subject: [PATCH 4/7] Add hexWrite test for invalid characters/lengths --- test/write-hex.js | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/write-hex.js diff --git a/test/write-hex.js b/test/write-hex.js new file mode 100644 index 0000000..c35e61f --- /dev/null +++ b/test/write-hex.js @@ -0,0 +1,57 @@ +'use strict' + +const Buffer = require('../').Buffer +const test = require('tape') + +test('buffer.write("hex") should stop on invalid characters', function (t) { + const buf = Buffer.alloc(4) + + // Test the entire 16-bit space. + for (let ch = 0; ch <= 0xffff; ch++) { + // 0-9 + if (ch >= 0x30 && ch <= 0x39) { + continue + } + + // A-F + if (ch >= 0x41 && ch <= 0x46) { + continue + } + + // a-f + if (ch >= 0x61 && ch <= 0x66) { + continue + } + + const str = 'abcd' + String.fromCharCode(ch) + 'ef0' + + buf.fill(0) + + t.equal(str.length, 8) + t.equal(buf.write(str, 'hex'), 2) + t.equal(buf.toString('hex'), 'abcd0000') + t.equal(Buffer.from(str, 'hex').toString('hex'), 'abcd') + } + + t.end() +}) + +test('buffer.write("hex") should truncate odd string lengths', function (t) { + const buf = Buffer.alloc(32) + const charset = '0123456789abcdef' + + let str = '' + + for (let i = 0; i < 63; i++) { + str += charset[Math.random() * charset.length | 0] + } + + t.equal(buf.write('abcde', 'hex'), 2) + t.equal(buf.toString('hex', 0, 3), 'abcd00') + + buf.fill(0) + + t.equal(buf.write(str, 'hex'), 31) + t.equal(buf.toString('hex', 0, 32), str.slice(0, -1) + '00') + t.end() +}) From c30efe6d5c1223e3f8abd0938c532e7909aeabb2 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 31 Jan 2024 19:06:58 -0500 Subject: [PATCH 5/7] Add more cases to hexWrite test --- test/write-hex.js | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/test/write-hex.js b/test/write-hex.js index c35e61f..7b4842b 100644 --- a/test/write-hex.js +++ b/test/write-hex.js @@ -23,14 +23,33 @@ test('buffer.write("hex") should stop on invalid characters', function (t) { continue } - const str = 'abcd' + String.fromCharCode(ch) + 'ef0' + for (let i = 0; i < 3; i++) { + let str - buf.fill(0) + switch (i) { + case 0: + str = 'abcd' + String.fromCharCode(ch) + 'ef0' + break + case 1: + str = 'abcde' + String.fromCharCode(ch) + 'f0' + break + case 2: + str = 'abcd' + String.fromCharCode(ch + 0) + + String.fromCharCode(ch + 1) + 'f0' + break + case 3: + str = 'abcde' + String.fromCharCode(ch + 0) + + String.fromCharCode(ch + 1) + '0' + break + } - t.equal(str.length, 8) - t.equal(buf.write(str, 'hex'), 2) - t.equal(buf.toString('hex'), 'abcd0000') - t.equal(Buffer.from(str, 'hex').toString('hex'), 'abcd') + buf.fill(0) + + t.equal(str.length, 8) + t.equal(buf.write(str, 'hex'), 2) + t.equal(buf.toString('hex'), 'abcd0000') + t.equal(Buffer.from(str, 'hex').toString('hex'), 'abcd') + } } t.end() From 8c8ed25d28c3c7f9819bd9956916ff8301b1898b Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 31 Jan 2024 19:10:06 -0500 Subject: [PATCH 6/7] Another fix for hexWrite test --- test/write-hex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/write-hex.js b/test/write-hex.js index 7b4842b..9848ee1 100644 --- a/test/write-hex.js +++ b/test/write-hex.js @@ -23,7 +23,7 @@ test('buffer.write("hex") should stop on invalid characters', function (t) { continue } - for (let i = 0; i < 3; i++) { + for (let i = 0; i < 4; i++) { let str switch (i) { From 23bf9d15834e92736a3bce5f83cbfe44adabe491 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:53:01 +1100 Subject: [PATCH 7/7] prefer for of loop --- test/write-hex.js | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/test/write-hex.js b/test/write-hex.js index 9848ee1..c10ac2b 100644 --- a/test/write-hex.js +++ b/test/write-hex.js @@ -4,8 +4,6 @@ const Buffer = require('../').Buffer const test = require('tape') test('buffer.write("hex") should stop on invalid characters', function (t) { - const buf = Buffer.alloc(4) - // Test the entire 16-bit space. for (let ch = 0; ch <= 0xffff; ch++) { // 0-9 @@ -23,28 +21,13 @@ test('buffer.write("hex") should stop on invalid characters', function (t) { continue } - for (let i = 0; i < 4; i++) { - let str - - switch (i) { - case 0: - str = 'abcd' + String.fromCharCode(ch) + 'ef0' - break - case 1: - str = 'abcde' + String.fromCharCode(ch) + 'f0' - break - case 2: - str = 'abcd' + String.fromCharCode(ch + 0) + - String.fromCharCode(ch + 1) + 'f0' - break - case 3: - str = 'abcde' + String.fromCharCode(ch + 0) + - String.fromCharCode(ch + 1) + '0' - break - } - - buf.fill(0) - + for (const str of [ + 'abcd' + String.fromCharCode(ch) + 'ef0', + 'abcde' + String.fromCharCode(ch) + 'f0', + 'abcd' + String.fromCharCode(ch + 0) + String.fromCharCode(ch + 1) + 'f0', + 'abcde' + String.fromCharCode(ch + 0) + String.fromCharCode(ch + 1) + '0' + ]) { + const buf = Buffer.alloc(4) t.equal(str.length, 8) t.equal(buf.write(str, 'hex'), 2) t.equal(buf.toString('hex'), 'abcd0000')