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 }