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 }