// Copyright (c) 2012, 2013 Ugorji Nwoke. All rights reserved.
|
// Use of this source code is governed by a BSD-style license found in the LICENSE file.
|
|
package codec
|
|
import (
|
"io"
|
"reflect"
|
)
|
|
const (
|
// Some tagging information for error messages.
|
msgTagEnc = "codec.encoder"
|
defEncByteBufSize = 1 << 6 // 4:16, 6:64, 8:256, 10:1024
|
// maxTimeSecs32 = math.MaxInt32 / 60 / 24 / 366
|
)
|
|
// AsSymbolFlag defines what should be encoded as symbols.
|
type AsSymbolFlag uint8
|
|
const (
|
// AsSymbolDefault is default.
|
// Currently, this means only encode struct field names as symbols.
|
// The default is subject to change.
|
AsSymbolDefault AsSymbolFlag = iota
|
|
// AsSymbolAll means encode anything which could be a symbol as a symbol.
|
AsSymbolAll = 0xfe
|
|
// AsSymbolNone means do not encode anything as a symbol.
|
AsSymbolNone = 1 << iota
|
|
// AsSymbolMapStringKeys means encode keys in map[string]XXX as symbols.
|
AsSymbolMapStringKeysFlag
|
|
// AsSymbolStructFieldName means encode struct field names as symbols.
|
AsSymbolStructFieldNameFlag
|
)
|
|
// encWriter abstracting writing to a byte array or to an io.Writer.
|
type encWriter interface {
|
writeUint16(uint16)
|
writeUint32(uint32)
|
writeUint64(uint64)
|
writeb([]byte)
|
writestr(string)
|
writen1(byte)
|
writen2(byte, byte)
|
atEndOfEncode()
|
}
|
|
// encDriver abstracts the actual codec (binc vs msgpack, etc)
|
type encDriver interface {
|
isBuiltinType(rt uintptr) bool
|
encodeBuiltin(rt uintptr, v interface{})
|
encodeNil()
|
encodeInt(i int64)
|
encodeUint(i uint64)
|
encodeBool(b bool)
|
encodeFloat32(f float32)
|
encodeFloat64(f float64)
|
encodeExtPreamble(xtag byte, length int)
|
encodeArrayPreamble(length int)
|
encodeMapPreamble(length int)
|
encodeString(c charEncoding, v string)
|
encodeSymbol(v string)
|
encodeStringBytes(c charEncoding, v []byte)
|
//TODO
|
//encBignum(f *big.Int)
|
//encStringRunes(c charEncoding, v []rune)
|
}
|
|
type ioEncWriterWriter interface {
|
WriteByte(c byte) error
|
WriteString(s string) (n int, err error)
|
Write(p []byte) (n int, err error)
|
}
|
|
type ioEncStringWriter interface {
|
WriteString(s string) (n int, err error)
|
}
|
|
type EncodeOptions struct {
|
// Encode a struct as an array, and not as a map.
|
StructToArray bool
|
|
// AsSymbols defines what should be encoded as symbols.
|
//
|
// Encoding as symbols can reduce the encoded size significantly.
|
//
|
// However, during decoding, each string to be encoded as a symbol must
|
// be checked to see if it has been seen before. Consequently, encoding time
|
// will increase if using symbols, because string comparisons has a clear cost.
|
//
|
// Sample values:
|
// AsSymbolNone
|
// AsSymbolAll
|
// AsSymbolMapStringKeys
|
// AsSymbolMapStringKeysFlag | AsSymbolStructFieldNameFlag
|
AsSymbols AsSymbolFlag
|
}
|
|
// ---------------------------------------------
|
|
type simpleIoEncWriterWriter struct {
|
w io.Writer
|
bw io.ByteWriter
|
sw ioEncStringWriter
|
}
|
|
func (o *simpleIoEncWriterWriter) WriteByte(c byte) (err error) {
|
if o.bw != nil {
|
return o.bw.WriteByte(c)
|
}
|
_, err = o.w.Write([]byte{c})
|
return
|
}
|
|
func (o *simpleIoEncWriterWriter) WriteString(s string) (n int, err error) {
|
if o.sw != nil {
|
return o.sw.WriteString(s)
|
}
|
return o.w.Write([]byte(s))
|
}
|
|
func (o *simpleIoEncWriterWriter) Write(p []byte) (n int, err error) {
|
return o.w.Write(p)
|
}
|
|
// ----------------------------------------
|
|
// ioEncWriter implements encWriter and can write to an io.Writer implementation
|
type ioEncWriter struct {
|
w ioEncWriterWriter
|
x [8]byte // temp byte array re-used internally for efficiency
|
}
|
|
func (z *ioEncWriter) writeUint16(v uint16) {
|
bigen.PutUint16(z.x[:2], v)
|
z.writeb(z.x[:2])
|
}
|
|
func (z *ioEncWriter) writeUint32(v uint32) {
|
bigen.PutUint32(z.x[:4], v)
|
z.writeb(z.x[:4])
|
}
|
|
func (z *ioEncWriter) writeUint64(v uint64) {
|
bigen.PutUint64(z.x[:8], v)
|
z.writeb(z.x[:8])
|
}
|
|
func (z *ioEncWriter) writeb(bs []byte) {
|
if len(bs) == 0 {
|
return
|
}
|
n, err := z.w.Write(bs)
|
if err != nil {
|
panic(err)
|
}
|
if n != len(bs) {
|
encErr("write: Incorrect num bytes written. Expecting: %v, Wrote: %v", len(bs), n)
|
}
|
}
|
|
func (z *ioEncWriter) writestr(s string) {
|
n, err := z.w.WriteString(s)
|
if err != nil {
|
panic(err)
|
}
|
if n != len(s) {
|
encErr("write: Incorrect num bytes written. Expecting: %v, Wrote: %v", len(s), n)
|
}
|
}
|
|
func (z *ioEncWriter) writen1(b byte) {
|
if err := z.w.WriteByte(b); err != nil {
|
panic(err)
|
}
|
}
|
|
func (z *ioEncWriter) writen2(b1 byte, b2 byte) {
|
z.writen1(b1)
|
z.writen1(b2)
|
}
|
|
func (z *ioEncWriter) atEndOfEncode() {}
|
|
// ----------------------------------------
|
|
// bytesEncWriter implements encWriter and can write to an byte slice.
|
// It is used by Marshal function.
|
type bytesEncWriter struct {
|
b []byte
|
c int // cursor
|
out *[]byte // write out on atEndOfEncode
|
}
|
|
func (z *bytesEncWriter) writeUint16(v uint16) {
|
c := z.grow(2)
|
z.b[c] = byte(v >> 8)
|
z.b[c+1] = byte(v)
|
}
|
|
func (z *bytesEncWriter) writeUint32(v uint32) {
|
c := z.grow(4)
|
z.b[c] = byte(v >> 24)
|
z.b[c+1] = byte(v >> 16)
|
z.b[c+2] = byte(v >> 8)
|
z.b[c+3] = byte(v)
|
}
|
|
func (z *bytesEncWriter) writeUint64(v uint64) {
|
c := z.grow(8)
|
z.b[c] = byte(v >> 56)
|
z.b[c+1] = byte(v >> 48)
|
z.b[c+2] = byte(v >> 40)
|
z.b[c+3] = byte(v >> 32)
|
z.b[c+4] = byte(v >> 24)
|
z.b[c+5] = byte(v >> 16)
|
z.b[c+6] = byte(v >> 8)
|
z.b[c+7] = byte(v)
|
}
|
|
func (z *bytesEncWriter) writeb(s []byte) {
|
if len(s) == 0 {
|
return
|
}
|
c := z.grow(len(s))
|
copy(z.b[c:], s)
|
}
|
|
func (z *bytesEncWriter) writestr(s string) {
|
c := z.grow(len(s))
|
copy(z.b[c:], s)
|
}
|
|
func (z *bytesEncWriter) writen1(b1 byte) {
|
c := z.grow(1)
|
z.b[c] = b1
|
}
|
|
func (z *bytesEncWriter) writen2(b1 byte, b2 byte) {
|
c := z.grow(2)
|
z.b[c] = b1
|
z.b[c+1] = b2
|
}
|
|
func (z *bytesEncWriter) atEndOfEncode() {
|
*(z.out) = z.b[:z.c]
|
}
|
|
func (z *bytesEncWriter) grow(n int) (oldcursor int) {
|
oldcursor = z.c
|
z.c = oldcursor + n
|
if z.c > cap(z.b) {
|
// Tried using appendslice logic: (if cap < 1024, *2, else *1.25).
|
// However, it was too expensive, causing too many iterations of copy.
|
// Using bytes.Buffer model was much better (2*cap + n)
|
bs := make([]byte, 2*cap(z.b)+n)
|
copy(bs, z.b[:oldcursor])
|
z.b = bs
|
} else if z.c > len(z.b) {
|
z.b = z.b[:cap(z.b)]
|
}
|
return
|
}
|
|
// ---------------------------------------------
|
|
type encFnInfo struct {
|
ti *typeInfo
|
e *Encoder
|
ee encDriver
|
xfFn func(reflect.Value) ([]byte, error)
|
xfTag byte
|
}
|
|
func (f *encFnInfo) builtin(rv reflect.Value) {
|
f.ee.encodeBuiltin(f.ti.rtid, rv.Interface())
|
}
|
|
func (f *encFnInfo) rawExt(rv reflect.Value) {
|
f.e.encRawExt(rv.Interface().(RawExt))
|
}
|
|
func (f *encFnInfo) ext(rv reflect.Value) {
|
bs, fnerr := f.xfFn(rv)
|
if fnerr != nil {
|
panic(fnerr)
|
}
|
if bs == nil {
|
f.ee.encodeNil()
|
return
|
}
|
if f.e.hh.writeExt() {
|
f.ee.encodeExtPreamble(f.xfTag, len(bs))
|
f.e.w.writeb(bs)
|
} else {
|
f.ee.encodeStringBytes(c_RAW, bs)
|
}
|
|
}
|
|
func (f *encFnInfo) binaryMarshal(rv reflect.Value) {
|
var bm binaryMarshaler
|
if f.ti.mIndir == 0 {
|
bm = rv.Interface().(binaryMarshaler)
|
} else if f.ti.mIndir == -1 {
|
bm = rv.Addr().Interface().(binaryMarshaler)
|
} else {
|
for j, k := int8(0), f.ti.mIndir; j < k; j++ {
|
if rv.IsNil() {
|
f.ee.encodeNil()
|
return
|
}
|
rv = rv.Elem()
|
}
|
bm = rv.Interface().(binaryMarshaler)
|
}
|
// debugf(">>>> binaryMarshaler: %T", rv.Interface())
|
bs, fnerr := bm.MarshalBinary()
|
if fnerr != nil {
|
panic(fnerr)
|
}
|
if bs == nil {
|
f.ee.encodeNil()
|
} else {
|
f.ee.encodeStringBytes(c_RAW, bs)
|
}
|
}
|
|
func (f *encFnInfo) kBool(rv reflect.Value) {
|
f.ee.encodeBool(rv.Bool())
|
}
|
|
func (f *encFnInfo) kString(rv reflect.Value) {
|
f.ee.encodeString(c_UTF8, rv.String())
|
}
|
|
func (f *encFnInfo) kFloat64(rv reflect.Value) {
|
f.ee.encodeFloat64(rv.Float())
|
}
|
|
func (f *encFnInfo) kFloat32(rv reflect.Value) {
|
f.ee.encodeFloat32(float32(rv.Float()))
|
}
|
|
func (f *encFnInfo) kInt(rv reflect.Value) {
|
f.ee.encodeInt(rv.Int())
|
}
|
|
func (f *encFnInfo) kUint(rv reflect.Value) {
|
f.ee.encodeUint(rv.Uint())
|
}
|
|
func (f *encFnInfo) kInvalid(rv reflect.Value) {
|
f.ee.encodeNil()
|
}
|
|
func (f *encFnInfo) kErr(rv reflect.Value) {
|
encErr("Unsupported kind: %s, for: %#v", rv.Kind(), rv)
|
}
|
|
func (f *encFnInfo) kSlice(rv reflect.Value) {
|
if rv.IsNil() {
|
f.ee.encodeNil()
|
return
|
}
|
|
if shortCircuitReflectToFastPath {
|
switch f.ti.rtid {
|
case intfSliceTypId:
|
f.e.encSliceIntf(rv.Interface().([]interface{}))
|
return
|
case strSliceTypId:
|
f.e.encSliceStr(rv.Interface().([]string))
|
return
|
case uint64SliceTypId:
|
f.e.encSliceUint64(rv.Interface().([]uint64))
|
return
|
case int64SliceTypId:
|
f.e.encSliceInt64(rv.Interface().([]int64))
|
return
|
}
|
}
|
|
// If in this method, then there was no extension function defined.
|
// So it's okay to treat as []byte.
|
if f.ti.rtid == uint8SliceTypId || f.ti.rt.Elem().Kind() == reflect.Uint8 {
|
f.ee.encodeStringBytes(c_RAW, rv.Bytes())
|
return
|
}
|
|
l := rv.Len()
|
if f.ti.mbs {
|
if l%2 == 1 {
|
encErr("mapBySlice: invalid length (must be divisible by 2): %v", l)
|
}
|
f.ee.encodeMapPreamble(l / 2)
|
} else {
|
f.ee.encodeArrayPreamble(l)
|
}
|
if l == 0 {
|
return
|
}
|
for j := 0; j < l; j++ {
|
// TODO: Consider perf implication of encoding odd index values as symbols if type is string
|
f.e.encodeValue(rv.Index(j))
|
}
|
}
|
|
func (f *encFnInfo) kArray(rv reflect.Value) {
|
// We cannot share kSlice method, because the array may be non-addressable.
|
// E.g. type struct S{B [2]byte}; Encode(S{}) will bomb on "panic: slice of unaddressable array".
|
// So we have to duplicate the functionality here.
|
// f.e.encodeValue(rv.Slice(0, rv.Len()))
|
// f.kSlice(rv.Slice(0, rv.Len()))
|
|
l := rv.Len()
|
// Handle an array of bytes specially (in line with what is done for slices)
|
if f.ti.rt.Elem().Kind() == reflect.Uint8 {
|
if l == 0 {
|
f.ee.encodeStringBytes(c_RAW, nil)
|
return
|
}
|
var bs []byte
|
if rv.CanAddr() {
|
bs = rv.Slice(0, l).Bytes()
|
} else {
|
bs = make([]byte, l)
|
for i := 0; i < l; i++ {
|
bs[i] = byte(rv.Index(i).Uint())
|
}
|
}
|
f.ee.encodeStringBytes(c_RAW, bs)
|
return
|
}
|
|
if f.ti.mbs {
|
if l%2 == 1 {
|
encErr("mapBySlice: invalid length (must be divisible by 2): %v", l)
|
}
|
f.ee.encodeMapPreamble(l / 2)
|
} else {
|
f.ee.encodeArrayPreamble(l)
|
}
|
if l == 0 {
|
return
|
}
|
for j := 0; j < l; j++ {
|
// TODO: Consider perf implication of encoding odd index values as symbols if type is string
|
f.e.encodeValue(rv.Index(j))
|
}
|
}
|
|
func (f *encFnInfo) kStruct(rv reflect.Value) {
|
fti := f.ti
|
newlen := len(fti.sfi)
|
rvals := make([]reflect.Value, newlen)
|
var encnames []string
|
e := f.e
|
tisfi := fti.sfip
|
toMap := !(fti.toArray || e.h.StructToArray)
|
// if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct)
|
if toMap {
|
tisfi = fti.sfi
|
encnames = make([]string, newlen)
|
}
|
newlen = 0
|
for _, si := range tisfi {
|
if si.i != -1 {
|
rvals[newlen] = rv.Field(int(si.i))
|
} else {
|
rvals[newlen] = rv.FieldByIndex(si.is)
|
}
|
if toMap {
|
if si.omitEmpty && isEmptyValue(rvals[newlen]) {
|
continue
|
}
|
encnames[newlen] = si.encName
|
} else {
|
if si.omitEmpty && isEmptyValue(rvals[newlen]) {
|
rvals[newlen] = reflect.Value{} //encode as nil
|
}
|
}
|
newlen++
|
}
|
|
// debugf(">>>> kStruct: newlen: %v", newlen)
|
if toMap {
|
ee := f.ee //don't dereference everytime
|
ee.encodeMapPreamble(newlen)
|
// asSymbols := e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
|
asSymbols := e.h.AsSymbols == AsSymbolDefault || e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
|
for j := 0; j < newlen; j++ {
|
if asSymbols {
|
ee.encodeSymbol(encnames[j])
|
} else {
|
ee.encodeString(c_UTF8, encnames[j])
|
}
|
e.encodeValue(rvals[j])
|
}
|
} else {
|
f.ee.encodeArrayPreamble(newlen)
|
for j := 0; j < newlen; j++ {
|
e.encodeValue(rvals[j])
|
}
|
}
|
}
|
|
// func (f *encFnInfo) kPtr(rv reflect.Value) {
|
// debugf(">>>>>>> ??? encode kPtr called - shouldn't get called")
|
// if rv.IsNil() {
|
// f.ee.encodeNil()
|
// return
|
// }
|
// f.e.encodeValue(rv.Elem())
|
// }
|
|
func (f *encFnInfo) kInterface(rv reflect.Value) {
|
if rv.IsNil() {
|
f.ee.encodeNil()
|
return
|
}
|
f.e.encodeValue(rv.Elem())
|
}
|
|
func (f *encFnInfo) kMap(rv reflect.Value) {
|
if rv.IsNil() {
|
f.ee.encodeNil()
|
return
|
}
|
|
if shortCircuitReflectToFastPath {
|
switch f.ti.rtid {
|
case mapIntfIntfTypId:
|
f.e.encMapIntfIntf(rv.Interface().(map[interface{}]interface{}))
|
return
|
case mapStrIntfTypId:
|
f.e.encMapStrIntf(rv.Interface().(map[string]interface{}))
|
return
|
case mapStrStrTypId:
|
f.e.encMapStrStr(rv.Interface().(map[string]string))
|
return
|
case mapInt64IntfTypId:
|
f.e.encMapInt64Intf(rv.Interface().(map[int64]interface{}))
|
return
|
case mapUint64IntfTypId:
|
f.e.encMapUint64Intf(rv.Interface().(map[uint64]interface{}))
|
return
|
}
|
}
|
|
l := rv.Len()
|
f.ee.encodeMapPreamble(l)
|
if l == 0 {
|
return
|
}
|
// keyTypeIsString := f.ti.rt.Key().Kind() == reflect.String
|
keyTypeIsString := f.ti.rt.Key() == stringTyp
|
var asSymbols bool
|
if keyTypeIsString {
|
asSymbols = f.e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
|
}
|
mks := rv.MapKeys()
|
// for j, lmks := 0, len(mks); j < lmks; j++ {
|
for j := range mks {
|
if keyTypeIsString {
|
if asSymbols {
|
f.ee.encodeSymbol(mks[j].String())
|
} else {
|
f.ee.encodeString(c_UTF8, mks[j].String())
|
}
|
} else {
|
f.e.encodeValue(mks[j])
|
}
|
f.e.encodeValue(rv.MapIndex(mks[j]))
|
}
|
|
}
|
|
// --------------------------------------------------
|
|
// encFn encapsulates the captured variables and the encode function.
|
// This way, we only do some calculations one times, and pass to the
|
// code block that should be called (encapsulated in a function)
|
// instead of executing the checks every time.
|
type encFn struct {
|
i *encFnInfo
|
f func(*encFnInfo, reflect.Value)
|
}
|
|
// --------------------------------------------------
|
|
// An Encoder writes an object to an output stream in the codec format.
|
type Encoder struct {
|
w encWriter
|
e encDriver
|
h *BasicHandle
|
hh Handle
|
f map[uintptr]encFn
|
x []uintptr
|
s []encFn
|
}
|
|
// NewEncoder returns an Encoder for encoding into an io.Writer.
|
//
|
// For efficiency, Users are encouraged to pass in a memory buffered writer
|
// (eg bufio.Writer, bytes.Buffer).
|
func NewEncoder(w io.Writer, h Handle) *Encoder {
|
ww, ok := w.(ioEncWriterWriter)
|
if !ok {
|
sww := simpleIoEncWriterWriter{w: w}
|
sww.bw, _ = w.(io.ByteWriter)
|
sww.sw, _ = w.(ioEncStringWriter)
|
ww = &sww
|
//ww = bufio.NewWriterSize(w, defEncByteBufSize)
|
}
|
z := ioEncWriter{
|
w: ww,
|
}
|
return &Encoder{w: &z, hh: h, h: h.getBasicHandle(), e: h.newEncDriver(&z)}
|
}
|
|
// NewEncoderBytes returns an encoder for encoding directly and efficiently
|
// into a byte slice, using zero-copying to temporary slices.
|
//
|
// It will potentially replace the output byte slice pointed to.
|
// After encoding, the out parameter contains the encoded contents.
|
func NewEncoderBytes(out *[]byte, h Handle) *Encoder {
|
in := *out
|
if in == nil {
|
in = make([]byte, defEncByteBufSize)
|
}
|
z := bytesEncWriter{
|
b: in,
|
out: out,
|
}
|
return &Encoder{w: &z, hh: h, h: h.getBasicHandle(), e: h.newEncDriver(&z)}
|
}
|
|
// Encode writes an object into a stream in the codec format.
|
//
|
// Encoding can be configured via the "codec" struct tag for the fields.
|
//
|
// The "codec" key in struct field's tag value is the key name,
|
// followed by an optional comma and options.
|
//
|
// To set an option on all fields (e.g. omitempty on all fields), you
|
// can create a field called _struct, and set flags on it.
|
//
|
// Struct values "usually" encode as maps. Each exported struct field is encoded unless:
|
// - the field's codec tag is "-", OR
|
// - the field is empty and its codec tag specifies the "omitempty" option.
|
//
|
// When encoding as a map, the first string in the tag (before the comma)
|
// is the map key string to use when encoding.
|
//
|
// However, struct values may encode as arrays. This happens when:
|
// - StructToArray Encode option is set, OR
|
// - the codec tag on the _struct field sets the "toarray" option
|
//
|
// Values with types that implement MapBySlice are encoded as stream maps.
|
//
|
// The empty values (for omitempty option) are false, 0, any nil pointer
|
// or interface value, and any array, slice, map, or string of length zero.
|
//
|
// Anonymous fields are encoded inline if no struct tag is present.
|
// Else they are encoded as regular fields.
|
//
|
// Examples:
|
//
|
// type MyStruct struct {
|
// _struct bool `codec:",omitempty"` //set omitempty for every field
|
// Field1 string `codec:"-"` //skip this field
|
// Field2 int `codec:"myName"` //Use key "myName" in encode stream
|
// Field3 int32 `codec:",omitempty"` //use key "Field3". Omit if empty.
|
// Field4 bool `codec:"f4,omitempty"` //use key "f4". Omit if empty.
|
// ...
|
// }
|
//
|
// type MyStruct struct {
|
// _struct bool `codec:",omitempty,toarray"` //set omitempty for every field
|
// //and encode struct as an array
|
// }
|
//
|
// The mode of encoding is based on the type of the value. When a value is seen:
|
// - If an extension is registered for it, call that extension function
|
// - If it implements BinaryMarshaler, call its MarshalBinary() (data []byte, err error)
|
// - Else encode it based on its reflect.Kind
|
//
|
// Note that struct field names and keys in map[string]XXX will be treated as symbols.
|
// Some formats support symbols (e.g. binc) and will properly encode the string
|
// only once in the stream, and use a tag to refer to it thereafter.
|
func (e *Encoder) Encode(v interface{}) (err error) {
|
defer panicToErr(&err)
|
e.encode(v)
|
e.w.atEndOfEncode()
|
return
|
}
|
|
func (e *Encoder) encode(iv interface{}) {
|
switch v := iv.(type) {
|
case nil:
|
e.e.encodeNil()
|
|
case reflect.Value:
|
e.encodeValue(v)
|
|
case string:
|
e.e.encodeString(c_UTF8, v)
|
case bool:
|
e.e.encodeBool(v)
|
case int:
|
e.e.encodeInt(int64(v))
|
case int8:
|
e.e.encodeInt(int64(v))
|
case int16:
|
e.e.encodeInt(int64(v))
|
case int32:
|
e.e.encodeInt(int64(v))
|
case int64:
|
e.e.encodeInt(v)
|
case uint:
|
e.e.encodeUint(uint64(v))
|
case uint8:
|
e.e.encodeUint(uint64(v))
|
case uint16:
|
e.e.encodeUint(uint64(v))
|
case uint32:
|
e.e.encodeUint(uint64(v))
|
case uint64:
|
e.e.encodeUint(v)
|
case float32:
|
e.e.encodeFloat32(v)
|
case float64:
|
e.e.encodeFloat64(v)
|
|
case []interface{}:
|
e.encSliceIntf(v)
|
case []string:
|
e.encSliceStr(v)
|
case []int64:
|
e.encSliceInt64(v)
|
case []uint64:
|
e.encSliceUint64(v)
|
case []uint8:
|
e.e.encodeStringBytes(c_RAW, v)
|
|
case map[interface{}]interface{}:
|
e.encMapIntfIntf(v)
|
case map[string]interface{}:
|
e.encMapStrIntf(v)
|
case map[string]string:
|
e.encMapStrStr(v)
|
case map[int64]interface{}:
|
e.encMapInt64Intf(v)
|
case map[uint64]interface{}:
|
e.encMapUint64Intf(v)
|
|
case *string:
|
e.e.encodeString(c_UTF8, *v)
|
case *bool:
|
e.e.encodeBool(*v)
|
case *int:
|
e.e.encodeInt(int64(*v))
|
case *int8:
|
e.e.encodeInt(int64(*v))
|
case *int16:
|
e.e.encodeInt(int64(*v))
|
case *int32:
|
e.e.encodeInt(int64(*v))
|
case *int64:
|
e.e.encodeInt(*v)
|
case *uint:
|
e.e.encodeUint(uint64(*v))
|
case *uint8:
|
e.e.encodeUint(uint64(*v))
|
case *uint16:
|
e.e.encodeUint(uint64(*v))
|
case *uint32:
|
e.e.encodeUint(uint64(*v))
|
case *uint64:
|
e.e.encodeUint(*v)
|
case *float32:
|
e.e.encodeFloat32(*v)
|
case *float64:
|
e.e.encodeFloat64(*v)
|
|
case *[]interface{}:
|
e.encSliceIntf(*v)
|
case *[]string:
|
e.encSliceStr(*v)
|
case *[]int64:
|
e.encSliceInt64(*v)
|
case *[]uint64:
|
e.encSliceUint64(*v)
|
case *[]uint8:
|
e.e.encodeStringBytes(c_RAW, *v)
|
|
case *map[interface{}]interface{}:
|
e.encMapIntfIntf(*v)
|
case *map[string]interface{}:
|
e.encMapStrIntf(*v)
|
case *map[string]string:
|
e.encMapStrStr(*v)
|
case *map[int64]interface{}:
|
e.encMapInt64Intf(*v)
|
case *map[uint64]interface{}:
|
e.encMapUint64Intf(*v)
|
|
default:
|
e.encodeValue(reflect.ValueOf(iv))
|
}
|
}
|
|
func (e *Encoder) encodeValue(rv reflect.Value) {
|
for rv.Kind() == reflect.Ptr {
|
if rv.IsNil() {
|
e.e.encodeNil()
|
return
|
}
|
rv = rv.Elem()
|
}
|
|
rt := rv.Type()
|
rtid := reflect.ValueOf(rt).Pointer()
|
|
// if e.f == nil && e.s == nil { debugf("---->Creating new enc f map for type: %v\n", rt) }
|
var fn encFn
|
var ok bool
|
if useMapForCodecCache {
|
fn, ok = e.f[rtid]
|
} else {
|
for i, v := range e.x {
|
if v == rtid {
|
fn, ok = e.s[i], true
|
break
|
}
|
}
|
}
|
if !ok {
|
// debugf("\tCreating new enc fn for type: %v\n", rt)
|
fi := encFnInfo{ti: getTypeInfo(rtid, rt), e: e, ee: e.e}
|
fn.i = &fi
|
if rtid == rawExtTypId {
|
fn.f = (*encFnInfo).rawExt
|
} else if e.e.isBuiltinType(rtid) {
|
fn.f = (*encFnInfo).builtin
|
} else if xfTag, xfFn := e.h.getEncodeExt(rtid); xfFn != nil {
|
fi.xfTag, fi.xfFn = xfTag, xfFn
|
fn.f = (*encFnInfo).ext
|
} else if supportBinaryMarshal && fi.ti.m {
|
fn.f = (*encFnInfo).binaryMarshal
|
} else {
|
switch rk := rt.Kind(); rk {
|
case reflect.Bool:
|
fn.f = (*encFnInfo).kBool
|
case reflect.String:
|
fn.f = (*encFnInfo).kString
|
case reflect.Float64:
|
fn.f = (*encFnInfo).kFloat64
|
case reflect.Float32:
|
fn.f = (*encFnInfo).kFloat32
|
case reflect.Int, reflect.Int8, reflect.Int64, reflect.Int32, reflect.Int16:
|
fn.f = (*encFnInfo).kInt
|
case reflect.Uint8, reflect.Uint64, reflect.Uint, reflect.Uint32, reflect.Uint16:
|
fn.f = (*encFnInfo).kUint
|
case reflect.Invalid:
|
fn.f = (*encFnInfo).kInvalid
|
case reflect.Slice:
|
fn.f = (*encFnInfo).kSlice
|
case reflect.Array:
|
fn.f = (*encFnInfo).kArray
|
case reflect.Struct:
|
fn.f = (*encFnInfo).kStruct
|
// case reflect.Ptr:
|
// fn.f = (*encFnInfo).kPtr
|
case reflect.Interface:
|
fn.f = (*encFnInfo).kInterface
|
case reflect.Map:
|
fn.f = (*encFnInfo).kMap
|
default:
|
fn.f = (*encFnInfo).kErr
|
}
|
}
|
if useMapForCodecCache {
|
if e.f == nil {
|
e.f = make(map[uintptr]encFn, 16)
|
}
|
e.f[rtid] = fn
|
} else {
|
e.s = append(e.s, fn)
|
e.x = append(e.x, rtid)
|
}
|
}
|
|
fn.f(fn.i, rv)
|
|
}
|
|
func (e *Encoder) encRawExt(re RawExt) {
|
if re.Data == nil {
|
e.e.encodeNil()
|
return
|
}
|
if e.hh.writeExt() {
|
e.e.encodeExtPreamble(re.Tag, len(re.Data))
|
e.w.writeb(re.Data)
|
} else {
|
e.e.encodeStringBytes(c_RAW, re.Data)
|
}
|
}
|
|
// ---------------------------------------------
|
// short circuit functions for common maps and slices
|
|
func (e *Encoder) encSliceIntf(v []interface{}) {
|
e.e.encodeArrayPreamble(len(v))
|
for _, v2 := range v {
|
e.encode(v2)
|
}
|
}
|
|
func (e *Encoder) encSliceStr(v []string) {
|
e.e.encodeArrayPreamble(len(v))
|
for _, v2 := range v {
|
e.e.encodeString(c_UTF8, v2)
|
}
|
}
|
|
func (e *Encoder) encSliceInt64(v []int64) {
|
e.e.encodeArrayPreamble(len(v))
|
for _, v2 := range v {
|
e.e.encodeInt(v2)
|
}
|
}
|
|
func (e *Encoder) encSliceUint64(v []uint64) {
|
e.e.encodeArrayPreamble(len(v))
|
for _, v2 := range v {
|
e.e.encodeUint(v2)
|
}
|
}
|
|
func (e *Encoder) encMapStrStr(v map[string]string) {
|
e.e.encodeMapPreamble(len(v))
|
asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
|
for k2, v2 := range v {
|
if asSymbols {
|
e.e.encodeSymbol(k2)
|
} else {
|
e.e.encodeString(c_UTF8, k2)
|
}
|
e.e.encodeString(c_UTF8, v2)
|
}
|
}
|
|
func (e *Encoder) encMapStrIntf(v map[string]interface{}) {
|
e.e.encodeMapPreamble(len(v))
|
asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
|
for k2, v2 := range v {
|
if asSymbols {
|
e.e.encodeSymbol(k2)
|
} else {
|
e.e.encodeString(c_UTF8, k2)
|
}
|
e.encode(v2)
|
}
|
}
|
|
func (e *Encoder) encMapInt64Intf(v map[int64]interface{}) {
|
e.e.encodeMapPreamble(len(v))
|
for k2, v2 := range v {
|
e.e.encodeInt(k2)
|
e.encode(v2)
|
}
|
}
|
|
func (e *Encoder) encMapUint64Intf(v map[uint64]interface{}) {
|
e.e.encodeMapPreamble(len(v))
|
for k2, v2 := range v {
|
e.e.encodeUint(uint64(k2))
|
e.encode(v2)
|
}
|
}
|
|
func (e *Encoder) encMapIntfIntf(v map[interface{}]interface{}) {
|
e.e.encodeMapPreamble(len(v))
|
for k2, v2 := range v {
|
e.encode(k2)
|
e.encode(v2)
|
}
|
}
|
|
// ----------------------------------------
|
|
func encErr(format string, params ...interface{}) {
|
doPanic(msgTagEnc, format, params...)
|
}
|