1 package jsoniter
2
3 import (
4 "unicode/utf8"
5 )
6
7
8
9
10
11
12
13
14 var htmlSafeSet = [utf8.RuneSelf]bool{
15 ' ': true,
16 '!': true,
17 '"': false,
18 '#': true,
19 '$': true,
20 '%': true,
21 '&': false,
22 '\'': true,
23 '(': true,
24 ')': true,
25 '*': true,
26 '+': true,
27 ',': true,
28 '-': true,
29 '.': true,
30 '/': true,
31 '0': true,
32 '1': true,
33 '2': true,
34 '3': true,
35 '4': true,
36 '5': true,
37 '6': true,
38 '7': true,
39 '8': true,
40 '9': true,
41 ':': true,
42 ';': true,
43 '<': false,
44 '=': true,
45 '>': false,
46 '?': true,
47 '@': true,
48 'A': true,
49 'B': true,
50 'C': true,
51 'D': true,
52 'E': true,
53 'F': true,
54 'G': true,
55 'H': true,
56 'I': true,
57 'J': true,
58 'K': true,
59 'L': true,
60 'M': true,
61 'N': true,
62 'O': true,
63 'P': true,
64 'Q': true,
65 'R': true,
66 'S': true,
67 'T': true,
68 'U': true,
69 'V': true,
70 'W': true,
71 'X': true,
72 'Y': true,
73 'Z': true,
74 '[': true,
75 '\\': false,
76 ']': true,
77 '^': true,
78 '_': true,
79 '`': true,
80 'a': true,
81 'b': true,
82 'c': true,
83 'd': true,
84 'e': true,
85 'f': true,
86 'g': true,
87 'h': true,
88 'i': true,
89 'j': true,
90 'k': true,
91 'l': true,
92 'm': true,
93 'n': true,
94 'o': true,
95 'p': true,
96 'q': true,
97 'r': true,
98 's': true,
99 't': true,
100 'u': true,
101 'v': true,
102 'w': true,
103 'x': true,
104 'y': true,
105 'z': true,
106 '{': true,
107 '|': true,
108 '}': true,
109 '~': true,
110 '\u007f': true,
111 }
112
113
114
115
116
117
118
119 var safeSet = [utf8.RuneSelf]bool{
120 ' ': true,
121 '!': true,
122 '"': false,
123 '#': true,
124 '$': true,
125 '%': true,
126 '&': true,
127 '\'': true,
128 '(': true,
129 ')': true,
130 '*': true,
131 '+': true,
132 ',': true,
133 '-': true,
134 '.': true,
135 '/': true,
136 '0': true,
137 '1': true,
138 '2': true,
139 '3': true,
140 '4': true,
141 '5': true,
142 '6': true,
143 '7': true,
144 '8': true,
145 '9': true,
146 ':': true,
147 ';': true,
148 '<': true,
149 '=': true,
150 '>': true,
151 '?': true,
152 '@': true,
153 'A': true,
154 'B': true,
155 'C': true,
156 'D': true,
157 'E': true,
158 'F': true,
159 'G': true,
160 'H': true,
161 'I': true,
162 'J': true,
163 'K': true,
164 'L': true,
165 'M': true,
166 'N': true,
167 'O': true,
168 'P': true,
169 'Q': true,
170 'R': true,
171 'S': true,
172 'T': true,
173 'U': true,
174 'V': true,
175 'W': true,
176 'X': true,
177 'Y': true,
178 'Z': true,
179 '[': true,
180 '\\': false,
181 ']': true,
182 '^': true,
183 '_': true,
184 '`': true,
185 'a': true,
186 'b': true,
187 'c': true,
188 'd': true,
189 'e': true,
190 'f': true,
191 'g': true,
192 'h': true,
193 'i': true,
194 'j': true,
195 'k': true,
196 'l': true,
197 'm': true,
198 'n': true,
199 'o': true,
200 'p': true,
201 'q': true,
202 'r': true,
203 's': true,
204 't': true,
205 'u': true,
206 'v': true,
207 'w': true,
208 'x': true,
209 'y': true,
210 'z': true,
211 '{': true,
212 '|': true,
213 '}': true,
214 '~': true,
215 '\u007f': true,
216 }
217
218 var hex = "0123456789abcdef"
219
220
221 func (stream *Stream) WriteStringWithHTMLEscaped(s string) {
222 valLen := len(s)
223 stream.buf = append(stream.buf, '"')
224
225 i := 0
226 for ; i < valLen; i++ {
227 c := s[i]
228 if c < utf8.RuneSelf && htmlSafeSet[c] {
229 stream.buf = append(stream.buf, c)
230 } else {
231 break
232 }
233 }
234 if i == valLen {
235 stream.buf = append(stream.buf, '"')
236 return
237 }
238 writeStringSlowPathWithHTMLEscaped(stream, i, s, valLen)
239 }
240
241 func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen int) {
242 start := i
243
244 for i < valLen {
245 if b := s[i]; b < utf8.RuneSelf {
246 if htmlSafeSet[b] {
247 i++
248 continue
249 }
250 if start < i {
251 stream.WriteRaw(s[start:i])
252 }
253 switch b {
254 case '\\', '"':
255 stream.writeTwoBytes('\\', b)
256 case '\n':
257 stream.writeTwoBytes('\\', 'n')
258 case '\r':
259 stream.writeTwoBytes('\\', 'r')
260 case '\t':
261 stream.writeTwoBytes('\\', 't')
262 default:
263
264
265
266
267
268 stream.WriteRaw(`\u00`)
269 stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
270 }
271 i++
272 start = i
273 continue
274 }
275 c, size := utf8.DecodeRuneInString(s[i:])
276 if c == utf8.RuneError && size == 1 {
277 if start < i {
278 stream.WriteRaw(s[start:i])
279 }
280 stream.WriteRaw(`\ufffd`)
281 i++
282 start = i
283 continue
284 }
285
286
287
288
289
290
291
292 if c == '\u2028' || c == '\u2029' {
293 if start < i {
294 stream.WriteRaw(s[start:i])
295 }
296 stream.WriteRaw(`\u202`)
297 stream.writeByte(hex[c&0xF])
298 i += size
299 start = i
300 continue
301 }
302 i += size
303 }
304 if start < len(s) {
305 stream.WriteRaw(s[start:])
306 }
307 stream.writeByte('"')
308 }
309
310
311 func (stream *Stream) WriteString(s string) {
312 valLen := len(s)
313 stream.buf = append(stream.buf, '"')
314
315 i := 0
316 for ; i < valLen; i++ {
317 c := s[i]
318 if c > 31 && c != '"' && c != '\\' {
319 stream.buf = append(stream.buf, c)
320 } else {
321 break
322 }
323 }
324 if i == valLen {
325 stream.buf = append(stream.buf, '"')
326 return
327 }
328 writeStringSlowPath(stream, i, s, valLen)
329 }
330
331 func writeStringSlowPath(stream *Stream, i int, s string, valLen int) {
332 start := i
333
334 for i < valLen {
335 if b := s[i]; b < utf8.RuneSelf {
336 if safeSet[b] {
337 i++
338 continue
339 }
340 if start < i {
341 stream.WriteRaw(s[start:i])
342 }
343 switch b {
344 case '\\', '"':
345 stream.writeTwoBytes('\\', b)
346 case '\n':
347 stream.writeTwoBytes('\\', 'n')
348 case '\r':
349 stream.writeTwoBytes('\\', 'r')
350 case '\t':
351 stream.writeTwoBytes('\\', 't')
352 default:
353
354
355
356
357
358 stream.WriteRaw(`\u00`)
359 stream.writeTwoBytes(hex[b>>4], hex[b&0xF])
360 }
361 i++
362 start = i
363 continue
364 }
365 i++
366 continue
367 }
368 if start < len(s) {
369 stream.WriteRaw(s[start:])
370 }
371 stream.writeByte('"')
372 }
373
View as plain text