package json import ( "bytes" "encoding/json" "io" "math/bits" "reflect" "runtime" "sync" "unsafe" ) // Delim is documented at https://golang.org/pkg/encoding/json/#Delim type Delim = json.Delim // InvalidUTF8Error is documented at https://golang.org/pkg/encoding/json/#InvalidUTF8Error type InvalidUTF8Error = json.InvalidUTF8Error // InvalidUnmarshalError is documented at https://golang.org/pkg/encoding/json/#InvalidUnmarshalError type InvalidUnmarshalError = json.InvalidUnmarshalError // Marshaler is documented at https://golang.org/pkg/encoding/json/#Marshaler type Marshaler = json.Marshaler // MarshalerError is documented at https://golang.org/pkg/encoding/json/#MarshalerError type MarshalerError = json.MarshalerError // Number is documented at https://golang.org/pkg/encoding/json/#Number type Number = json.Number // RawMessage is documented at https://golang.org/pkg/encoding/json/#RawMessage type RawMessage = json.RawMessage // A SyntaxError is a description of a JSON syntax error. type SyntaxError = json.SyntaxError // Token is documented at https://golang.org/pkg/encoding/json/#Token type Token = json.Token // UnmarshalFieldError is documented at https://golang.org/pkg/encoding/json/#UnmarshalFieldError type UnmarshalFieldError = json.UnmarshalFieldError // UnmarshalTypeError is documented at https://golang.org/pkg/encoding/json/#UnmarshalTypeError type UnmarshalTypeError = json.UnmarshalTypeError // Unmarshaler is documented at https://golang.org/pkg/encoding/json/#Unmarshaler type Unmarshaler = json.Unmarshaler // UnsupportedTypeError is documented at https://golang.org/pkg/encoding/json/#UnsupportedTypeError type UnsupportedTypeError = json.UnsupportedTypeError // UnsupportedValueError is documented at https://golang.org/pkg/encoding/json/#UnsupportedValueError type UnsupportedValueError = json.UnsupportedValueError // AppendFlags is a type used to represent configuration options that can be // applied when formatting json output. type AppendFlags uint32 const ( // EscapeHTML is a formatting flag used to to escape HTML in json strings. EscapeHTML AppendFlags = 1 << iota // SortMapKeys is formatting flag used to enable sorting of map keys when // encoding JSON (this matches the behavior of the standard encoding/json // package). SortMapKeys // TrustRawMessage is a performance optimization flag to skip value // checking of raw messages. It should only be used if the values are // known to be valid json (e.g., they were created by json.Unmarshal). TrustRawMessage // appendNewline is a formatting flag to enable the addition of a newline // in Encode (this matches the behavior of the standard encoding/json // package). appendNewline ) // ParseFlags is a type used to represent configuration options that can be // applied when parsing json input. type ParseFlags uint32 func (flags ParseFlags) has(f ParseFlags) bool { return (flags & f) != 0 } func (f ParseFlags) kind() Kind { return Kind((f >> kindOffset) & 0xFF) } func (f ParseFlags) withKind(kind Kind) ParseFlags { return (f & ^(ParseFlags(0xFF) << kindOffset)) | (ParseFlags(kind) << kindOffset) } const ( // DisallowUnknownFields is a parsing flag used to prevent decoding of // objects to Go struct values when a field of the input does not match // with any of the struct fields. DisallowUnknownFields ParseFlags = 1 << iota // UseNumber is a parsing flag used to load numeric values as Number // instead of float64. UseNumber // DontCopyString is a parsing flag used to provide zero-copy support when // loading string values from a json payload. It is not always possible to // avoid dynamic memory allocations, for example when a string is escaped in // the json data a new buffer has to be allocated, but when the `wire` value // can be used as content of a Go value the decoder will simply point into // the input buffer. DontCopyString // DontCopyNumber is a parsing flag used to provide zero-copy support when // loading Number values (see DontCopyString and DontCopyRawMessage). DontCopyNumber // DontCopyRawMessage is a parsing flag used to provide zero-copy support // when loading RawMessage values from a json payload. When used, the // RawMessage values will not be allocated into new memory buffers and // will instead point directly to the area of the input buffer where the // value was found. DontCopyRawMessage // DontMatchCaseInsensitiveStructFields is a parsing flag used to prevent // matching fields in a case-insensitive way. This can prevent degrading // performance on case conversions, and can also act as a stricter decoding // mode. DontMatchCaseInsensitiveStructFields // ZeroCopy is a parsing flag that combines all the copy optimizations // available in the package. // // The zero-copy optimizations are better used in request-handler style // code where none of the values are retained after the handler returns. ZeroCopy = DontCopyString | DontCopyNumber | DontCopyRawMessage // validAsciiPrint is an internal flag indicating that the input contains // only valid ASCII print chars (0x20 <= c <= 0x7E). If the flag is unset, // it's unknown whether the input is valid ASCII print. validAsciiPrint ParseFlags = 1 << 28 // noBackslach is an internal flag indicating that the input does not // contain a backslash. If the flag is unset, it's unknown whether the // input contains a backslash. noBackslash ParseFlags = 1 << 29 // Bit offset where the kind of the json value is stored. // // See Kind in token.go for the enum. kindOffset ParseFlags = 16 ) // Kind represents the different kinds of value that exist in JSON. type Kind uint const ( Undefined Kind = 0 Null Kind = 1 // Null is not zero, so we keep zero for "undefined". Bool Kind = 2 // Bit two is set to 1, means it's a boolean. False Kind = 2 // Bool + 0 True Kind = 3 // Bool + 1 Num Kind = 4 // Bit three is set to 1, means it's a number. Uint Kind = 5 // Num + 1 Int Kind = 6 // Num + 2 Float Kind = 7 // Num + 3 String Kind = 8 // Bit four is set to 1, means it's a string. Unescaped Kind = 9 // String + 1 Array Kind = 16 // Equivalent to Delim == '[' Object Kind = 32 // Equivalent to Delim == '{' ) // Class returns the class of k. func (k Kind) Class() Kind { return Kind(1 << uint(bits.Len(uint(k))-1)) } // Append acts like Marshal but appends the json representation to b instead of // always reallocating a new slice. func Append(b []byte, x interface{}, flags AppendFlags) ([]byte, error) { if x == nil { // Special case for nil values because it makes the rest of the code // simpler to assume that it won't be seeing nil pointers. return append(b, "null"...), nil } t := reflect.TypeOf(x) p := (*iface)(unsafe.Pointer(&x)).ptr cache := cacheLoad() c, found := cache[typeid(t)] if !found { c = constructCachedCodec(t, cache) } b, err := c.encode(encoder{flags: flags}, b, p) runtime.KeepAlive(x) return b, err } // Escape is a convenience helper to construct an escaped JSON string from s. // The function escales HTML characters, for more control over the escape // behavior and to write to a pre-allocated buffer, use AppendEscape. func Escape(s string) []byte { // +10 for extra escape characters, maybe not enough and the buffer will // be reallocated. b := make([]byte, 0, len(s)+10) return AppendEscape(b, s, EscapeHTML) } // AppendEscape appends s to b with the string escaped as a JSON value. // This will include the starting and ending quote characters, and the // appropriate characters will be escaped correctly for JSON encoding. func AppendEscape(b []byte, s string, flags AppendFlags) []byte { e := encoder{flags: flags} b, _ = e.encodeString(b, unsafe.Pointer(&s)) return b } // Unescape is a convenience helper to unescape a JSON value. // For more control over the unescape behavior and // to write to a pre-allocated buffer, use AppendUnescape. func Unescape(s []byte) []byte { b := make([]byte, 0, len(s)) return AppendUnescape(b, s, ParseFlags(0)) } // AppendUnescape appends s to b with the string unescaped as a JSON value. // This will remove starting and ending quote characters, and the // appropriate characters will be escaped correctly as if JSON decoded. // New space will be reallocated if more space is needed. func AppendUnescape(b []byte, s []byte, flags ParseFlags) []byte { d := decoder{flags: flags} buf := new(string) d.decodeString(s, unsafe.Pointer(buf)) return append(b, *buf...) } // Compact is documented at https://golang.org/pkg/encoding/json/#Compact func Compact(dst *bytes.Buffer, src []byte) error { return json.Compact(dst, src) } // HTMLEscape is documented at https://golang.org/pkg/encoding/json/#HTMLEscape func HTMLEscape(dst *bytes.Buffer, src []byte) { json.HTMLEscape(dst, src) } // Indent is documented at https://golang.org/pkg/encoding/json/#Indent func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { return json.Indent(dst, src, prefix, indent) } // Marshal is documented at https://golang.org/pkg/encoding/json/#Marshal func Marshal(x interface{}) ([]byte, error) { var err error var buf = encoderBufferPool.Get().(*encoderBuffer) if buf.data, err = Append(buf.data[:0], x, EscapeHTML|SortMapKeys); err != nil { return nil, err } b := make([]byte, len(buf.data)) copy(b, buf.data) encoderBufferPool.Put(buf) return b, nil } // MarshalIndent is documented at https://golang.org/pkg/encoding/json/#MarshalIndent func MarshalIndent(x interface{}, prefix, indent string) ([]byte, error) { b, err := Marshal(x) if err == nil { tmp := &bytes.Buffer{} tmp.Grow(2 * len(b)) Indent(tmp, b, prefix, indent) b = tmp.Bytes() } return b, err } // Unmarshal is documented at https://golang.org/pkg/encoding/json/#Unmarshal func Unmarshal(b []byte, x interface{}) error { r, err := Parse(b, x, 0) if len(r) != 0 { if _, ok := err.(*SyntaxError); !ok { // The encoding/json package prioritizes reporting errors caused by // unexpected trailing bytes over other issues; here we emulate this // behavior by overriding the error. err = syntaxError(r, "invalid character '%c' after top-level value", r[0]) } } return err } // Parse behaves like Unmarshal but the caller can pass a set of flags to // configure the parsing behavior. func Parse(b []byte, x interface{}, flags ParseFlags) ([]byte, error) { t := reflect.TypeOf(x) p := (*iface)(unsafe.Pointer(&x)).ptr d := decoder{flags: flags | internalParseFlags(b)} b = skipSpaces(b) if t == nil || p == nil || t.Kind() != reflect.Ptr { _, r, _, err := d.parseValue(b) r = skipSpaces(r) if err != nil { return r, err } return r, &InvalidUnmarshalError{Type: t} } t = t.Elem() cache := cacheLoad() c, found := cache[typeid(t)] if !found { c = constructCachedCodec(t, cache) } r, err := c.decode(d, b, p) return skipSpaces(r), err } // Valid is documented at https://golang.org/pkg/encoding/json/#Valid func Valid(data []byte) bool { data = skipSpaces(data) d := decoder{flags: internalParseFlags(data)} _, data, _, err := d.parseValue(data) if err != nil { return false } return len(skipSpaces(data)) == 0 } // Decoder is documented at https://golang.org/pkg/encoding/json/#Decoder type Decoder struct { reader io.Reader buffer []byte remain []byte inputOffset int64 err error flags ParseFlags } // NewDecoder is documented at https://golang.org/pkg/encoding/json/#NewDecoder func NewDecoder(r io.Reader) *Decoder { return &Decoder{reader: r} } // Buffered is documented at https://golang.org/pkg/encoding/json/#Decoder.Buffered func (dec *Decoder) Buffered() io.Reader { return bytes.NewReader(dec.remain) } // Decode is documented at https://golang.org/pkg/encoding/json/#Decoder.Decode func (dec *Decoder) Decode(v interface{}) error { raw, err := dec.readValue() if err != nil { return err } _, err = Parse(raw, v, dec.flags) return err } const ( minBufferSize = 32768 minReadSize = 4096 ) // readValue reads one JSON value from the buffer and returns its raw bytes. It // is optimized for the "one JSON value per line" case. func (dec *Decoder) readValue() (v []byte, err error) { var n int var r []byte d := decoder{flags: dec.flags} for { if len(dec.remain) != 0 { v, r, _, err = d.parseValue(dec.remain) if err == nil { dec.remain, n = skipSpacesN(r) dec.inputOffset += int64(len(v) + n) return } if len(r) != 0 { // Parsing of the next JSON value stopped at a position other // than the end of the input buffer, which indicaates that a // syntax error was encountered. return } } if err = dec.err; err != nil { if len(dec.remain) != 0 && err == io.EOF { err = io.ErrUnexpectedEOF } return } if dec.buffer == nil { dec.buffer = make([]byte, 0, minBufferSize) } else { dec.buffer = dec.buffer[:copy(dec.buffer[:cap(dec.buffer)], dec.remain)] dec.remain = nil } if (cap(dec.buffer) - len(dec.buffer)) < minReadSize { buf := make([]byte, len(dec.buffer), 2*cap(dec.buffer)) copy(buf, dec.buffer) dec.buffer = buf } n, err = io.ReadFull(dec.reader, dec.buffer[len(dec.buffer):cap(dec.buffer)]) if n > 0 { dec.buffer = dec.buffer[:len(dec.buffer)+n] if err != nil { err = nil } } else if err == io.ErrUnexpectedEOF { err = io.EOF } dec.remain, n = skipSpacesN(dec.buffer) d.flags = dec.flags | internalParseFlags(dec.remain) dec.inputOffset += int64(n) dec.err = err } } // DisallowUnknownFields is documented at https://golang.org/pkg/encoding/json/#Decoder.DisallowUnknownFields func (dec *Decoder) DisallowUnknownFields() { dec.flags |= DisallowUnknownFields } // UseNumber is documented at https://golang.org/pkg/encoding/json/#Decoder.UseNumber func (dec *Decoder) UseNumber() { dec.flags |= UseNumber } // DontCopyString is an extension to the standard encoding/json package // which instructs the decoder to not copy strings loaded from the json // payloads when possible. func (dec *Decoder) DontCopyString() { dec.flags |= DontCopyString } // DontCopyNumber is an extension to the standard encoding/json package // which instructs the decoder to not copy numbers loaded from the json // payloads. func (dec *Decoder) DontCopyNumber() { dec.flags |= DontCopyNumber } // DontCopyRawMessage is an extension to the standard encoding/json package // which instructs the decoder to not allocate RawMessage values in separate // memory buffers (see the documentation of the DontcopyRawMessage flag for // more detais). func (dec *Decoder) DontCopyRawMessage() { dec.flags |= DontCopyRawMessage } // DontMatchCaseInsensitiveStructFields is an extension to the standard // encoding/json package which instructs the decoder to not match object fields // against struct fields in a case-insensitive way, the field names have to // match exactly to be decoded into the struct field values. func (dec *Decoder) DontMatchCaseInsensitiveStructFields() { dec.flags |= DontMatchCaseInsensitiveStructFields } // ZeroCopy is an extension to the standard encoding/json package which enables // all the copy optimizations of the decoder. func (dec *Decoder) ZeroCopy() { dec.flags |= ZeroCopy } // InputOffset returns the input stream byte offset of the current decoder position. // The offset gives the location of the end of the most recently returned token // and the beginning of the next token. func (dec *Decoder) InputOffset() int64 { return dec.inputOffset } // Encoder is documented at https://golang.org/pkg/encoding/json/#Encoder type Encoder struct { writer io.Writer prefix string indent string buffer *bytes.Buffer err error flags AppendFlags } // NewEncoder is documented at https://golang.org/pkg/encoding/json/#NewEncoder func NewEncoder(w io.Writer) *Encoder { return &Encoder{writer: w, flags: EscapeHTML | SortMapKeys | appendNewline} } // Encode is documented at https://golang.org/pkg/encoding/json/#Encoder.Encode func (enc *Encoder) Encode(v interface{}) error { if enc.err != nil { return enc.err } var err error var buf = encoderBufferPool.Get().(*encoderBuffer) buf.data, err = Append(buf.data[:0], v, enc.flags) if err != nil { encoderBufferPool.Put(buf) return err } if (enc.flags & appendNewline) != 0 { buf.data = append(buf.data, '\n') } b := buf.data if enc.prefix != "" || enc.indent != "" { if enc.buffer == nil { enc.buffer = new(bytes.Buffer) enc.buffer.Grow(2 * len(buf.data)) } else { enc.buffer.Reset() } Indent(enc.buffer, buf.data, enc.prefix, enc.indent) b = enc.buffer.Bytes() } if _, err := enc.writer.Write(b); err != nil { enc.err = err } encoderBufferPool.Put(buf) return err } // SetEscapeHTML is documented at https://golang.org/pkg/encoding/json/#Encoder.SetEscapeHTML func (enc *Encoder) SetEscapeHTML(on bool) { if on { enc.flags |= EscapeHTML } else { enc.flags &= ^EscapeHTML } } // SetIndent is documented at https://golang.org/pkg/encoding/json/#Encoder.SetIndent func (enc *Encoder) SetIndent(prefix, indent string) { enc.prefix = prefix enc.indent = indent } // SetSortMapKeys is an extension to the standard encoding/json package which // allows the program to toggle sorting of map keys on and off. func (enc *Encoder) SetSortMapKeys(on bool) { if on { enc.flags |= SortMapKeys } else { enc.flags &= ^SortMapKeys } } // SetTrustRawMessage skips value checking when encoding a raw json message. It should only // be used if the values are known to be valid json, e.g. because they were originally created // by json.Unmarshal. func (enc *Encoder) SetTrustRawMessage(on bool) { if on { enc.flags |= TrustRawMessage } else { enc.flags &= ^TrustRawMessage } } // SetAppendNewline is an extension to the standard encoding/json package which // allows the program to toggle the addition of a newline in Encode on or off. func (enc *Encoder) SetAppendNewline(on bool) { if on { enc.flags |= appendNewline } else { enc.flags &= ^appendNewline } } var encoderBufferPool = sync.Pool{ New: func() interface{} { return &encoderBuffer{data: make([]byte, 0, 4096)} }, } type encoderBuffer struct{ data []byte }