From ec74bf7f74745396f0765902f4a64ce9dd1c3b80 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:54:18 +0900 Subject: [PATCH] perf: improve performance of isomorphicEncode (#3101) * perf: improve performance of isomorphicEncode * minor change * chore: remove isomorphic-encode.mjs * create isomorphic-encode.mjs * apply suggestions from code review * apply suggestions from code review --- benchmarks/fetch/isomorphic-encode.mjs | 75 ++++++++++++++++++++++++++ lib/web/fetch/util.js | 6 +-- 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 benchmarks/fetch/isomorphic-encode.mjs diff --git a/benchmarks/fetch/isomorphic-encode.mjs b/benchmarks/fetch/isomorphic-encode.mjs new file mode 100644 index 00000000000..bf772390bba --- /dev/null +++ b/benchmarks/fetch/isomorphic-encode.mjs @@ -0,0 +1,75 @@ +import { bench, group, run } from 'mitata' +import { isomorphicEncode } from '../../lib/web/fetch/util.js' + +const characters = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' +const charactersLength = characters.length + +function generateAsciiString (length) { + let result = '' + for (let i = 0; i < length; ++i) { + result += characters[Math.floor(Math.random() * charactersLength)] + } + return result +} + +const invalidIsomorphicEncodeValueRegex = /[^\x00-\xFF]/ // eslint-disable-line + +function isomorphicEncode1 (input) { + for (let i = 0; i < input.length; i++) { + if (input.charCodeAt(i) > 0xff) { + throw new TypeError('Unreachable') + } + } + return input +} + +function isomorphicEncode2 (input) { + if (invalidIsomorphicEncodeValueRegex.test(input)) { + throw new TypeError('Unreachable') + } + return input +} + +const settings = { + small: 10, + middle: 30, + long: 70 +} + +for (const [runName, length] of Object.entries(settings)) { + const value = generateAsciiString(length); + + [ + { name: `${runName} (valid)`, value }, + { + name: `${runName} (invalid)`, + value: `${value.slice(0, -1)}${String.fromCharCode(0xff + 1)}` + } + ].forEach(({ name, value }) => { + group(name, () => { + [ + { + name: 'original', + fn: isomorphicEncode + }, + { + name: 'String#charCodeAt', + fn: isomorphicEncode1 + }, + { + name: 'RegExp#test', + fn: isomorphicEncode2 + } + ].forEach(({ name, fn }) => { + bench(name, () => { + try { + return fn(value) + } catch (err) {} + }) + }) + }) + }) +} + +await run() diff --git a/lib/web/fetch/util.js b/lib/web/fetch/util.js index 7dd56c1d5c1..8c73f909604 100644 --- a/lib/web/fetch/util.js +++ b/lib/web/fetch/util.js @@ -1098,15 +1098,15 @@ function readableStreamClose (controller) { } } +const invalidIsomorphicEncodeValueRegex = /[^\x00-\xFF]/ // eslint-disable-line + /** * @see https://infra.spec.whatwg.org/#isomorphic-encode * @param {string} input */ function isomorphicEncode (input) { // 1. Assert: input contains no code points greater than U+00FF. - for (let i = 0; i < input.length; i++) { - assert(input.charCodeAt(i) <= 0xFF) - } + assert(!invalidIsomorphicEncodeValueRegex.test(input)) // 2. Return a byte sequence whose length is equal to input’s code // point length and whose bytes have the same values as the