Skip to content

Commit dbc5143

Browse files
committed
perf: improve performance to escape big strings
The time to escape strings that contained characters that needed escaping is now lower than before. Native JSON.stringify() got a lot faster in newer versions and may now be used directly. This has to side effect to also reduce the code size a bit.
1 parent 5c078f2 commit dbc5143

File tree

2 files changed

+11
-62
lines changed

2 files changed

+11
-62
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## v2.4.3
44

55
- Fixed replacer function receiving array keys as number instead of string
6+
- Improved performance to escape long strings that contain characters that need escaping
67

78
## v2.4.2
89

index.js

Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -21,67 +21,15 @@ module.exports = stringify
2121

2222
// eslint-disable-next-line no-control-regex
2323
const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/
24-
const strEscapeSequencesReplacer = new RegExp(strEscapeSequencesRegExp, 'g')
25-
26-
// Escaped special characters. Use empty strings to fill up unused entries.
27-
const meta = [
28-
'\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004',
29-
'\\u0005', '\\u0006', '\\u0007', '\\b', '\\t',
30-
'\\n', '\\u000b', '\\f', '\\r', '\\u000e',
31-
'\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013',
32-
'\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018',
33-
'\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d',
34-
'\\u001e', '\\u001f', '', '', '\\"',
35-
'', '', '', '', '', '', '', '', '', '',
36-
'', '', '', '', '', '', '', '', '', '',
37-
'', '', '', '', '', '', '', '', '', '',
38-
'', '', '', '', '', '', '', '', '', '',
39-
'', '', '', '', '', '', '', '', '', '',
40-
'', '', '', '', '', '', '', '\\\\'
41-
]
42-
43-
function escapeFn (str) {
44-
if (str.length === 2) {
45-
const charCode = str.charCodeAt(1)
46-
return `${str[0]}\\u${charCode.toString(16)}`
47-
}
48-
const charCode = str.charCodeAt(0)
49-
return meta.length > charCode
50-
? meta[charCode]
51-
: `\\u${charCode.toString(16)}`
52-
}
5324

5425
// Escape C0 control characters, double quotes, the backslash and every code
5526
// unit with a numeric value in the inclusive range 0xD800 to 0xDFFF.
5627
function strEscape (str) {
5728
// Some magic numbers that worked out fine while benchmarking with v8 8.0
5829
if (str.length < 5000 && !strEscapeSequencesRegExp.test(str)) {
59-
return str
60-
}
61-
if (str.length > 100) {
62-
return str.replace(strEscapeSequencesReplacer, escapeFn)
63-
}
64-
let result = ''
65-
let last = 0
66-
for (let i = 0; i < str.length; i++) {
67-
const point = str.charCodeAt(i)
68-
if (point === 34 || point === 92 || point < 32) {
69-
result += `${str.slice(last, i)}${meta[point]}`
70-
last = i + 1
71-
} else if (point >= 0xd800 && point <= 0xdfff) {
72-
if (point <= 0xdbff && i + 1 < str.length) {
73-
const nextPoint = str.charCodeAt(i + 1)
74-
if (nextPoint >= 0xdc00 && nextPoint <= 0xdfff) {
75-
i++
76-
continue
77-
}
78-
}
79-
result += `${str.slice(last, i)}\\u${point.toString(16)}`
80-
last = i + 1
81-
}
30+
return `"${str}"`
8231
}
83-
result += str.slice(last)
84-
return result
32+
return JSON.stringify(str)
8533
}
8634

8735
function insertSort (array) {
@@ -237,7 +185,7 @@ function configure (options) {
237185

238186
switch (typeof value) {
239187
case 'string':
240-
return `"${strEscape(value)}"`
188+
return strEscape(value)
241189
case 'object': {
242190
if (value === null) {
243191
return 'null'
@@ -313,7 +261,7 @@ function configure (options) {
313261
const key = keys[i]
314262
const tmp = stringifyFnReplacer(key, value, stack, replacer, spacer, indentation)
315263
if (tmp !== undefined) {
316-
res += `${separator}"${strEscape(key)}":${whitespace}${tmp}`
264+
res += `${separator}${strEscape(key)}:${whitespace}${tmp}`
317265
separator = join
318266
}
319267
}
@@ -351,7 +299,7 @@ function configure (options) {
351299

352300
switch (typeof value) {
353301
case 'string':
354-
return `"${strEscape(value)}"`
302+
return strEscape(value)
355303
case 'object': {
356304
if (value === null) {
357305
return 'null'
@@ -407,7 +355,7 @@ function configure (options) {
407355
for (const key of replacer) {
408356
const tmp = stringifyArrayReplacer(key, value[key], stack, replacer, spacer, indentation)
409357
if (tmp !== undefined) {
410-
res += `${separator}"${strEscape(key)}":${whitespace}${tmp}`
358+
res += `${separator}${strEscape(key)}:${whitespace}${tmp}`
411359
separator = join
412360
}
413361
}
@@ -436,7 +384,7 @@ function configure (options) {
436384
function stringifyIndent (key, value, stack, spacer, indentation) {
437385
switch (typeof value) {
438386
case 'string':
439-
return `"${strEscape(value)}"`
387+
return strEscape(value)
440388
case 'object': {
441389
if (value === null) {
442390
return 'null'
@@ -512,7 +460,7 @@ function configure (options) {
512460
const key = keys[i]
513461
const tmp = stringifyIndent(key, value[key], stack, spacer, indentation)
514462
if (tmp !== undefined) {
515-
res += `${separator}"${strEscape(key)}": ${tmp}`
463+
res += `${separator}${strEscape(key)}: ${tmp}`
516464
separator = join
517465
}
518466
}
@@ -546,7 +494,7 @@ function configure (options) {
546494
function stringifySimple (key, value, stack) {
547495
switch (typeof value) {
548496
case 'string':
549-
return `"${strEscape(value)}"`
497+
return strEscape(value)
550498
case 'object': {
551499
if (value === null) {
552500
return 'null'
@@ -616,7 +564,7 @@ function configure (options) {
616564
const key = keys[i]
617565
const tmp = stringifySimple(key, value[key], stack)
618566
if (tmp !== undefined) {
619-
res += `${separator}"${strEscape(key)}":${tmp}`
567+
res += `${separator}${strEscape(key)}:${tmp}`
620568
separator = ','
621569
}
622570
}

0 commit comments

Comments
 (0)