From 1c4c1f873ca6d3a53a0baad3321570c4c2070a81 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Wed, 26 Jul 2023 01:07:53 +0200 Subject: [PATCH] improve perf of asString --- benchmark/bench.js | 19 +++++++++++++++++++ lib/serializer.js | 35 +++++++++++++++++------------------ test/basic.test.js | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/benchmark/bench.js b/benchmark/bench.js index 5a7fc56d..a84da705 100644 --- a/benchmark/bench.js +++ b/benchmark/bench.js @@ -18,6 +18,11 @@ function getRandomInt (max) { return Math.floor(Math.random() * max) } +let longSimpleString = '' +for (let i = 0; i < LONG_STRING_LENGTH; i++) { + longSimpleString += i +} + let longString = '' for (let i = 0; i < LONG_STRING_LENGTH; i++) { longString += i @@ -42,6 +47,20 @@ const benchmarks = [ }, input: 'hello world' }, + { + name: 'short string with double quote', + schema: { + type: 'string' + }, + input: 'hello " world' + }, + { + name: 'long string without double quotes', + schema: { + type: 'string' + }, + input: longSimpleString + }, { name: 'long string', schema: { diff --git a/lib/serializer.js b/lib/serializer.js index b3606e1a..922be357 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -113,11 +113,10 @@ module.exports = class Serializer { } } - // Fast escape chars check - if (!STR_ESCAPE.test(str)) { - return '"' + str + '"' - } else if (str.length < 42) { + if (str.length < 42) { return this.asStringSmall(str) + } else if (STR_ESCAPE.test(str) === false) { + return '"' + str + '"' } else { return JSON.stringify(str) } @@ -130,32 +129,32 @@ module.exports = class Serializer { // 34 and 92 happens all the time, so we // have a fast case for them asStringSmall (str) { - const l = str.length + const len = str.length let result = '' - let last = 0 - let found = false - let surrogateFound = false + let last = -1 let point = 255 + // eslint-disable-next-line - for (var i = 0; i < l && point >= 32; i++) { + for (var i = 0; i < len; i++) { point = str.charCodeAt(i) + if (point < 32) { + return JSON.stringify(str) + } if (point >= 0xD800 && point <= 0xDFFF) { // The current character is a surrogate. - surrogateFound = true + return JSON.stringify(str) } - if (point === 34 || point === 92) { + if ( + point === 0x22 || // '"' + point === 0x5c // '\' + ) { + last === -1 && (last = 0) result += str.slice(last, i) + '\\' last = i - found = true } } - if (!found) { - result = str - } else { - result += str.slice(last) - } - return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"' + return (last === -1 && ('"' + str + '"')) || ('"' + result + str.slice(last) + '"') } getState () { diff --git a/test/basic.test.js b/test/basic.test.js index 1c8f370c..bd47ac7d 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -344,7 +344,7 @@ test('patternProperties - throw on unknown type', (t) => { } }) -test('render a single quote as JSON', (t) => { +test('render a double quote as JSON /1', (t) => { t.plan(2) const schema = { @@ -360,6 +360,38 @@ test('render a single quote as JSON', (t) => { t.ok(validate(JSON.parse(output)), 'valid schema') }) +test('render a double quote as JSON /2', (t) => { + t.plan(2) + + const schema = { + type: 'string' + } + const toStringify = 'double quote " 2' + + const validate = validator(schema) + const stringify = build(schema) + const output = stringify(toStringify) + + t.equal(output, JSON.stringify(toStringify)) + t.ok(validate(JSON.parse(output)), 'valid schema') +}) + +test('render a long string', (t) => { + t.plan(2) + + const schema = { + type: 'string' + } + const toStringify = 'the Ultimate Question of Life, the Universe, and Everything.' + + const validate = validator(schema) + const stringify = build(schema) + const output = stringify(toStringify) + + t.equal(output, JSON.stringify(toStringify)) + t.ok(validate(JSON.parse(output)), 'valid schema') +}) + test('returns JSON.stringify if schema type is boolean', t => { t.plan(1)