writer.go raw
1 // Package jwriter contains a JSON writer.
2 package jwriter
3
4 import (
5 "io"
6 "strconv"
7 "unicode/utf8"
8
9 "github.com/mailru/easyjson/buffer"
10 )
11
12 // Flags describe various encoding options. The behavior may be actually implemented in the encoder, but
13 // Flags field in Writer is used to set and pass them around.
14 type Flags int
15
16 const (
17 NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'.
18 NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'.
19 )
20
21 // Writer is a JSON writer.
22 type Writer struct {
23 Flags Flags
24
25 Error error
26 Buffer buffer.Buffer
27 NoEscapeHTML bool
28 }
29
30 // Size returns the size of the data that was written out.
31 func (w *Writer) Size() int {
32 return w.Buffer.Size()
33 }
34
35 // DumpTo outputs the data to given io.Writer, resetting the buffer.
36 func (w *Writer) DumpTo(out io.Writer) (written int, err error) {
37 return w.Buffer.DumpTo(out)
38 }
39
40 // BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice
41 // as argument that it will try to reuse.
42 func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) {
43 if w.Error != nil {
44 return nil, w.Error
45 }
46
47 return w.Buffer.BuildBytes(reuse...), nil
48 }
49
50 // ReadCloser returns an io.ReadCloser that can be used to read the data.
51 // ReadCloser also resets the buffer.
52 func (w *Writer) ReadCloser() (io.ReadCloser, error) {
53 if w.Error != nil {
54 return nil, w.Error
55 }
56
57 return w.Buffer.ReadCloser(), nil
58 }
59
60 // RawByte appends raw binary data to the buffer.
61 func (w *Writer) RawByte(c byte) {
62 w.Buffer.AppendByte(c)
63 }
64
65 // RawByte appends raw binary data to the buffer.
66 func (w *Writer) RawString(s string) {
67 w.Buffer.AppendString(s)
68 }
69
70 // RawBytesString appends string from bytes to the buffer.
71 func (w *Writer) RawBytesString(data []byte, err error) {
72 switch {
73 case w.Error != nil:
74 return
75 case err != nil:
76 w.Error = err
77 default:
78 w.String(string(data))
79 }
80 }
81
82 // Raw appends raw binary data to the buffer or sets the error if it is given. Useful for
83 // calling with results of MarshalJSON-like functions.
84 func (w *Writer) Raw(data []byte, err error) {
85 switch {
86 case w.Error != nil:
87 return
88 case err != nil:
89 w.Error = err
90 case len(data) > 0:
91 w.Buffer.AppendBytes(data)
92 default:
93 w.RawString("null")
94 }
95 }
96
97 // RawText encloses raw binary data in quotes and appends in to the buffer.
98 // Useful for calling with results of MarshalText-like functions.
99 func (w *Writer) RawText(data []byte, err error) {
100 switch {
101 case w.Error != nil:
102 return
103 case err != nil:
104 w.Error = err
105 case len(data) > 0:
106 w.String(string(data))
107 default:
108 w.RawString("null")
109 }
110 }
111
112 // Base64Bytes appends data to the buffer after base64 encoding it
113 func (w *Writer) Base64Bytes(data []byte) {
114 if data == nil {
115 w.Buffer.AppendString("null")
116 return
117 }
118 w.Buffer.AppendByte('"')
119 w.base64(data)
120 w.Buffer.AppendByte('"')
121 }
122
123 func (w *Writer) Uint8(n uint8) {
124 w.Buffer.EnsureSpace(3)
125 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
126 }
127
128 func (w *Writer) Uint16(n uint16) {
129 w.Buffer.EnsureSpace(5)
130 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
131 }
132
133 func (w *Writer) Uint32(n uint32) {
134 w.Buffer.EnsureSpace(10)
135 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
136 }
137
138 func (w *Writer) Uint(n uint) {
139 w.Buffer.EnsureSpace(20)
140 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
141 }
142
143 func (w *Writer) Uint64(n uint64) {
144 w.Buffer.EnsureSpace(20)
145 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
146 }
147
148 func (w *Writer) Int8(n int8) {
149 w.Buffer.EnsureSpace(4)
150 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
151 }
152
153 func (w *Writer) Int16(n int16) {
154 w.Buffer.EnsureSpace(6)
155 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
156 }
157
158 func (w *Writer) Int32(n int32) {
159 w.Buffer.EnsureSpace(11)
160 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
161 }
162
163 func (w *Writer) Int(n int) {
164 w.Buffer.EnsureSpace(21)
165 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
166 }
167
168 func (w *Writer) Int64(n int64) {
169 w.Buffer.EnsureSpace(21)
170 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
171 }
172
173 func (w *Writer) Uint8Str(n uint8) {
174 w.Buffer.EnsureSpace(3)
175 w.Buffer.Buf = append(w.Buffer.Buf, '"')
176 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
177 w.Buffer.Buf = append(w.Buffer.Buf, '"')
178 }
179
180 func (w *Writer) Uint16Str(n uint16) {
181 w.Buffer.EnsureSpace(5)
182 w.Buffer.Buf = append(w.Buffer.Buf, '"')
183 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
184 w.Buffer.Buf = append(w.Buffer.Buf, '"')
185 }
186
187 func (w *Writer) Uint32Str(n uint32) {
188 w.Buffer.EnsureSpace(10)
189 w.Buffer.Buf = append(w.Buffer.Buf, '"')
190 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
191 w.Buffer.Buf = append(w.Buffer.Buf, '"')
192 }
193
194 func (w *Writer) UintStr(n uint) {
195 w.Buffer.EnsureSpace(20)
196 w.Buffer.Buf = append(w.Buffer.Buf, '"')
197 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
198 w.Buffer.Buf = append(w.Buffer.Buf, '"')
199 }
200
201 func (w *Writer) Uint64Str(n uint64) {
202 w.Buffer.EnsureSpace(20)
203 w.Buffer.Buf = append(w.Buffer.Buf, '"')
204 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
205 w.Buffer.Buf = append(w.Buffer.Buf, '"')
206 }
207
208 func (w *Writer) UintptrStr(n uintptr) {
209 w.Buffer.EnsureSpace(20)
210 w.Buffer.Buf = append(w.Buffer.Buf, '"')
211 w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
212 w.Buffer.Buf = append(w.Buffer.Buf, '"')
213 }
214
215 func (w *Writer) Int8Str(n int8) {
216 w.Buffer.EnsureSpace(4)
217 w.Buffer.Buf = append(w.Buffer.Buf, '"')
218 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
219 w.Buffer.Buf = append(w.Buffer.Buf, '"')
220 }
221
222 func (w *Writer) Int16Str(n int16) {
223 w.Buffer.EnsureSpace(6)
224 w.Buffer.Buf = append(w.Buffer.Buf, '"')
225 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
226 w.Buffer.Buf = append(w.Buffer.Buf, '"')
227 }
228
229 func (w *Writer) Int32Str(n int32) {
230 w.Buffer.EnsureSpace(11)
231 w.Buffer.Buf = append(w.Buffer.Buf, '"')
232 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
233 w.Buffer.Buf = append(w.Buffer.Buf, '"')
234 }
235
236 func (w *Writer) IntStr(n int) {
237 w.Buffer.EnsureSpace(21)
238 w.Buffer.Buf = append(w.Buffer.Buf, '"')
239 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
240 w.Buffer.Buf = append(w.Buffer.Buf, '"')
241 }
242
243 func (w *Writer) Int64Str(n int64) {
244 w.Buffer.EnsureSpace(21)
245 w.Buffer.Buf = append(w.Buffer.Buf, '"')
246 w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
247 w.Buffer.Buf = append(w.Buffer.Buf, '"')
248 }
249
250 func (w *Writer) Float32(n float32) {
251 w.Buffer.EnsureSpace(20)
252 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
253 }
254
255 func (w *Writer) Float32Str(n float32) {
256 w.Buffer.EnsureSpace(20)
257 w.Buffer.Buf = append(w.Buffer.Buf, '"')
258 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
259 w.Buffer.Buf = append(w.Buffer.Buf, '"')
260 }
261
262 func (w *Writer) Float64(n float64) {
263 w.Buffer.EnsureSpace(20)
264 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
265 }
266
267 func (w *Writer) Float64Str(n float64) {
268 w.Buffer.EnsureSpace(20)
269 w.Buffer.Buf = append(w.Buffer.Buf, '"')
270 w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64)
271 w.Buffer.Buf = append(w.Buffer.Buf, '"')
272 }
273
274 func (w *Writer) Bool(v bool) {
275 w.Buffer.EnsureSpace(5)
276 if v {
277 w.Buffer.Buf = append(w.Buffer.Buf, "true"...)
278 } else {
279 w.Buffer.Buf = append(w.Buffer.Buf, "false"...)
280 }
281 }
282
283 const chars = "0123456789abcdef"
284
285 func getTable(falseValues ...int) [128]bool {
286 table := [128]bool{}
287
288 for i := 0; i < 128; i++ {
289 table[i] = true
290 }
291
292 for _, v := range falseValues {
293 table[v] = false
294 }
295
296 return table
297 }
298
299 var (
300 htmlEscapeTable = getTable(0, 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, '"', '&', '<', '>', '\\')
301 htmlNoEscapeTable = getTable(0, 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, '"', '\\')
302 )
303
304 func (w *Writer) String(s string) {
305 w.Buffer.AppendByte('"')
306
307 // Portions of the string that contain no escapes are appended as
308 // byte slices.
309
310 p := 0 // last non-escape symbol
311
312 escapeTable := &htmlEscapeTable
313 if w.NoEscapeHTML {
314 escapeTable = &htmlNoEscapeTable
315 }
316
317 for i := 0; i < len(s); {
318 c := s[i]
319
320 if c < utf8.RuneSelf {
321 if escapeTable[c] {
322 // single-width character, no escaping is required
323 i++
324 continue
325 }
326
327 w.Buffer.AppendString(s[p:i])
328 switch c {
329 case '\t':
330 w.Buffer.AppendString(`\t`)
331 case '\r':
332 w.Buffer.AppendString(`\r`)
333 case '\n':
334 w.Buffer.AppendString(`\n`)
335 case '\\':
336 w.Buffer.AppendString(`\\`)
337 case '"':
338 w.Buffer.AppendString(`\"`)
339 default:
340 w.Buffer.AppendString(`\u00`)
341 w.Buffer.AppendByte(chars[c>>4])
342 w.Buffer.AppendByte(chars[c&0xf])
343 }
344
345 i++
346 p = i
347 continue
348 }
349
350 // broken utf
351 runeValue, runeWidth := utf8.DecodeRuneInString(s[i:])
352 if runeValue == utf8.RuneError && runeWidth == 1 {
353 w.Buffer.AppendString(s[p:i])
354 w.Buffer.AppendString(`\ufffd`)
355 i++
356 p = i
357 continue
358 }
359
360 // jsonp stuff - tab separator and line separator
361 if runeValue == '\u2028' || runeValue == '\u2029' {
362 w.Buffer.AppendString(s[p:i])
363 w.Buffer.AppendString(`\u202`)
364 w.Buffer.AppendByte(chars[runeValue&0xf])
365 i += runeWidth
366 p = i
367 continue
368 }
369 i += runeWidth
370 }
371 w.Buffer.AppendString(s[p:])
372 w.Buffer.AppendByte('"')
373 }
374
375 const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
376 const padChar = '='
377
378 func (w *Writer) base64(in []byte) {
379
380 if len(in) == 0 {
381 return
382 }
383
384 w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4)
385
386 si := 0
387 n := (len(in) / 3) * 3
388
389 for si < n {
390 // Convert 3x 8bit source bytes into 4 bytes
391 val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2])
392
393 w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F])
394
395 si += 3
396 }
397
398 remain := len(in) - si
399 if remain == 0 {
400 return
401 }
402
403 // Add the remaining small block
404 val := uint(in[si+0]) << 16
405 if remain == 2 {
406 val |= uint(in[si+1]) << 8
407 }
408
409 w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F])
410
411 switch remain {
412 case 2:
413 w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar))
414 case 1:
415 w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar))
416 }
417 }
418