1package jsoncolor
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6	"io"
  7	"reflect"
  8	"runtime"
  9	"sync"
 10	"unsafe"
 11)
 12
 13// Delim is documented at https://golang.org/pkg/encoding/json/#Delim
 14type Delim = json.Delim
 15
 16// InvalidUTF8Error is documented at https://golang.org/pkg/encoding/json/#InvalidUTF8Error
 17type InvalidUTF8Error = json.InvalidUTF8Error
 18
 19// InvalidUnmarshalError is documented at https://golang.org/pkg/encoding/json/#InvalidUnmarshalError
 20type InvalidUnmarshalError = json.InvalidUnmarshalError
 21
 22// Marshaler is documented at https://golang.org/pkg/encoding/json/#Marshaler
 23type Marshaler = json.Marshaler
 24
 25// MarshalerError is documented at https://golang.org/pkg/encoding/json/#MarshalerError
 26type MarshalerError = json.MarshalerError
 27
 28// Number is documented at https://golang.org/pkg/encoding/json/#Number
 29type Number = json.Number
 30
 31// RawMessage is documented at https://golang.org/pkg/encoding/json/#RawMessage
 32type RawMessage = json.RawMessage
 33
 34// A SyntaxError is a description of a JSON syntax error.
 35type SyntaxError = json.SyntaxError
 36
 37// Token is documented at https://golang.org/pkg/encoding/json/#Token
 38type Token = json.Token
 39
 40// UnmarshalFieldError is documented at https://golang.org/pkg/encoding/json/#UnmarshalFieldError
 41type UnmarshalFieldError = json.UnmarshalFieldError
 42
 43// UnmarshalTypeError is documented at https://golang.org/pkg/encoding/json/#UnmarshalTypeError
 44type UnmarshalTypeError = json.UnmarshalTypeError
 45
 46// Unmarshaler is documented at https://golang.org/pkg/encoding/json/#Unmarshaler
 47type Unmarshaler = json.Unmarshaler
 48
 49// UnsupportedTypeError is documented at https://golang.org/pkg/encoding/json/#UnsupportedTypeError
 50type UnsupportedTypeError = json.UnsupportedTypeError
 51
 52// UnsupportedValueError is documented at https://golang.org/pkg/encoding/json/#UnsupportedValueError
 53type UnsupportedValueError = json.UnsupportedValueError
 54
 55// AppendFlags is a type used to represent configuration options that can be
 56// applied when formatting json output.
 57type AppendFlags int
 58
 59const (
 60	// EscapeHTML is a formatting flag used to to escape HTML in json strings.
 61	EscapeHTML AppendFlags = 1 << iota
 62
 63	// SortMapKeys is formatting flag used to enable sorting of map keys when
 64	// encoding JSON (this matches the behavior of the standard encoding/json
 65	// package).
 66	SortMapKeys
 67
 68	// TrustRawMessage is a performance optimization flag to skip value
 69	// checking of raw messages. It should only be used if the values are
 70	// known to be valid json (e.g., they were created by json.Unmarshal).
 71	TrustRawMessage
 72)
 73
 74// ParseFlags is a type used to represent configuration options that can be
 75// applied when parsing json input.
 76type ParseFlags int
 77
 78const (
 79	// DisallowUnknownFields is a parsing flag used to prevent decoding of
 80	// objects to Go struct values when a field of the input does not match
 81	// with any of the struct fields.
 82	DisallowUnknownFields ParseFlags = 1 << iota
 83
 84	// UseNumber is a parsing flag used to load numeric values as Number
 85	// instead of float64.
 86	UseNumber
 87
 88	// DontCopyString is a parsing flag used to provide zero-copy support when
 89	// loading string values from a json payload. It is not always possible to
 90	// avoid dynamic memory allocations, for example when a string is escaped in
 91	// the json data a new buffer has to be allocated, but when the `wire` value
 92	// can be used as content of a Go value the decoder will simply point into
 93	// the input buffer.
 94	DontCopyString
 95
 96	// DontCopyNumber is a parsing flag used to provide zero-copy support when
 97	// loading Number values (see DontCopyString and DontCopyRawMessage).
 98	DontCopyNumber
 99
100	// DontCopyRawMessage is a parsing flag used to provide zero-copy support
101	// when loading RawMessage values from a json payload. When used, the
102	// RawMessage values will not be allocated into new memory buffers and
103	// will instead point directly to the area of the input buffer where the
104	// value was found.
105	DontCopyRawMessage
106
107	// DontMatchCaseInsensitiveStructFields is a parsing flag used to prevent
108	// matching fields in a case-insensitive way. This can prevent degrading
109	// performance on case conversions, and can also act as a stricter decoding
110	// mode.
111	DontMatchCaseInsensitiveStructFields
112
113	// ZeroCopy is a parsing flag that combines all the copy optimizations
114	// available in the package.
115	//
116	// The zero-copy optimizations are better used in request-handler style
117	// code where none of the values are retained after the handler returns.
118	ZeroCopy = DontCopyString | DontCopyNumber | DontCopyRawMessage
119)
120
121// Append acts like Marshal but appends the json representation to b instead of
122// always reallocating a new slice.
123func Append(b []byte, x interface{}, flags AppendFlags, clrs *Colors, indentr *indenter) ([]byte, error) {
124	if x == nil {
125		// Special case for nil values because it makes the rest of the code
126		// simpler to assume that it won't be seeing nil pointers.
127		return clrs.appendNull(b), nil
128	}
129
130	t := reflect.TypeOf(x)
131	p := (*iface)(unsafe.Pointer(&x)).ptr
132
133	cache := cacheLoad()
134	c, found := cache[typeid(t)]
135
136	if !found {
137		c = constructCachedCodec(t, cache)
138	}
139
140	b, err := c.encode(encoder{flags: flags, clrs: clrs, indentr: indentr}, b, p)
141	runtime.KeepAlive(x)
142	return b, err
143}
144
145// Compact is documented at https://golang.org/pkg/encoding/json/#Compact
146func Compact(dst *bytes.Buffer, src []byte) error {
147	return json.Compact(dst, src)
148}
149
150// HTMLEscape is documented at https://golang.org/pkg/encoding/json/#HTMLEscape
151func HTMLEscape(dst *bytes.Buffer, src []byte) {
152	json.HTMLEscape(dst, src)
153}
154
155// Indent is documented at https://golang.org/pkg/encoding/json/#Indent
156func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
157	return json.Indent(dst, src, prefix, indent)
158}
159
160// Marshal is documented at https://golang.org/pkg/encoding/json/#Marshal
161func Marshal(x interface{}) ([]byte, error) {
162	var err error
163	var buf = encoderBufferPool.Get().(*encoderBuffer)
164
165	if buf.data, err = Append(buf.data[:0], x, EscapeHTML|SortMapKeys, nil, nil); err != nil {
166		return nil, err
167	}
168
169	b := make([]byte, len(buf.data))
170	copy(b, buf.data)
171	encoderBufferPool.Put(buf)
172	return b, nil
173}
174
175// MarshalIndent is documented at https://golang.org/pkg/encoding/json/#MarshalIndent
176func MarshalIndent(x interface{}, prefix, indent string) ([]byte, error) {
177	b, err := Marshal(x)
178
179	if err == nil {
180		tmp := &bytes.Buffer{}
181		tmp.Grow(2 * len(b))
182
183		if err = Indent(tmp, b, prefix, indent); err != nil {
184			return b, err
185		}
186
187		b = tmp.Bytes()
188	}
189
190	return b, err
191}
192
193// Unmarshal is documented at https://golang.org/pkg/encoding/json/#Unmarshal
194func Unmarshal(b []byte, x interface{}) error {
195	r, err := Parse(b, x, 0)
196	if len(r) != 0 {
197		if _, ok := err.(*SyntaxError); !ok {
198			// The encoding/json package prioritizes reporting errors caused by
199			// unexpected trailing bytes over other issues; here we emulate this
200			// behavior by overriding the error.
201			err = syntaxError(r, "invalid character '%c' after top-level value", r[0])
202		}
203	}
204	return err
205}
206
207// Parse behaves like Unmarshal but the caller can pass a set of flags to
208// configure the parsing behavior.
209func Parse(b []byte, x interface{}, flags ParseFlags) ([]byte, error) {
210	t := reflect.TypeOf(x)
211	p := (*iface)(unsafe.Pointer(&x)).ptr
212
213	if t == nil || p == nil || t.Kind() != reflect.Ptr {
214		_, r, err := parseValue(skipSpaces(b))
215		r = skipSpaces(r)
216		if err != nil {
217			return r, err
218		}
219		return r, &InvalidUnmarshalError{Type: t}
220	}
221	t = t.Elem()
222
223	cache := cacheLoad()
224	c, found := cache[typeid(t)]
225
226	if !found {
227		c = constructCachedCodec(t, cache)
228	}
229
230	r, err := c.decode(decoder{flags: flags}, skipSpaces(b), p)
231	return skipSpaces(r), err
232}
233
234// Valid is documented at https://golang.org/pkg/encoding/json/#Valid
235func Valid(data []byte) bool {
236	_, data, err := parseValue(skipSpaces(data))
237	if err != nil {
238		return false
239	}
240	return len(skipSpaces(data)) == 0
241}
242
243// Decoder is documented at https://golang.org/pkg/encoding/json/#Decoder
244type Decoder struct {
245	reader      io.Reader
246	buffer      []byte
247	remain      []byte
248	inputOffset int64
249	err         error
250	flags       ParseFlags
251}
252
253// NewDecoder is documented at https://golang.org/pkg/encoding/json/#NewDecoder
254func NewDecoder(r io.Reader) *Decoder { return &Decoder{reader: r} }
255
256// Buffered is documented at https://golang.org/pkg/encoding/json/#Decoder.Buffered
257func (dec *Decoder) Buffered() io.Reader {
258	return bytes.NewReader(dec.remain)
259}
260
261// Decode is documented at https://golang.org/pkg/encoding/json/#Decoder.Decode
262func (dec *Decoder) Decode(v interface{}) error {
263	raw, err := dec.readValue()
264	if err != nil {
265		return err
266	}
267	_, err = Parse(raw, v, dec.flags)
268	return err
269}
270
271const (
272	minBufferSize = 32768
273	minReadSize   = 4096
274)
275
276// readValue reads one JSON value from the buffer and returns its raw bytes. It
277// is optimized for the "one JSON value per line" case.
278func (dec *Decoder) readValue() (v []byte, err error) {
279	var n int
280	var r []byte
281
282	for {
283		if len(dec.remain) != 0 {
284			v, r, err = parseValue(dec.remain)
285			if err == nil {
286				dec.remain, n = skipSpacesN(r)
287				dec.inputOffset += int64(len(v) + n)
288				return
289			}
290			if len(r) != 0 {
291				// Parsing of the next JSON value stopped at a position other
292				// than the end of the input buffer, which indicaates that a
293				// syntax error was encountered.
294				return
295			}
296		}
297
298		if err = dec.err; err != nil {
299			if len(dec.remain) != 0 && err == io.EOF {
300				err = io.ErrUnexpectedEOF
301			}
302			return
303		}
304
305		if dec.buffer == nil {
306			dec.buffer = make([]byte, 0, minBufferSize)
307		} else {
308			dec.buffer = dec.buffer[:copy(dec.buffer[:cap(dec.buffer)], dec.remain)]
309			dec.remain = nil
310		}
311
312		if (cap(dec.buffer) - len(dec.buffer)) < minReadSize {
313			buf := make([]byte, len(dec.buffer), 2*cap(dec.buffer))
314			copy(buf, dec.buffer)
315			dec.buffer = buf
316		}
317
318		n, err = io.ReadFull(dec.reader, dec.buffer[len(dec.buffer):cap(dec.buffer)])
319		if n > 0 {
320			dec.buffer = dec.buffer[:len(dec.buffer)+n]
321			if err != nil {
322				err = nil
323			}
324		} else if err == io.ErrUnexpectedEOF {
325			err = io.EOF
326		}
327		dec.remain, n = skipSpacesN(dec.buffer)
328		dec.inputOffset += int64(n)
329		dec.err = err
330	}
331}
332
333// DisallowUnknownFields is documented at https://golang.org/pkg/encoding/json/#Decoder.DisallowUnknownFields
334func (dec *Decoder) DisallowUnknownFields() { dec.flags |= DisallowUnknownFields }
335
336// UseNumber is documented at https://golang.org/pkg/encoding/json/#Decoder.UseNumber
337func (dec *Decoder) UseNumber() { dec.flags |= UseNumber }
338
339// DontCopyString is an extension to the standard encoding/json package
340// which instructs the decoder to not copy strings loaded from the json
341// payloads when possible.
342func (dec *Decoder) DontCopyString() { dec.flags |= DontCopyString }
343
344// DontCopyNumber is an extension to the standard encoding/json package
345// which instructs the decoder to not copy numbers loaded from the json
346// payloads.
347func (dec *Decoder) DontCopyNumber() { dec.flags |= DontCopyNumber }
348
349// DontCopyRawMessage is an extension to the standard encoding/json package
350// which instructs the decoder to not allocate RawMessage values in separate
351// memory buffers (see the documentation of the DontcopyRawMessage flag for
352// more detais).
353func (dec *Decoder) DontCopyRawMessage() { dec.flags |= DontCopyRawMessage }
354
355// DontMatchCaseInsensitiveStructFields is an extension to the standard
356// encoding/json package which instructs the decoder to not match object fields
357// against struct fields in a case-insensitive way, the field names have to
358// match exactly to be decoded into the struct field values.
359func (dec *Decoder) DontMatchCaseInsensitiveStructFields() {
360	dec.flags |= DontMatchCaseInsensitiveStructFields
361}
362
363// ZeroCopy is an extension to the standard encoding/json package which enables
364// all the copy optimizations of the decoder.
365func (dec *Decoder) ZeroCopy() { dec.flags |= ZeroCopy }
366
367// InputOffset returns the input stream byte offset of the current decoder position.
368// The offset gives the location of the end of the most recently returned token
369// and the beginning of the next token.
370func (dec *Decoder) InputOffset() int64 {
371	return dec.inputOffset
372}
373
374// Encoder is documented at https://golang.org/pkg/encoding/json/#Encoder
375type Encoder struct {
376	writer  io.Writer
377	buffer  *bytes.Buffer
378	err     error
379	flags   AppendFlags
380	clrs    *Colors
381	indentr *indenter
382}
383
384// NewEncoder is documented at https://golang.org/pkg/encoding/json/#NewEncoder
385func NewEncoder(w io.Writer) *Encoder { return &Encoder{writer: w, flags: EscapeHTML | SortMapKeys} }
386
387// SetColors sets the colors for the encoder to use.
388func (enc *Encoder) SetColors(c *Colors) {
389	enc.clrs = c
390}
391
392// Encode is documented at https://golang.org/pkg/encoding/json/#Encoder.Encode
393func (enc *Encoder) Encode(v interface{}) error {
394	if enc.err != nil {
395		return enc.err
396	}
397
398	var err error
399	var buf = encoderBufferPool.Get().(*encoderBuffer)
400
401	// Note: unlike the original segmentio encoder, indentation is
402	// performed via the Append function.
403	buf.data, err = Append(buf.data[:0], v, enc.flags, enc.clrs, enc.indentr)
404	if err != nil {
405		encoderBufferPool.Put(buf)
406		return err
407	}
408
409	buf.data = append(buf.data, '\n')
410	b := buf.data
411
412	if _, err := enc.writer.Write(b); err != nil {
413		enc.err = err
414	}
415
416	encoderBufferPool.Put(buf)
417	return err
418}
419
420// SetEscapeHTML is documented at https://golang.org/pkg/encoding/json/#Encoder.SetEscapeHTML
421func (enc *Encoder) SetEscapeHTML(on bool) {
422	if on {
423		enc.flags |= EscapeHTML
424	} else {
425		enc.flags &= ^EscapeHTML
426	}
427}
428
429// SetIndent is documented at https://golang.org/pkg/encoding/json/#Encoder.SetIndent
430func (enc *Encoder) SetIndent(prefix, indent string) {
431	enc.indentr = newIndenter(prefix, indent)
432}
433
434// SetSortMapKeys is an extension to the standard encoding/json package which
435// allows the program to toggle sorting of map keys on and off.
436func (enc *Encoder) SetSortMapKeys(on bool) {
437	if on {
438		enc.flags |= SortMapKeys
439	} else {
440		enc.flags &= ^SortMapKeys
441	}
442}
443
444// SetTrustRawMessage skips value checking when encoding a raw json message. It should only
445// be used if the values are known to be valid json, e.g. because they were originally created
446// by json.Unmarshal.
447func (enc *Encoder) SetTrustRawMessage(on bool) {
448	if on {
449		enc.flags |= TrustRawMessage
450	} else {
451		enc.flags &= ^TrustRawMessage
452	}
453}
454
455var encoderBufferPool = sync.Pool{
456	New: func() interface{} { return &encoderBuffer{data: make([]byte, 0, 4096)} },
457}
458
459type encoderBuffer struct{ data []byte }