github.com/neilotoole/jsoncolor@v0.7.2-0.20231115150201-1637fae69be1/json.go (about)

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