diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c16e2a..0e290b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## v2.4.3 - Fixed replacer function receiving array keys as number instead of string +- Improved performance to escape long strings that contain characters that need escaping ## v2.4.2 diff --git a/index.js b/index.js index bd2aa1d..fd55add 100644 --- a/index.js +++ b/index.js @@ -21,67 +21,15 @@ module.exports = stringify // eslint-disable-next-line no-control-regex const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/ -const strEscapeSequencesReplacer = new RegExp(strEscapeSequencesRegExp, 'g') - -// Escaped special characters. Use empty strings to fill up unused entries. -const meta = [ - '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004', - '\\u0005', '\\u0006', '\\u0007', '\\b', '\\t', - '\\n', '\\u000b', '\\f', '\\r', '\\u000e', - '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013', - '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', - '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d', - '\\u001e', '\\u001f', '', '', '\\"', - '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '\\\\' -] - -function escapeFn (str) { - if (str.length === 2) { - const charCode = str.charCodeAt(1) - return `${str[0]}\\u${charCode.toString(16)}` - } - const charCode = str.charCodeAt(0) - return meta.length > charCode - ? meta[charCode] - : `\\u${charCode.toString(16)}` -} // Escape C0 control characters, double quotes, the backslash and every code // unit with a numeric value in the inclusive range 0xD800 to 0xDFFF. function strEscape (str) { // Some magic numbers that worked out fine while benchmarking with v8 8.0 if (str.length < 5000 && !strEscapeSequencesRegExp.test(str)) { - return str - } - if (str.length > 100) { - return str.replace(strEscapeSequencesReplacer, escapeFn) - } - let result = '' - let last = 0 - for (let i = 0; i < str.length; i++) { - const point = str.charCodeAt(i) - if (point === 34 || point === 92 || point < 32) { - result += `${str.slice(last, i)}${meta[point]}` - last = i + 1 - } else if (point >= 0xd800 && point <= 0xdfff) { - if (point <= 0xdbff && i + 1 < str.length) { - const nextPoint = str.charCodeAt(i + 1) - if (nextPoint >= 0xdc00 && nextPoint <= 0xdfff) { - i++ - continue - } - } - result += `${str.slice(last, i)}\\u${point.toString(16)}` - last = i + 1 - } + return `"${str}"` } - result += str.slice(last) - return result + return JSON.stringify(str) } function insertSort (array) { @@ -237,7 +185,7 @@ function configure (options) { switch (typeof value) { case 'string': - return `"${strEscape(value)}"` + return strEscape(value) case 'object': { if (value === null) { return 'null' @@ -313,7 +261,7 @@ function configure (options) { const key = keys[i] const tmp = stringifyFnReplacer(key, value, stack, replacer, spacer, indentation) if (tmp !== undefined) { - res += `${separator}"${strEscape(key)}":${whitespace}${tmp}` + res += `${separator}${strEscape(key)}:${whitespace}${tmp}` separator = join } } @@ -351,7 +299,7 @@ function configure (options) { switch (typeof value) { case 'string': - return `"${strEscape(value)}"` + return strEscape(value) case 'object': { if (value === null) { return 'null' @@ -407,7 +355,7 @@ function configure (options) { for (const key of replacer) { const tmp = stringifyArrayReplacer(key, value[key], stack, replacer, spacer, indentation) if (tmp !== undefined) { - res += `${separator}"${strEscape(key)}":${whitespace}${tmp}` + res += `${separator}${strEscape(key)}:${whitespace}${tmp}` separator = join } } @@ -436,7 +384,7 @@ function configure (options) { function stringifyIndent (key, value, stack, spacer, indentation) { switch (typeof value) { case 'string': - return `"${strEscape(value)}"` + return strEscape(value) case 'object': { if (value === null) { return 'null' @@ -512,7 +460,7 @@ function configure (options) { const key = keys[i] const tmp = stringifyIndent(key, value[key], stack, spacer, indentation) if (tmp !== undefined) { - res += `${separator}"${strEscape(key)}": ${tmp}` + res += `${separator}${strEscape(key)}: ${tmp}` separator = join } } @@ -546,7 +494,7 @@ function configure (options) { function stringifySimple (key, value, stack) { switch (typeof value) { case 'string': - return `"${strEscape(value)}"` + return strEscape(value) case 'object': { if (value === null) { return 'null' @@ -616,7 +564,7 @@ function configure (options) { const key = keys[i] const tmp = stringifySimple(key, value[key], stack) if (tmp !== undefined) { - res += `${separator}"${strEscape(key)}":${tmp}` + res += `${separator}${strEscape(key)}:${tmp}` separator = ',' } }