1233 lines
33 KiB
Go
1233 lines
33 KiB
Go
|
package json
|
||
|
|
||
|
import (
|
||
|
"encoding"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"sort"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"sync/atomic"
|
||
|
"time"
|
||
|
"unicode"
|
||
|
"unsafe"
|
||
|
|
||
|
"github.com/segmentio/asm/keyset"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// 1000 is the value used by the standard encoding/json package.
|
||
|
//
|
||
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.17.3:src/encoding/json/encode.go;drc=refs%2Ftags%2Fgo1.17.3;l=300
|
||
|
startDetectingCyclesAfter = 1000
|
||
|
)
|
||
|
|
||
|
type codec struct {
|
||
|
encode encodeFunc
|
||
|
decode decodeFunc
|
||
|
}
|
||
|
|
||
|
type encoder struct {
|
||
|
flags AppendFlags
|
||
|
// ptrDepth tracks the depth of pointer cycles, when it reaches the value
|
||
|
// of startDetectingCyclesAfter, the ptrSeen map is allocated and the
|
||
|
// encoder starts tracking pointers it has seen as an attempt to detect
|
||
|
// whether it has entered a pointer cycle and needs to error before the
|
||
|
// goroutine runs out of stack space.
|
||
|
ptrDepth uint32
|
||
|
ptrSeen map[unsafe.Pointer]struct{}
|
||
|
}
|
||
|
|
||
|
type decoder struct {
|
||
|
flags ParseFlags
|
||
|
}
|
||
|
|
||
|
type encodeFunc func(encoder, []byte, unsafe.Pointer) ([]byte, error)
|
||
|
type decodeFunc func(decoder, []byte, unsafe.Pointer) ([]byte, error)
|
||
|
|
||
|
type emptyFunc func(unsafe.Pointer) bool
|
||
|
type sortFunc func([]reflect.Value)
|
||
|
|
||
|
var (
|
||
|
// Eventually consistent cache mapping go types to dynamically generated
|
||
|
// codecs.
|
||
|
//
|
||
|
// Note: using a uintptr as key instead of reflect.Type shaved ~15ns off of
|
||
|
// the ~30ns Marhsal/Unmarshal functions which were dominated by the map
|
||
|
// lookup time for simple types like bool, int, etc..
|
||
|
cache unsafe.Pointer // map[unsafe.Pointer]codec
|
||
|
)
|
||
|
|
||
|
func cacheLoad() map[unsafe.Pointer]codec {
|
||
|
p := atomic.LoadPointer(&cache)
|
||
|
return *(*map[unsafe.Pointer]codec)(unsafe.Pointer(&p))
|
||
|
}
|
||
|
|
||
|
func cacheStore(typ reflect.Type, cod codec, oldCodecs map[unsafe.Pointer]codec) {
|
||
|
newCodecs := make(map[unsafe.Pointer]codec, len(oldCodecs)+1)
|
||
|
newCodecs[typeid(typ)] = cod
|
||
|
|
||
|
for t, c := range oldCodecs {
|
||
|
newCodecs[t] = c
|
||
|
}
|
||
|
|
||
|
atomic.StorePointer(&cache, *(*unsafe.Pointer)(unsafe.Pointer(&newCodecs)))
|
||
|
}
|
||
|
|
||
|
func typeid(t reflect.Type) unsafe.Pointer {
|
||
|
return (*iface)(unsafe.Pointer(&t)).ptr
|
||
|
}
|
||
|
|
||
|
func constructCachedCodec(t reflect.Type, cache map[unsafe.Pointer]codec) codec {
|
||
|
c := constructCodec(t, map[reflect.Type]*structType{}, t.Kind() == reflect.Ptr)
|
||
|
|
||
|
if inlined(t) {
|
||
|
c.encode = constructInlineValueEncodeFunc(c.encode)
|
||
|
}
|
||
|
|
||
|
cacheStore(t, c, cache)
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
func constructCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) (c codec) {
|
||
|
switch t {
|
||
|
case nullType, nil:
|
||
|
c = codec{encode: encoder.encodeNull, decode: decoder.decodeNull}
|
||
|
|
||
|
case numberType:
|
||
|
c = codec{encode: encoder.encodeNumber, decode: decoder.decodeNumber}
|
||
|
|
||
|
case bytesType:
|
||
|
c = codec{encode: encoder.encodeBytes, decode: decoder.decodeBytes}
|
||
|
|
||
|
case durationType:
|
||
|
c = codec{encode: encoder.encodeDuration, decode: decoder.decodeDuration}
|
||
|
|
||
|
case timeType:
|
||
|
c = codec{encode: encoder.encodeTime, decode: decoder.decodeTime}
|
||
|
|
||
|
case interfaceType:
|
||
|
c = codec{encode: encoder.encodeInterface, decode: decoder.decodeInterface}
|
||
|
|
||
|
case rawMessageType:
|
||
|
c = codec{encode: encoder.encodeRawMessage, decode: decoder.decodeRawMessage}
|
||
|
|
||
|
case numberPtrType:
|
||
|
c = constructPointerCodec(numberPtrType, nil)
|
||
|
|
||
|
case durationPtrType:
|
||
|
c = constructPointerCodec(durationPtrType, nil)
|
||
|
|
||
|
case timePtrType:
|
||
|
c = constructPointerCodec(timePtrType, nil)
|
||
|
|
||
|
case rawMessagePtrType:
|
||
|
c = constructPointerCodec(rawMessagePtrType, nil)
|
||
|
}
|
||
|
|
||
|
if c.encode != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
switch t.Kind() {
|
||
|
case reflect.Bool:
|
||
|
c = codec{encode: encoder.encodeBool, decode: decoder.decodeBool}
|
||
|
|
||
|
case reflect.Int:
|
||
|
c = codec{encode: encoder.encodeInt, decode: decoder.decodeInt}
|
||
|
|
||
|
case reflect.Int8:
|
||
|
c = codec{encode: encoder.encodeInt8, decode: decoder.decodeInt8}
|
||
|
|
||
|
case reflect.Int16:
|
||
|
c = codec{encode: encoder.encodeInt16, decode: decoder.decodeInt16}
|
||
|
|
||
|
case reflect.Int32:
|
||
|
c = codec{encode: encoder.encodeInt32, decode: decoder.decodeInt32}
|
||
|
|
||
|
case reflect.Int64:
|
||
|
c = codec{encode: encoder.encodeInt64, decode: decoder.decodeInt64}
|
||
|
|
||
|
case reflect.Uint:
|
||
|
c = codec{encode: encoder.encodeUint, decode: decoder.decodeUint}
|
||
|
|
||
|
case reflect.Uintptr:
|
||
|
c = codec{encode: encoder.encodeUintptr, decode: decoder.decodeUintptr}
|
||
|
|
||
|
case reflect.Uint8:
|
||
|
c = codec{encode: encoder.encodeUint8, decode: decoder.decodeUint8}
|
||
|
|
||
|
case reflect.Uint16:
|
||
|
c = codec{encode: encoder.encodeUint16, decode: decoder.decodeUint16}
|
||
|
|
||
|
case reflect.Uint32:
|
||
|
c = codec{encode: encoder.encodeUint32, decode: decoder.decodeUint32}
|
||
|
|
||
|
case reflect.Uint64:
|
||
|
c = codec{encode: encoder.encodeUint64, decode: decoder.decodeUint64}
|
||
|
|
||
|
case reflect.Float32:
|
||
|
c = codec{encode: encoder.encodeFloat32, decode: decoder.decodeFloat32}
|
||
|
|
||
|
case reflect.Float64:
|
||
|
c = codec{encode: encoder.encodeFloat64, decode: decoder.decodeFloat64}
|
||
|
|
||
|
case reflect.String:
|
||
|
c = codec{encode: encoder.encodeString, decode: decoder.decodeString}
|
||
|
|
||
|
case reflect.Interface:
|
||
|
c = constructInterfaceCodec(t)
|
||
|
|
||
|
case reflect.Array:
|
||
|
c = constructArrayCodec(t, seen, canAddr)
|
||
|
|
||
|
case reflect.Slice:
|
||
|
c = constructSliceCodec(t, seen)
|
||
|
|
||
|
case reflect.Map:
|
||
|
c = constructMapCodec(t, seen)
|
||
|
|
||
|
case reflect.Struct:
|
||
|
c = constructStructCodec(t, seen, canAddr)
|
||
|
|
||
|
case reflect.Ptr:
|
||
|
c = constructPointerCodec(t, seen)
|
||
|
|
||
|
default:
|
||
|
c = constructUnsupportedTypeCodec(t)
|
||
|
}
|
||
|
|
||
|
p := reflect.PtrTo(t)
|
||
|
|
||
|
if canAddr {
|
||
|
switch {
|
||
|
case p.Implements(jsonMarshalerType):
|
||
|
c.encode = constructJSONMarshalerEncodeFunc(t, true)
|
||
|
case p.Implements(textMarshalerType):
|
||
|
c.encode = constructTextMarshalerEncodeFunc(t, true)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case t.Implements(jsonMarshalerType):
|
||
|
c.encode = constructJSONMarshalerEncodeFunc(t, false)
|
||
|
case t.Implements(textMarshalerType):
|
||
|
c.encode = constructTextMarshalerEncodeFunc(t, false)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case p.Implements(jsonUnmarshalerType):
|
||
|
c.decode = constructJSONUnmarshalerDecodeFunc(t, true)
|
||
|
case p.Implements(textUnmarshalerType):
|
||
|
c.decode = constructTextUnmarshalerDecodeFunc(t, true)
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func constructStringCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) codec {
|
||
|
c := constructCodec(t, seen, canAddr)
|
||
|
return codec{
|
||
|
encode: constructStringEncodeFunc(c.encode),
|
||
|
decode: constructStringDecodeFunc(c.decode),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructStringEncodeFunc(encode encodeFunc) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeToString(b, p, encode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructStringDecodeFunc(decode decodeFunc) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeFromString(b, p, decode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructStringToIntDecodeFunc(t reflect.Type, decode decodeFunc) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeFromStringToInt(b, p, t, decode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructArrayCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) codec {
|
||
|
e := t.Elem()
|
||
|
c := constructCodec(e, seen, canAddr)
|
||
|
s := alignedSize(e)
|
||
|
return codec{
|
||
|
encode: constructArrayEncodeFunc(s, t, c.encode),
|
||
|
decode: constructArrayDecodeFunc(s, t, c.decode),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructArrayEncodeFunc(size uintptr, t reflect.Type, encode encodeFunc) encodeFunc {
|
||
|
n := t.Len()
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeArray(b, p, n, size, t, encode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructArrayDecodeFunc(size uintptr, t reflect.Type, decode decodeFunc) decodeFunc {
|
||
|
n := t.Len()
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeArray(b, p, n, size, t, decode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructSliceCodec(t reflect.Type, seen map[reflect.Type]*structType) codec {
|
||
|
e := t.Elem()
|
||
|
s := alignedSize(e)
|
||
|
|
||
|
if e.Kind() == reflect.Uint8 {
|
||
|
// Go 1.7+ behavior: slices of byte types (and aliases) may override the
|
||
|
// default encoding and decoding behaviors by implementing marshaler and
|
||
|
// unmarshaler interfaces.
|
||
|
p := reflect.PtrTo(e)
|
||
|
c := codec{}
|
||
|
|
||
|
switch {
|
||
|
case e.Implements(jsonMarshalerType):
|
||
|
c.encode = constructJSONMarshalerEncodeFunc(e, false)
|
||
|
case e.Implements(textMarshalerType):
|
||
|
c.encode = constructTextMarshalerEncodeFunc(e, false)
|
||
|
case p.Implements(jsonMarshalerType):
|
||
|
c.encode = constructJSONMarshalerEncodeFunc(e, true)
|
||
|
case p.Implements(textMarshalerType):
|
||
|
c.encode = constructTextMarshalerEncodeFunc(e, true)
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case e.Implements(jsonUnmarshalerType):
|
||
|
c.decode = constructJSONUnmarshalerDecodeFunc(e, false)
|
||
|
case e.Implements(textUnmarshalerType):
|
||
|
c.decode = constructTextUnmarshalerDecodeFunc(e, false)
|
||
|
case p.Implements(jsonUnmarshalerType):
|
||
|
c.decode = constructJSONUnmarshalerDecodeFunc(e, true)
|
||
|
case p.Implements(textUnmarshalerType):
|
||
|
c.decode = constructTextUnmarshalerDecodeFunc(e, true)
|
||
|
}
|
||
|
|
||
|
if c.encode != nil {
|
||
|
c.encode = constructSliceEncodeFunc(s, t, c.encode)
|
||
|
} else {
|
||
|
c.encode = encoder.encodeBytes
|
||
|
}
|
||
|
|
||
|
if c.decode != nil {
|
||
|
c.decode = constructSliceDecodeFunc(s, t, c.decode)
|
||
|
} else {
|
||
|
c.decode = decoder.decodeBytes
|
||
|
}
|
||
|
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
c := constructCodec(e, seen, true)
|
||
|
return codec{
|
||
|
encode: constructSliceEncodeFunc(s, t, c.encode),
|
||
|
decode: constructSliceDecodeFunc(s, t, c.decode),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructSliceEncodeFunc(size uintptr, t reflect.Type, encode encodeFunc) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeSlice(b, p, size, t, encode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructSliceDecodeFunc(size uintptr, t reflect.Type, decode decodeFunc) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeSlice(b, p, size, t, decode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructMapCodec(t reflect.Type, seen map[reflect.Type]*structType) codec {
|
||
|
var sortKeys sortFunc
|
||
|
k := t.Key()
|
||
|
v := t.Elem()
|
||
|
|
||
|
// Faster implementations for some common cases.
|
||
|
switch {
|
||
|
case k == stringType && v == interfaceType:
|
||
|
return codec{
|
||
|
encode: encoder.encodeMapStringInterface,
|
||
|
decode: decoder.decodeMapStringInterface,
|
||
|
}
|
||
|
|
||
|
case k == stringType && v == rawMessageType:
|
||
|
return codec{
|
||
|
encode: encoder.encodeMapStringRawMessage,
|
||
|
decode: decoder.decodeMapStringRawMessage,
|
||
|
}
|
||
|
|
||
|
case k == stringType && v == stringType:
|
||
|
return codec{
|
||
|
encode: encoder.encodeMapStringString,
|
||
|
decode: decoder.decodeMapStringString,
|
||
|
}
|
||
|
|
||
|
case k == stringType && v == stringsType:
|
||
|
return codec{
|
||
|
encode: encoder.encodeMapStringStringSlice,
|
||
|
decode: decoder.decodeMapStringStringSlice,
|
||
|
}
|
||
|
|
||
|
case k == stringType && v == boolType:
|
||
|
return codec{
|
||
|
encode: encoder.encodeMapStringBool,
|
||
|
decode: decoder.decodeMapStringBool,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
kc := codec{}
|
||
|
vc := constructCodec(v, seen, false)
|
||
|
|
||
|
if k.Implements(textMarshalerType) || reflect.PtrTo(k).Implements(textUnmarshalerType) {
|
||
|
kc.encode = constructTextMarshalerEncodeFunc(k, false)
|
||
|
kc.decode = constructTextUnmarshalerDecodeFunc(k, true)
|
||
|
|
||
|
sortKeys = func(keys []reflect.Value) {
|
||
|
sort.Slice(keys, func(i, j int) bool {
|
||
|
// This is a performance abomination but the use case is rare
|
||
|
// enough that it shouldn't be a problem in practice.
|
||
|
k1, _ := keys[i].Interface().(encoding.TextMarshaler).MarshalText()
|
||
|
k2, _ := keys[j].Interface().(encoding.TextMarshaler).MarshalText()
|
||
|
return string(k1) < string(k2)
|
||
|
})
|
||
|
}
|
||
|
} else {
|
||
|
switch k.Kind() {
|
||
|
case reflect.String:
|
||
|
kc.encode = encoder.encodeString
|
||
|
kc.decode = decoder.decodeString
|
||
|
|
||
|
sortKeys = func(keys []reflect.Value) {
|
||
|
sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() })
|
||
|
}
|
||
|
|
||
|
case reflect.Int,
|
||
|
reflect.Int8,
|
||
|
reflect.Int16,
|
||
|
reflect.Int32,
|
||
|
reflect.Int64:
|
||
|
kc = constructStringCodec(k, seen, false)
|
||
|
|
||
|
sortKeys = func(keys []reflect.Value) {
|
||
|
sort.Slice(keys, func(i, j int) bool { return intStringsAreSorted(keys[i].Int(), keys[j].Int()) })
|
||
|
}
|
||
|
|
||
|
case reflect.Uint,
|
||
|
reflect.Uintptr,
|
||
|
reflect.Uint8,
|
||
|
reflect.Uint16,
|
||
|
reflect.Uint32,
|
||
|
reflect.Uint64:
|
||
|
kc = constructStringCodec(k, seen, false)
|
||
|
|
||
|
sortKeys = func(keys []reflect.Value) {
|
||
|
sort.Slice(keys, func(i, j int) bool { return uintStringsAreSorted(keys[i].Uint(), keys[j].Uint()) })
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return constructUnsupportedTypeCodec(t)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if inlined(v) {
|
||
|
vc.encode = constructInlineValueEncodeFunc(vc.encode)
|
||
|
}
|
||
|
|
||
|
return codec{
|
||
|
encode: constructMapEncodeFunc(t, kc.encode, vc.encode, sortKeys),
|
||
|
decode: constructMapDecodeFunc(t, kc.decode, vc.decode),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructMapEncodeFunc(t reflect.Type, encodeKey, encodeValue encodeFunc, sortKeys sortFunc) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeMap(b, p, t, encodeKey, encodeValue, sortKeys)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructMapDecodeFunc(t reflect.Type, decodeKey, decodeValue decodeFunc) decodeFunc {
|
||
|
kt := t.Key()
|
||
|
vt := t.Elem()
|
||
|
kz := reflect.Zero(kt)
|
||
|
vz := reflect.Zero(vt)
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeMap(b, p, t, kt, vt, kz, vz, decodeKey, decodeValue)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructStructCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) codec {
|
||
|
st := constructStructType(t, seen, canAddr)
|
||
|
return codec{
|
||
|
encode: constructStructEncodeFunc(st),
|
||
|
decode: constructStructDecodeFunc(st),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructStructType(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) *structType {
|
||
|
// Used for preventing infinite recursion on types that have pointers to
|
||
|
// themselves.
|
||
|
st := seen[t]
|
||
|
|
||
|
if st == nil {
|
||
|
st = &structType{
|
||
|
fields: make([]structField, 0, t.NumField()),
|
||
|
fieldsIndex: make(map[string]*structField),
|
||
|
ficaseIndex: make(map[string]*structField),
|
||
|
typ: t,
|
||
|
}
|
||
|
|
||
|
seen[t] = st
|
||
|
st.fields = appendStructFields(st.fields, t, 0, seen, canAddr)
|
||
|
|
||
|
for i := range st.fields {
|
||
|
f := &st.fields[i]
|
||
|
s := strings.ToLower(f.name)
|
||
|
st.fieldsIndex[f.name] = f
|
||
|
// When there is ambiguity because multiple fields have the same
|
||
|
// case-insensitive representation, the first field must win.
|
||
|
if _, exists := st.ficaseIndex[s]; !exists {
|
||
|
st.ficaseIndex[s] = f
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// At a certain point the linear scan provided by keyset is less
|
||
|
// efficient than a map. The 32 was chosen based on benchmarks in the
|
||
|
// segmentio/asm repo run with an Intel Kaby Lake processor and go1.17.
|
||
|
if len(st.fields) <= 32 {
|
||
|
keys := make([][]byte, len(st.fields))
|
||
|
for i, f := range st.fields {
|
||
|
keys[i] = []byte(f.name)
|
||
|
}
|
||
|
st.keyset = keyset.New(keys)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return st
|
||
|
}
|
||
|
|
||
|
func constructStructEncodeFunc(st *structType) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeStruct(b, p, st)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructStructDecodeFunc(st *structType) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeStruct(b, p, st)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructEmbeddedStructPointerCodec(t reflect.Type, unexported bool, offset uintptr, field codec) codec {
|
||
|
return codec{
|
||
|
encode: constructEmbeddedStructPointerEncodeFunc(t, unexported, offset, field.encode),
|
||
|
decode: constructEmbeddedStructPointerDecodeFunc(t, unexported, offset, field.decode),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructEmbeddedStructPointerEncodeFunc(t reflect.Type, unexported bool, offset uintptr, encode encodeFunc) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeEmbeddedStructPointer(b, p, t, unexported, offset, encode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructEmbeddedStructPointerDecodeFunc(t reflect.Type, unexported bool, offset uintptr, decode decodeFunc) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeEmbeddedStructPointer(b, p, t, unexported, offset, decode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func appendStructFields(fields []structField, t reflect.Type, offset uintptr, seen map[reflect.Type]*structType, canAddr bool) []structField {
|
||
|
type embeddedField struct {
|
||
|
index int
|
||
|
offset uintptr
|
||
|
pointer bool
|
||
|
unexported bool
|
||
|
subtype *structType
|
||
|
subfield *structField
|
||
|
}
|
||
|
|
||
|
names := make(map[string]struct{})
|
||
|
embedded := make([]embeddedField, 0, 10)
|
||
|
|
||
|
for i, n := 0, t.NumField(); i < n; i++ {
|
||
|
f := t.Field(i)
|
||
|
|
||
|
var (
|
||
|
name = f.Name
|
||
|
anonymous = f.Anonymous
|
||
|
tag = false
|
||
|
omitempty = false
|
||
|
stringify = false
|
||
|
unexported = len(f.PkgPath) != 0
|
||
|
)
|
||
|
|
||
|
if unexported && !anonymous { // unexported
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if parts := strings.Split(f.Tag.Get("json"), ","); len(parts) != 0 {
|
||
|
if len(parts[0]) != 0 {
|
||
|
name, tag = parts[0], true
|
||
|
}
|
||
|
|
||
|
if name == "-" && len(parts) == 1 { // ignored
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if !isValidTag(name) {
|
||
|
name = f.Name
|
||
|
}
|
||
|
|
||
|
for _, tag := range parts[1:] {
|
||
|
switch tag {
|
||
|
case "omitempty":
|
||
|
omitempty = true
|
||
|
case "string":
|
||
|
stringify = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if anonymous && !tag { // embedded
|
||
|
typ := f.Type
|
||
|
ptr := f.Type.Kind() == reflect.Ptr
|
||
|
|
||
|
if ptr {
|
||
|
typ = f.Type.Elem()
|
||
|
}
|
||
|
|
||
|
if typ.Kind() == reflect.Struct {
|
||
|
// When the embedded fields is inlined the fields can be looked
|
||
|
// up by offset from the address of the wrapping object, so we
|
||
|
// simply add the embedded struct fields to the list of fields
|
||
|
// of the current struct type.
|
||
|
subtype := constructStructType(typ, seen, canAddr)
|
||
|
|
||
|
for j := range subtype.fields {
|
||
|
embedded = append(embedded, embeddedField{
|
||
|
index: i<<32 | j,
|
||
|
offset: offset + f.Offset,
|
||
|
pointer: ptr,
|
||
|
unexported: unexported,
|
||
|
subtype: subtype,
|
||
|
subfield: &subtype.fields[j],
|
||
|
})
|
||
|
}
|
||
|
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if unexported { // ignore unexported non-struct types
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
codec := constructCodec(f.Type, seen, canAddr)
|
||
|
|
||
|
if stringify {
|
||
|
// https://golang.org/pkg/encoding/json/#Marshal
|
||
|
//
|
||
|
// The "string" option signals that a field is stored as JSON inside
|
||
|
// a JSON-encoded string. It applies only to fields of string,
|
||
|
// floating point, integer, or boolean types. This extra level of
|
||
|
// encoding is sometimes used when communicating with JavaScript
|
||
|
// programs:
|
||
|
typ := f.Type
|
||
|
|
||
|
if typ.Kind() == reflect.Ptr {
|
||
|
typ = typ.Elem()
|
||
|
}
|
||
|
|
||
|
switch typ.Kind() {
|
||
|
case reflect.Int,
|
||
|
reflect.Int8,
|
||
|
reflect.Int16,
|
||
|
reflect.Int32,
|
||
|
reflect.Int64,
|
||
|
reflect.Uint,
|
||
|
reflect.Uintptr,
|
||
|
reflect.Uint8,
|
||
|
reflect.Uint16,
|
||
|
reflect.Uint32,
|
||
|
reflect.Uint64:
|
||
|
codec.encode = constructStringEncodeFunc(codec.encode)
|
||
|
codec.decode = constructStringToIntDecodeFunc(typ, codec.decode)
|
||
|
case reflect.Bool,
|
||
|
reflect.Float32,
|
||
|
reflect.Float64,
|
||
|
reflect.String:
|
||
|
codec.encode = constructStringEncodeFunc(codec.encode)
|
||
|
codec.decode = constructStringDecodeFunc(codec.decode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fields = append(fields, structField{
|
||
|
codec: codec,
|
||
|
offset: offset + f.Offset,
|
||
|
empty: emptyFuncOf(f.Type),
|
||
|
tag: tag,
|
||
|
omitempty: omitempty,
|
||
|
name: name,
|
||
|
index: i << 32,
|
||
|
typ: f.Type,
|
||
|
zero: reflect.Zero(f.Type),
|
||
|
})
|
||
|
|
||
|
names[name] = struct{}{}
|
||
|
}
|
||
|
|
||
|
// Only unambiguous embedded fields must be serialized.
|
||
|
ambiguousNames := make(map[string]int)
|
||
|
ambiguousTags := make(map[string]int)
|
||
|
|
||
|
// Embedded types can never override a field that was already present at
|
||
|
// the top-level.
|
||
|
for name := range names {
|
||
|
ambiguousNames[name]++
|
||
|
ambiguousTags[name]++
|
||
|
}
|
||
|
|
||
|
for _, embfield := range embedded {
|
||
|
ambiguousNames[embfield.subfield.name]++
|
||
|
if embfield.subfield.tag {
|
||
|
ambiguousTags[embfield.subfield.name]++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, embfield := range embedded {
|
||
|
subfield := *embfield.subfield
|
||
|
|
||
|
if ambiguousNames[subfield.name] > 1 && !(subfield.tag && ambiguousTags[subfield.name] == 1) {
|
||
|
continue // ambiguous embedded field
|
||
|
}
|
||
|
|
||
|
if embfield.pointer {
|
||
|
subfield.codec = constructEmbeddedStructPointerCodec(embfield.subtype.typ, embfield.unexported, subfield.offset, subfield.codec)
|
||
|
subfield.offset = embfield.offset
|
||
|
} else {
|
||
|
subfield.offset += embfield.offset
|
||
|
}
|
||
|
|
||
|
// To prevent dominant flags more than one level below the embedded one.
|
||
|
subfield.tag = false
|
||
|
|
||
|
// To ensure the order of the fields in the output is the same is in the
|
||
|
// struct type.
|
||
|
subfield.index = embfield.index
|
||
|
|
||
|
fields = append(fields, subfield)
|
||
|
}
|
||
|
|
||
|
for i := range fields {
|
||
|
name := fields[i].name
|
||
|
fields[i].json = encodeKeyFragment(name, 0)
|
||
|
fields[i].html = encodeKeyFragment(name, EscapeHTML)
|
||
|
}
|
||
|
|
||
|
sort.Slice(fields, func(i, j int) bool { return fields[i].index < fields[j].index })
|
||
|
return fields
|
||
|
}
|
||
|
|
||
|
func encodeKeyFragment(s string, flags AppendFlags) string {
|
||
|
b := make([]byte, 1, len(s)+4)
|
||
|
b[0] = ','
|
||
|
e := encoder{flags: flags}
|
||
|
b, _ = e.encodeString(b, unsafe.Pointer(&s))
|
||
|
b = append(b, ':')
|
||
|
return *(*string)(unsafe.Pointer(&b))
|
||
|
}
|
||
|
|
||
|
func constructPointerCodec(t reflect.Type, seen map[reflect.Type]*structType) codec {
|
||
|
e := t.Elem()
|
||
|
c := constructCodec(e, seen, true)
|
||
|
return codec{
|
||
|
encode: constructPointerEncodeFunc(e, c.encode),
|
||
|
decode: constructPointerDecodeFunc(e, c.decode),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructPointerEncodeFunc(t reflect.Type, encode encodeFunc) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodePointer(b, p, t, encode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructPointerDecodeFunc(t reflect.Type, decode decodeFunc) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodePointer(b, p, t, decode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructInterfaceCodec(t reflect.Type) codec {
|
||
|
return codec{
|
||
|
encode: constructMaybeEmptyInterfaceEncoderFunc(t),
|
||
|
decode: constructMaybeEmptyInterfaceDecoderFunc(t),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructMaybeEmptyInterfaceEncoderFunc(t reflect.Type) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeMaybeEmptyInterface(b, p, t)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructMaybeEmptyInterfaceDecoderFunc(t reflect.Type) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeMaybeEmptyInterface(b, p, t)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructUnsupportedTypeCodec(t reflect.Type) codec {
|
||
|
return codec{
|
||
|
encode: constructUnsupportedTypeEncodeFunc(t),
|
||
|
decode: constructUnsupportedTypeDecodeFunc(t),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructUnsupportedTypeEncodeFunc(t reflect.Type) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeUnsupportedTypeError(b, p, t)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructUnsupportedTypeDecodeFunc(t reflect.Type) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeUnmarshalTypeError(b, p, t)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructJSONMarshalerEncodeFunc(t reflect.Type, pointer bool) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeJSONMarshaler(b, p, t, pointer)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructJSONUnmarshalerDecodeFunc(t reflect.Type, pointer bool) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeJSONUnmarshaler(b, p, t, pointer)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructTextMarshalerEncodeFunc(t reflect.Type, pointer bool) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return e.encodeTextMarshaler(b, p, t, pointer)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructTextUnmarshalerDecodeFunc(t reflect.Type, pointer bool) decodeFunc {
|
||
|
return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return d.decodeTextUnmarshaler(b, p, t, pointer)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func constructInlineValueEncodeFunc(encode encodeFunc) encodeFunc {
|
||
|
return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
|
||
|
return encode(e, b, noescape(unsafe.Pointer(&p)))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// noescape hides a pointer from escape analysis. noescape is
|
||
|
// the identity function but escape analysis doesn't think the
|
||
|
// output depends on the input. noescape is inlined and currently
|
||
|
// compiles down to zero instructions.
|
||
|
// USE CAREFULLY!
|
||
|
// This was copied from the runtime; see issues 23382 and 7921.
|
||
|
//go:nosplit
|
||
|
func noescape(p unsafe.Pointer) unsafe.Pointer {
|
||
|
x := uintptr(p)
|
||
|
return unsafe.Pointer(x ^ 0)
|
||
|
}
|
||
|
|
||
|
func alignedSize(t reflect.Type) uintptr {
|
||
|
a := t.Align()
|
||
|
s := t.Size()
|
||
|
return align(uintptr(a), uintptr(s))
|
||
|
}
|
||
|
|
||
|
func align(align, size uintptr) uintptr {
|
||
|
if align != 0 && (size%align) != 0 {
|
||
|
size = ((size / align) + 1) * align
|
||
|
}
|
||
|
return size
|
||
|
}
|
||
|
|
||
|
func inlined(t reflect.Type) bool {
|
||
|
switch t.Kind() {
|
||
|
case reflect.Ptr:
|
||
|
return true
|
||
|
case reflect.Map:
|
||
|
return true
|
||
|
case reflect.Struct:
|
||
|
return t.NumField() == 1 && inlined(t.Field(0).Type)
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func isValidTag(s string) bool {
|
||
|
if s == "" {
|
||
|
return false
|
||
|
}
|
||
|
for _, c := range s {
|
||
|
switch {
|
||
|
case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
|
||
|
// Backslash and quote chars are reserved, but
|
||
|
// otherwise any punctuation chars are allowed
|
||
|
// in a tag name.
|
||
|
default:
|
||
|
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func emptyFuncOf(t reflect.Type) emptyFunc {
|
||
|
switch t {
|
||
|
case bytesType, rawMessageType:
|
||
|
return func(p unsafe.Pointer) bool { return (*slice)(p).len == 0 }
|
||
|
}
|
||
|
|
||
|
switch t.Kind() {
|
||
|
case reflect.Array:
|
||
|
if t.Len() == 0 {
|
||
|
return func(unsafe.Pointer) bool { return true }
|
||
|
}
|
||
|
|
||
|
case reflect.Map:
|
||
|
return func(p unsafe.Pointer) bool { return reflect.NewAt(t, p).Elem().Len() == 0 }
|
||
|
|
||
|
case reflect.Slice:
|
||
|
return func(p unsafe.Pointer) bool { return (*slice)(p).len == 0 }
|
||
|
|
||
|
case reflect.String:
|
||
|
return func(p unsafe.Pointer) bool { return len(*(*string)(p)) == 0 }
|
||
|
|
||
|
case reflect.Bool:
|
||
|
return func(p unsafe.Pointer) bool { return !*(*bool)(p) }
|
||
|
|
||
|
case reflect.Int, reflect.Uint:
|
||
|
return func(p unsafe.Pointer) bool { return *(*uint)(p) == 0 }
|
||
|
|
||
|
case reflect.Uintptr:
|
||
|
return func(p unsafe.Pointer) bool { return *(*uintptr)(p) == 0 }
|
||
|
|
||
|
case reflect.Int8, reflect.Uint8:
|
||
|
return func(p unsafe.Pointer) bool { return *(*uint8)(p) == 0 }
|
||
|
|
||
|
case reflect.Int16, reflect.Uint16:
|
||
|
return func(p unsafe.Pointer) bool { return *(*uint16)(p) == 0 }
|
||
|
|
||
|
case reflect.Int32, reflect.Uint32:
|
||
|
return func(p unsafe.Pointer) bool { return *(*uint32)(p) == 0 }
|
||
|
|
||
|
case reflect.Int64, reflect.Uint64:
|
||
|
return func(p unsafe.Pointer) bool { return *(*uint64)(p) == 0 }
|
||
|
|
||
|
case reflect.Float32:
|
||
|
return func(p unsafe.Pointer) bool { return *(*float32)(p) == 0 }
|
||
|
|
||
|
case reflect.Float64:
|
||
|
return func(p unsafe.Pointer) bool { return *(*float64)(p) == 0 }
|
||
|
|
||
|
case reflect.Ptr:
|
||
|
return func(p unsafe.Pointer) bool { return *(*unsafe.Pointer)(p) == nil }
|
||
|
|
||
|
case reflect.Interface:
|
||
|
return func(p unsafe.Pointer) bool { return (*iface)(p).ptr == nil }
|
||
|
}
|
||
|
|
||
|
return func(unsafe.Pointer) bool { return false }
|
||
|
}
|
||
|
|
||
|
type iface struct {
|
||
|
typ unsafe.Pointer
|
||
|
ptr unsafe.Pointer
|
||
|
}
|
||
|
|
||
|
type slice struct {
|
||
|
data unsafe.Pointer
|
||
|
len int
|
||
|
cap int
|
||
|
}
|
||
|
|
||
|
type structType struct {
|
||
|
fields []structField
|
||
|
fieldsIndex map[string]*structField
|
||
|
ficaseIndex map[string]*structField
|
||
|
keyset []byte
|
||
|
typ reflect.Type
|
||
|
inlined bool
|
||
|
}
|
||
|
|
||
|
type structField struct {
|
||
|
codec codec
|
||
|
offset uintptr
|
||
|
empty emptyFunc
|
||
|
tag bool
|
||
|
omitempty bool
|
||
|
json string
|
||
|
html string
|
||
|
name string
|
||
|
typ reflect.Type
|
||
|
zero reflect.Value
|
||
|
index int
|
||
|
}
|
||
|
|
||
|
func unmarshalTypeError(b []byte, t reflect.Type) error {
|
||
|
return &UnmarshalTypeError{Value: strconv.Quote(prefix(b)), Type: t}
|
||
|
}
|
||
|
|
||
|
func unmarshalOverflow(b []byte, t reflect.Type) error {
|
||
|
return &UnmarshalTypeError{Value: "number " + prefix(b) + " overflows", Type: t}
|
||
|
}
|
||
|
|
||
|
func unexpectedEOF(b []byte) error {
|
||
|
return syntaxError(b, "unexpected end of JSON input")
|
||
|
}
|
||
|
|
||
|
var syntaxErrorMsgOffset = ^uintptr(0)
|
||
|
|
||
|
func init() {
|
||
|
t := reflect.TypeOf(SyntaxError{})
|
||
|
for i, n := 0, t.NumField(); i < n; i++ {
|
||
|
if f := t.Field(i); f.Type.Kind() == reflect.String {
|
||
|
syntaxErrorMsgOffset = f.Offset
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func syntaxError(b []byte, msg string, args ...interface{}) error {
|
||
|
e := new(SyntaxError)
|
||
|
i := syntaxErrorMsgOffset
|
||
|
if i != ^uintptr(0) {
|
||
|
s := "json: " + fmt.Sprintf(msg, args...) + ": " + prefix(b)
|
||
|
p := unsafe.Pointer(e)
|
||
|
// Hack to set the unexported `msg` field.
|
||
|
*(*string)(unsafe.Pointer(uintptr(p) + i)) = s
|
||
|
}
|
||
|
return e
|
||
|
}
|
||
|
|
||
|
func objectKeyError(b []byte, err error) ([]byte, error) {
|
||
|
if len(b) == 0 {
|
||
|
return nil, unexpectedEOF(b)
|
||
|
}
|
||
|
switch err.(type) {
|
||
|
case *UnmarshalTypeError:
|
||
|
err = syntaxError(b, "invalid character '%c' looking for beginning of object key", b[0])
|
||
|
}
|
||
|
return b, err
|
||
|
}
|
||
|
|
||
|
func prefix(b []byte) string {
|
||
|
if len(b) < 32 {
|
||
|
return string(b)
|
||
|
}
|
||
|
return string(b[:32]) + "..."
|
||
|
}
|
||
|
|
||
|
func intStringsAreSorted(i0, i1 int64) bool {
|
||
|
var b0, b1 [32]byte
|
||
|
return string(strconv.AppendInt(b0[:0], i0, 10)) < string(strconv.AppendInt(b1[:0], i1, 10))
|
||
|
}
|
||
|
|
||
|
func uintStringsAreSorted(u0, u1 uint64) bool {
|
||
|
var b0, b1 [32]byte
|
||
|
return string(strconv.AppendUint(b0[:0], u0, 10)) < string(strconv.AppendUint(b1[:0], u1, 10))
|
||
|
}
|
||
|
|
||
|
func stringToBytes(s string) []byte {
|
||
|
return *(*[]byte)(unsafe.Pointer(&sliceHeader{
|
||
|
Data: *(*unsafe.Pointer)(unsafe.Pointer(&s)),
|
||
|
Len: len(s),
|
||
|
Cap: len(s),
|
||
|
}))
|
||
|
}
|
||
|
|
||
|
type sliceHeader struct {
|
||
|
Data unsafe.Pointer
|
||
|
Len int
|
||
|
Cap int
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
nullType = reflect.TypeOf(nil)
|
||
|
boolType = reflect.TypeOf(false)
|
||
|
|
||
|
intType = reflect.TypeOf(int(0))
|
||
|
int8Type = reflect.TypeOf(int8(0))
|
||
|
int16Type = reflect.TypeOf(int16(0))
|
||
|
int32Type = reflect.TypeOf(int32(0))
|
||
|
int64Type = reflect.TypeOf(int64(0))
|
||
|
|
||
|
uintType = reflect.TypeOf(uint(0))
|
||
|
uint8Type = reflect.TypeOf(uint8(0))
|
||
|
uint16Type = reflect.TypeOf(uint16(0))
|
||
|
uint32Type = reflect.TypeOf(uint32(0))
|
||
|
uint64Type = reflect.TypeOf(uint64(0))
|
||
|
uintptrType = reflect.TypeOf(uintptr(0))
|
||
|
|
||
|
float32Type = reflect.TypeOf(float32(0))
|
||
|
float64Type = reflect.TypeOf(float64(0))
|
||
|
|
||
|
numberType = reflect.TypeOf(json.Number(""))
|
||
|
stringType = reflect.TypeOf("")
|
||
|
stringsType = reflect.TypeOf([]string(nil))
|
||
|
bytesType = reflect.TypeOf(([]byte)(nil))
|
||
|
durationType = reflect.TypeOf(time.Duration(0))
|
||
|
timeType = reflect.TypeOf(time.Time{})
|
||
|
rawMessageType = reflect.TypeOf(RawMessage(nil))
|
||
|
|
||
|
numberPtrType = reflect.PtrTo(numberType)
|
||
|
durationPtrType = reflect.PtrTo(durationType)
|
||
|
timePtrType = reflect.PtrTo(timeType)
|
||
|
rawMessagePtrType = reflect.PtrTo(rawMessageType)
|
||
|
|
||
|
sliceInterfaceType = reflect.TypeOf(([]interface{})(nil))
|
||
|
sliceStringType = reflect.TypeOf(([]interface{})(nil))
|
||
|
mapStringInterfaceType = reflect.TypeOf((map[string]interface{})(nil))
|
||
|
mapStringRawMessageType = reflect.TypeOf((map[string]RawMessage)(nil))
|
||
|
mapStringStringType = reflect.TypeOf((map[string]string)(nil))
|
||
|
mapStringStringSliceType = reflect.TypeOf((map[string][]string)(nil))
|
||
|
mapStringBoolType = reflect.TypeOf((map[string]bool)(nil))
|
||
|
|
||
|
interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||
|
jsonMarshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||
|
jsonUnmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||
|
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||
|
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||
|
)
|
||
|
|
||
|
// =============================================================================
|
||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
// appendDuration appends a human-readable representation of d to b.
|
||
|
//
|
||
|
// The function copies the implementation of time.Duration.String but prevents
|
||
|
// Go from making a dynamic memory allocation on the returned value.
|
||
|
func appendDuration(b []byte, d time.Duration) []byte {
|
||
|
// Largest time is 2540400h10m10.000000000s
|
||
|
var buf [32]byte
|
||
|
w := len(buf)
|
||
|
|
||
|
u := uint64(d)
|
||
|
neg := d < 0
|
||
|
if neg {
|
||
|
u = -u
|
||
|
}
|
||
|
|
||
|
if u < uint64(time.Second) {
|
||
|
// Special case: if duration is smaller than a second,
|
||
|
// use smaller units, like 1.2ms
|
||
|
var prec int
|
||
|
w--
|
||
|
buf[w] = 's'
|
||
|
w--
|
||
|
switch {
|
||
|
case u == 0:
|
||
|
return append(b, '0', 's')
|
||
|
case u < uint64(time.Microsecond):
|
||
|
// print nanoseconds
|
||
|
prec = 0
|
||
|
buf[w] = 'n'
|
||
|
case u < uint64(time.Millisecond):
|
||
|
// print microseconds
|
||
|
prec = 3
|
||
|
// U+00B5 'µ' micro sign == 0xC2 0xB5
|
||
|
w-- // Need room for two bytes.
|
||
|
copy(buf[w:], "µ")
|
||
|
default:
|
||
|
// print milliseconds
|
||
|
prec = 6
|
||
|
buf[w] = 'm'
|
||
|
}
|
||
|
w, u = fmtFrac(buf[:w], u, prec)
|
||
|
w = fmtInt(buf[:w], u)
|
||
|
} else {
|
||
|
w--
|
||
|
buf[w] = 's'
|
||
|
|
||
|
w, u = fmtFrac(buf[:w], u, 9)
|
||
|
|
||
|
// u is now integer seconds
|
||
|
w = fmtInt(buf[:w], u%60)
|
||
|
u /= 60
|
||
|
|
||
|
// u is now integer minutes
|
||
|
if u > 0 {
|
||
|
w--
|
||
|
buf[w] = 'm'
|
||
|
w = fmtInt(buf[:w], u%60)
|
||
|
u /= 60
|
||
|
|
||
|
// u is now integer hours
|
||
|
// Stop at hours because days can be different lengths.
|
||
|
if u > 0 {
|
||
|
w--
|
||
|
buf[w] = 'h'
|
||
|
w = fmtInt(buf[:w], u)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if neg {
|
||
|
w--
|
||
|
buf[w] = '-'
|
||
|
}
|
||
|
|
||
|
return append(b, buf[w:]...)
|
||
|
}
|
||
|
|
||
|
// fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the
|
||
|
// tail of buf, omitting trailing zeros. it omits the decimal
|
||
|
// point too when the fraction is 0. It returns the index where the
|
||
|
// output bytes begin and the value v/10**prec.
|
||
|
func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) {
|
||
|
// Omit trailing zeros up to and including decimal point.
|
||
|
w := len(buf)
|
||
|
print := false
|
||
|
for i := 0; i < prec; i++ {
|
||
|
digit := v % 10
|
||
|
print = print || digit != 0
|
||
|
if print {
|
||
|
w--
|
||
|
buf[w] = byte(digit) + '0'
|
||
|
}
|
||
|
v /= 10
|
||
|
}
|
||
|
if print {
|
||
|
w--
|
||
|
buf[w] = '.'
|
||
|
}
|
||
|
return w, v
|
||
|
}
|
||
|
|
||
|
// fmtInt formats v into the tail of buf.
|
||
|
// It returns the index where the output begins.
|
||
|
func fmtInt(buf []byte, v uint64) int {
|
||
|
w := len(buf)
|
||
|
if v == 0 {
|
||
|
w--
|
||
|
buf[w] = '0'
|
||
|
} else {
|
||
|
for v > 0 {
|
||
|
w--
|
||
|
buf[w] = byte(v%10) + '0'
|
||
|
v /= 10
|
||
|
}
|
||
|
}
|
||
|
return w
|
||
|
}
|
||
|
|
||
|
// =============================================================================
|