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