github.com/neilotoole/jsoncolor@v0.6.0/json.go (about)

     1  package jsoncolor
     2  
     3  import (
     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
    14  type Delim = json.Delim
    15  
    16  // InvalidUTF8Error is documented at https://golang.org/pkg/encoding/json/#InvalidUTF8Error
    17  type InvalidUTF8Error = json.InvalidUTF8Error
    18  
    19  // InvalidUnmarshalError is documented at https://golang.org/pkg/encoding/json/#InvalidUnmarshalError
    20  type InvalidUnmarshalError = json.InvalidUnmarshalError
    21  
    22  // Marshaler is documented at https://golang.org/pkg/encoding/json/#Marshaler
    23  type Marshaler = json.Marshaler
    24  
    25  // MarshalerError is documented at https://golang.org/pkg/encoding/json/#MarshalerError
    26  type MarshalerError = json.MarshalerError
    27  
    28  // Number is documented at https://golang.org/pkg/encoding/json/#Number
    29  type Number = json.Number
    30  
    31  // RawMessage is documented at https://golang.org/pkg/encoding/json/#RawMessage
    32  type RawMessage = json.RawMessage
    33  
    34  // A SyntaxError is a description of a JSON syntax error.
    35  type SyntaxError = json.SyntaxError
    36  
    37  // Token is documented at https://golang.org/pkg/encoding/json/#Token
    38  type Token = json.Token
    39  
    40  // UnmarshalFieldError is documented at https://golang.org/pkg/encoding/json/#UnmarshalFieldError
    41  type UnmarshalFieldError = json.UnmarshalFieldError
    42  
    43  // UnmarshalTypeError is documented at https://golang.org/pkg/encoding/json/#UnmarshalTypeError
    44  type UnmarshalTypeError = json.UnmarshalTypeError
    45  
    46  // Unmarshaler is documented at https://golang.org/pkg/encoding/json/#Unmarshaler
    47  type Unmarshaler = json.Unmarshaler
    48  
    49  // UnsupportedTypeError is documented at https://golang.org/pkg/encoding/json/#UnsupportedTypeError
    50  type UnsupportedTypeError = json.UnsupportedTypeError
    51  
    52  // UnsupportedValueError is documented at https://golang.org/pkg/encoding/json/#UnsupportedValueError
    53  type UnsupportedValueError = json.UnsupportedValueError
    54  
    55  // AppendFlags is a type used to represent configuration options that can be
    56  // applied when formatting json output.
    57  type AppendFlags int
    58  
    59  const (
    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.
    76  type ParseFlags int
    77  
    78  const (
    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.
   123  func 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
   146  func 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
   151  func 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
   156  func 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
   161  func 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
   176  func 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
   194  func 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.
   209  func 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
   235  func 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
   244  type 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
   254  func NewDecoder(r io.Reader) *Decoder { return &Decoder{reader: r} }
   255  
   256  // Buffered is documented at https://golang.org/pkg/encoding/json/#Decoder.Buffered
   257  func (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
   262  func (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  
   271  const (
   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.
   278  func (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
   334  func (dec *Decoder) DisallowUnknownFields() { dec.flags |= DisallowUnknownFields }
   335  
   336  // UseNumber is documented at https://golang.org/pkg/encoding/json/#Decoder.UseNumber
   337  func (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.
   342  func (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.
   347  func (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).
   353  func (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.
   359  func (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.
   365  func (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.
   370  func (dec *Decoder) InputOffset() int64 {
   371  	return dec.inputOffset
   372  }
   373  
   374  // Encoder is documented at https://golang.org/pkg/encoding/json/#Encoder
   375  type 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
   385  func NewEncoder(w io.Writer) *Encoder { return &Encoder{writer: w, flags: EscapeHTML | SortMapKeys} }
   386  
   387  // SetColors sets the colors for the encoder to use.
   388  func (enc *Encoder) SetColors(c *Colors) {
   389  	enc.clrs = c
   390  }
   391  
   392  // Encode is documented at https://golang.org/pkg/encoding/json/#Encoder.Encode
   393  func (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
   421  func (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
   430  func (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.
   436  func (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.
   447  func (enc *Encoder) SetTrustRawMessage(on bool) {
   448  	if on {
   449  		enc.flags |= TrustRawMessage
   450  	} else {
   451  		enc.flags &= ^TrustRawMessage
   452  	}
   453  }
   454  
   455  var encoderBufferPool = sync.Pool{
   456  	New: func() interface{} { return &encoderBuffer{data: make([]byte, 0, 4096)} },
   457  }
   458  
   459  type encoderBuffer struct{ data []byte }