/
quote.go
114 lines (98 loc) · 2.29 KB
/
quote.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package helpers
import "unicode/utf8"
const hexChars = "0123456789ABCDEF"
const firstASCII = 0x20
const lastASCII = 0x7E
const firstHighSurrogate = 0xD800
const firstLowSurrogate = 0xDC00
const lastLowSurrogate = 0xDFFF
func canPrintWithoutEscape(c rune, asciiOnly bool) bool {
if c <= lastASCII {
return c >= firstASCII && c != '\\' && c != '"'
} else {
return !asciiOnly && c != '\uFEFF' && (c < firstHighSurrogate || c > lastLowSurrogate)
}
}
func QuoteForJSON(text string, asciiOnly bool) []byte {
// Estimate the required length
lenEstimate := 2
for _, c := range text {
if canPrintWithoutEscape(c, asciiOnly) {
lenEstimate += utf8.RuneLen(c)
} else {
switch c {
case '\b', '\f', '\n', '\r', '\t', '\\', '"':
lenEstimate += 2
default:
if c <= 0xFFFF {
lenEstimate += 6
} else {
lenEstimate += 12
}
}
}
}
// Preallocate the array
bytes := make([]byte, 0, lenEstimate)
i := 0
n := len(text)
bytes = append(bytes, '"')
for i < n {
c, width := DecodeWTF8Rune(text[i:])
// Fast path: a run of characters that don't need escaping
if canPrintWithoutEscape(c, asciiOnly) {
start := i
i += width
for i < n {
c, width = DecodeWTF8Rune(text[i:])
if !canPrintWithoutEscape(c, asciiOnly) {
break
}
i += width
}
bytes = append(bytes, text[start:i]...)
continue
}
switch c {
case '\b':
bytes = append(bytes, "\\b"...)
i++
case '\f':
bytes = append(bytes, "\\f"...)
i++
case '\n':
bytes = append(bytes, "\\n"...)
i++
case '\r':
bytes = append(bytes, "\\r"...)
i++
case '\t':
bytes = append(bytes, "\\t"...)
i++
case '\\':
bytes = append(bytes, "\\\\"...)
i++
case '"':
bytes = append(bytes, "\\\""...)
i++
default:
i += width
if c <= 0xFFFF {
bytes = append(
bytes,
'\\', 'u', hexChars[c>>12], hexChars[(c>>8)&15], hexChars[(c>>4)&15], hexChars[c&15],
)
} else {
c -= 0x10000
lo := firstHighSurrogate + ((c >> 10) & 0x3FF)
hi := firstLowSurrogate + (c & 0x3FF)
bytes = append(
bytes,
'\\', 'u', hexChars[lo>>12], hexChars[(lo>>8)&15], hexChars[(lo>>4)&15], hexChars[lo&15],
'\\', 'u', hexChars[hi>>12], hexChars[(hi>>8)&15], hexChars[(hi>>4)&15], hexChars[hi&15],
)
}
}
}
return append(bytes, '"')
}