| // 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...) |
| } |