From f1135a5564fdbc5413e88271b5f2ed10c78a2371 Mon Sep 17 00:00:00 2001 From: George MacKerron Date: Thu, 12 Dec 2024 09:43:40 +0000 Subject: [PATCH 1/2] Updated hexSlice to user TextDecoder and TypedArrays for large speed gains --- .gitignore | 2 ++ index.js | 60 +++++++++++++++++++++++++++++++++----------- package.json | 2 +- perf/toString-hex.js | 21 ++++++++++++++++ 4 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 perf/toString-hex.js diff --git a/.gitignore b/.gitignore index 319db685..1acbc287 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .airtap.yml *.log node_modules/ +package-lock.json +perf/bundle.js diff --git a/index.js b/index.js index bdea6048..b5590365 100644 --- a/index.js +++ b/index.js @@ -1083,16 +1083,45 @@ function latin1Slice (buf, start, end) { } function hexSlice (buf, start, end) { - const len = buf.length + const buflen = buf.length if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len + if (!end || end < 0 || end > buflen) end = buflen - let out = '' - for (let i = start; i < end; ++i) { - out += hexSliceLookupTable[buf[i]] + buf = buf.subarray(start, end) // `slice` is deprecated + + // buffer needs to be 4-byte aligned for what follows + if (buf.byteOffset % 4 !== 0) buf = new Uint8Array(buf) + + const len = buf.length + const halfLen = len >>> 1 + const quarterLen = len >>> 2 + const out16 = new Uint16Array(len) + const buf32 = new Uint32Array(buf.buffer, buf.byteOffset, quarterLen) + const out32 = new Uint32Array(out16.buffer, out16.byteOffset, halfLen) + const lt = hexSliceLookupTable + + // read 4 bytes (1x uint32) and write 8 bytes (2x uint32) at a time + let i = 0 + let j = 0 + let v + if (littleEndian) while (i < quarterLen) { + v = buf32[i++] + out32[j++] = lt[(v >>> 8) & 255] << 16 | lt[v & 255] + out32[j++] = lt[v >>> 24] << 16 | lt[(v >>> 16) & 255] } - return out + else while (i < quarterLen) { + v = buf32[i++] + out32[j++] = lt[v >>> 24] << 16 | lt[(v >>> 16) & 255] + out32[j++] = lt[(v >>> 8) & 255] << 16 | lt[v & 255] + } + + // deal with up to 3 remaining bytes + i <<= 2; // uint32 addressing to uint8 addressing + while (i < len) out16[i] = lt[buf[i++]] + + const hex = textDecoder.decode(out16.subarray(0, len)) + return hex } function utf16leSlice (buf, start, end) { @@ -2091,16 +2120,17 @@ function numberIsNaN (obj) { return obj !== obj // eslint-disable-line no-self-compare } -// Create lookup table for `toString('hex')` -// See: https://github.com/feross/buffer/issues/219 +// Create lookup table etc. for `toString('hex')` +const textDecoder = new TextDecoder() +const littleEndian = new Uint8Array((new Uint16Array([0x0102]).buffer))[0] === 0x02 const hexSliceLookupTable = (function () { - const alphabet = '0123456789abcdef' - const table = new Array(256) - for (let i = 0; i < 16; ++i) { - const i16 = i * 16 - for (let j = 0; j < 16; ++j) { - table[i16 + j] = alphabet[i] + alphabet[j] - } + const alphabet = new TextEncoder().encode('0123456789abcdef') + const table = new Uint16Array(256) + if (littleEndian) for (let i = 0; i < 256; i++) { + table[i] = alphabet[i & 0xF] << 8 | alphabet[i >>> 4] + } + else for (let i = 0; i < 256; i++) { + table[i] = alphabet[i & 0xF] | alphabet[i >>> 4] << 8 } return table })() diff --git a/package.json b/package.json index 5ee6b8d9..ba699152 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 && node perf/write-hex.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/toString-hex.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/toString-hex.js b/perf/toString-hex.js new file mode 100644 index 00000000..033ae02b --- /dev/null +++ b/perf/toString-hex.js @@ -0,0 +1,21 @@ +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) + +for (let i = 0; i < LENGTH; i++) { + browserSubject[i] = nodeSubject[i] = (Math.random() * 255) << 0 +} + +suite + .add('BrowserBuffer#toString("hex")', function () { + browserSubject.toString('hex') + }) + +if (!process.browser) suite + .add('NodeBuffer#toString("hex")', function () { + nodeSubject.toString('hex') + }) From 68b24d1f8c1c25bd1a5e5e920dd5cd06ec348a09 Mon Sep 17 00:00:00 2001 From: George MacKerron Date: Thu, 12 Dec 2024 09:45:02 +0000 Subject: [PATCH 2/2] Updated AUTTHORS.md --- AUTHORS.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index 3f4918c7..2c4acbfa 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -69,6 +69,13 @@ - jkkang (jkkang@smartauth.kr) - Deklan Webster (deklanw@gmail.com) - Martin Heidegger (martin.heidegger@gmail.com) -- junderw (junderwood@bitcoinbank.co.jp) +- André Werlang (589286+awerlang@users.noreply.github.com) +- Bradley Odell (btodell@hotmail.com) +- Dominik Moritz (domoritz@gmail.com) +- Rachel Simone Weil (partytimehexcellent@gmail.com) +- Patrick McAndrew (urg@users.noreply.github.com) +- Jonathan Underwood (jonathan.underwood4649@gmail.com) +- Christopher Jeffrey (JJ) (chjjeffrey@gmail.com) +- George MacKerron (george@mackerron.co.uk) #### Generated by bin/update-authors.sh.