|
1 | | -// A map of strings that can be directly substituted for any code points that need to be escaped. |
2 | | -const escapeStrings: { [key: number]: string } = { |
3 | | - 34: '\\"', |
4 | | - 92: '\\\\', |
5 | | -}; |
6 | | - |
7 | | -// Generate UTF-16 hex values for ASCII control characters by converting numbers 0 to 31 to hex values. |
8 | | -for (let i = 0; i < 32; i++) { |
9 | | - escapeStrings[i] = '\\u00' + i.toString(16); |
10 | | -} |
11 | | - |
12 | | -// Generate UTF-16 hex values for surrogate code points by converting numbers 55,296 to 57,343 to hex values. |
13 | | -for (let i = 55_296; i < 57_344; i++) { |
14 | | - escapeStrings[i] = '\\u' + i.toString(16); |
15 | | -} |
| 1 | +const QUOTATION_MARK = '"'; |
| 2 | +const BACKSLASH = '\\'; |
16 | 3 |
|
17 | 4 | /** |
18 | | - * stringifyString() converts a string to a JSON string by escaping any ASCII control characters and lone UTF-16 |
19 | | - * surrogate code points, as well as the double quote and backslash characters, and then returning the resulting string |
20 | | - * bookended by double quote characters. Control characters and lone surrogate code points are converted to UTF-16 hex |
21 | | - * values before being escaped. Double quote and backslash characters are simply escaped with a backslash. |
| 5 | + * Converts a string to a JSON string by escaping any double quote and backslash characters, and then returning the |
| 6 | + * resulting string bookended by double quotes. ASCII control characters and lone surrogate code points are NOT escaped. |
22 | 7 | */ |
23 | 8 |
|
24 | | -export function stringifyString(value: string): string { |
25 | | - let str = '"'; |
26 | | - let i; |
27 | | - let j; |
28 | | - let charCode; |
29 | | - let nextCharCode; |
30 | | - |
31 | | - for (i = 0, j = 0; i < value.length; i++) { |
32 | | - charCode = value.charCodeAt(i); |
33 | | - |
34 | | - if (charCode < 32 || charCode === 34 || charCode === 92) { |
35 | | - // The current character is a control character, double quote, or backslash. |
36 | | - str += value.slice(j, i) + escapeStrings[charCode]; |
37 | | - j = i + 1; |
38 | | - } |
39 | | - |
40 | | - else if (charCode >= 0xD800 && charCode <= 0xDFFF) { |
41 | | - // The current character is a surrogate. |
42 | | - if (charCode <= 0xDBFF) { |
43 | | - // The current character is a leading surrogate. |
44 | | - nextCharCode = value.charCodeAt(i + 1); |
| 9 | +export function serializeString(value: string): string { |
| 10 | + let result = QUOTATION_MARK; |
| 11 | + let index; |
| 12 | + let prevIndex = 0; |
45 | 13 |
|
46 | | - if (nextCharCode >= 0xDC00 && nextCharCode <= 0xDFFF) { |
47 | | - // The next character is a trailing surrogate, so skip the next iteration. |
48 | | - i++; |
49 | | - } |
| 14 | + let i = value.indexOf(QUOTATION_MARK); |
| 15 | + let j = value.indexOf(BACKSLASH); |
50 | 16 |
|
51 | | - else { |
52 | | - // The current character is a lone leading surrogate. |
53 | | - str += value.slice(j, i) + escapeStrings[charCode]; |
54 | | - j = i + 1; |
55 | | - } |
| 17 | + if (i > -1 && j > -1) { |
| 18 | + do { |
| 19 | + if (i < j) { |
| 20 | + index = i; |
| 21 | + i = value.indexOf(QUOTATION_MARK, index + 1); |
56 | 22 | } |
57 | 23 |
|
58 | 24 | else { |
59 | | - // The current character is a lone trailing surrogate. |
60 | | - str += value.slice(j, i) + escapeStrings[charCode]; |
61 | | - j = i + 1; |
| 25 | + index = j; |
| 26 | + j = value.indexOf(BACKSLASH, index + 1); |
62 | 27 | } |
63 | | - } |
| 28 | + |
| 29 | + result += value.slice(prevIndex, index) + BACKSLASH; |
| 30 | + prevIndex = index; |
| 31 | + } while (i > -1 && j > -1) |
| 32 | + } |
| 33 | + |
| 34 | + if (i > -1) { |
| 35 | + do { |
| 36 | + index = i; |
| 37 | + i = value.indexOf(QUOTATION_MARK, index + 1); |
| 38 | + |
| 39 | + result += value.slice(prevIndex, index) + BACKSLASH; |
| 40 | + prevIndex = index; |
| 41 | + } while (i > -1) |
64 | 42 | } |
65 | 43 |
|
66 | | - // If j is 0 then the string was not altered, so the original string value can be used. |
67 | | - str += j === 0 ? value : value.slice(j); |
| 44 | + else if (j > -1) { |
| 45 | + do { |
| 46 | + index = j; |
| 47 | + j = value.indexOf(BACKSLASH, index + 1); |
| 48 | + |
| 49 | + result += value.slice(prevIndex, index) + BACKSLASH; |
| 50 | + prevIndex = index; |
| 51 | + } while (j > -1) |
| 52 | + } |
68 | 53 |
|
69 | | - return str + '"'; |
| 54 | + return result + value.slice(prevIndex) + QUOTATION_MARK; |
70 | 55 | } |
0 commit comments