| // Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. |
| // Use of this source code is governed by a MIT license found in the LICENSE file. |
| |
| package codec |
| |
| import ( |
| "encoding" |
| "fmt" |
| "io" |
| "reflect" |
| "sort" |
| "sync" |
| ) |
| |
| const ( |
| defEncByteBufSize = 1 << 6 // 4:16, 6:64, 8:256, 10:1024 |
| ) |
| |
| // 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 abstracts writing to a byte array or to an io.Writer. |
| type encWriter interface { |
| 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) |
| EncodeRawExt(re *RawExt, e *Encoder) |
| EncodeExt(v interface{}, xtag uint64, ext Ext, e *Encoder) |
| EncodeArrayStart(length int) |
| EncodeMapStart(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) |
| |
| reset() |
| } |
| |
| type encDriverAsis interface { |
| EncodeAsis(v []byte) |
| } |
| |
| type encNoSeparator struct{} |
| |
| func (_ encNoSeparator) EncodeEnd() {} |
| |
| 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 |
| |
| // Canonical representation means that encoding a value will always result in the same |
| // sequence of bytes. |
| // |
| // This only affects maps, as the iteration order for maps is random. |
| // |
| // The implementation MAY use the natural sort order for the map keys if possible: |
| // |
| // - If there is a natural sort order (ie for number, bool, string or []byte keys), |
| // then the map keys are first sorted in natural order and then written |
| // with corresponding map values to the strema. |
| // - If there is no natural sort order, then the map keys will first be |
| // encoded into []byte, and then sorted, |
| // before writing the sorted keys and the corresponding map values to the stream. |
| // |
| Canonical bool |
| |
| // CheckCircularRef controls whether we check for circular references |
| // and error fast during an encode. |
| // |
| // If enabled, an error is received if a pointer to a struct |
| // references itself either directly or through one of its fields (iteratively). |
| // |
| // This is opt-in, as there may be a performance hit to checking circular references. |
| CheckCircularRef bool |
| |
| // RecursiveEmptyCheck controls whether we descend into interfaces, structs and pointers |
| // when checking if a value is empty. |
| // |
| // Note that this may make OmitEmpty more expensive, as it incurs a lot more reflect calls. |
| RecursiveEmptyCheck bool |
| |
| // Raw controls whether we encode Raw values. |
| // This is a "dangerous" option and must be explicitly set. |
| // If set, we blindly encode Raw values as-is, without checking |
| // if they are a correct representation of a value in that format. |
| // If unset, we error out. |
| Raw 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 |
| bs [1]byte |
| } |
| |
| func (o *simpleIoEncWriterWriter) WriteByte(c byte) (err error) { |
| if o.bw != nil { |
| return o.bw.WriteByte(c) |
| } |
| // _, err = o.w.Write([]byte{c}) |
| o.bs[0] = c |
| _, err = o.w.Write(o.bs[:]) |
| 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)) |
| return o.w.Write(bytesView(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 |
| s simpleIoEncWriterWriter |
| // x [8]byte // temp byte array re-used internally for efficiency |
| } |
| |
| 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) { |
| panic(fmt.Errorf("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) { |
| panic(fmt.Errorf("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) writeb(s []byte) { |
| if len(s) == 0 { |
| return |
| } |
| oc, a := z.growNoAlloc(len(s)) |
| if a { |
| z.growAlloc(len(s), oc) |
| } |
| copy(z.b[oc:], s) |
| } |
| |
| func (z *bytesEncWriter) writestr(s string) { |
| if len(s) == 0 { |
| return |
| } |
| oc, a := z.growNoAlloc(len(s)) |
| if a { |
| z.growAlloc(len(s), oc) |
| } |
| copy(z.b[oc:], s) |
| } |
| |
| func (z *bytesEncWriter) writen1(b1 byte) { |
| oc, a := z.growNoAlloc(1) |
| if a { |
| z.growAlloc(1, oc) |
| } |
| z.b[oc] = b1 |
| } |
| |
| func (z *bytesEncWriter) writen2(b1 byte, b2 byte) { |
| oc, a := z.growNoAlloc(2) |
| if a { |
| z.growAlloc(2, oc) |
| } |
| z.b[oc+1] = b2 |
| z.b[oc] = b1 |
| } |
| |
| func (z *bytesEncWriter) atEndOfEncode() { |
| *(z.out) = z.b[:z.c] |
| } |
| |
| // have a growNoalloc(n int), which can be inlined. |
| // if allocation is needed, then call growAlloc(n int) |
| |
| func (z *bytesEncWriter) growNoAlloc(n int) (oldcursor int, allocNeeded bool) { |
| oldcursor = z.c |
| z.c = z.c + n |
| if z.c > len(z.b) { |
| if z.c > cap(z.b) { |
| allocNeeded = true |
| } else { |
| z.b = z.b[:cap(z.b)] |
| } |
| } |
| return |
| } |
| |
| func (z *bytesEncWriter) growAlloc(n int, oldcursor int) { |
| // appendslice logic (if cap < 1024, *2, else *1.25): more expensive. many copy calls. |
| // bytes.Buffer model (2*cap + n): much better |
| // bs := make([]byte, 2*cap(z.b)+n) |
| bs := make([]byte, growCap(cap(z.b), 1, n)) |
| copy(bs, z.b[:oldcursor]) |
| z.b = bs |
| } |
| |
| // --------------------------------------------- |
| |
| type encFnInfo struct { |
| e *Encoder |
| ti *typeInfo |
| xfFn Ext |
| xfTag uint64 |
| seq seqType |
| } |
| |
| func (f *encFnInfo) builtin(rv reflect.Value) { |
| f.e.e.EncodeBuiltin(f.ti.rtid, rv.Interface()) |
| } |
| |
| func (f *encFnInfo) raw(rv reflect.Value) { |
| f.e.raw(rv.Interface().(Raw)) |
| } |
| |
| func (f *encFnInfo) rawExt(rv reflect.Value) { |
| // rev := rv.Interface().(RawExt) |
| // f.e.e.EncodeRawExt(&rev, f.e) |
| var re *RawExt |
| if rv.CanAddr() { |
| re = rv.Addr().Interface().(*RawExt) |
| } else { |
| rev := rv.Interface().(RawExt) |
| re = &rev |
| } |
| f.e.e.EncodeRawExt(re, f.e) |
| } |
| |
| func (f *encFnInfo) ext(rv reflect.Value) { |
| // if this is a struct|array and it was addressable, then pass the address directly (not the value) |
| if k := rv.Kind(); (k == reflect.Struct || k == reflect.Array) && rv.CanAddr() { |
| rv = rv.Addr() |
| } |
| f.e.e.EncodeExt(rv.Interface(), f.xfTag, f.xfFn, f.e) |
| } |
| |
| func (f *encFnInfo) getValueForMarshalInterface(rv reflect.Value, indir int8) (v interface{}, proceed bool) { |
| if indir == 0 { |
| v = rv.Interface() |
| } else if indir == -1 { |
| // If a non-pointer was passed to Encode(), then that value is not addressable. |
| // Take addr if addressable, else copy value to an addressable value. |
| if rv.CanAddr() { |
| v = rv.Addr().Interface() |
| } else { |
| rv2 := reflect.New(rv.Type()) |
| rv2.Elem().Set(rv) |
| v = rv2.Interface() |
| // fmt.Printf("rv.Type: %v, rv2.Type: %v, v: %v\n", rv.Type(), rv2.Type(), v) |
| } |
| } else { |
| for j := int8(0); j < indir; j++ { |
| if rv.IsNil() { |
| f.e.e.EncodeNil() |
| return |
| } |
| rv = rv.Elem() |
| } |
| v = rv.Interface() |
| } |
| return v, true |
| } |
| |
| func (f *encFnInfo) selferMarshal(rv reflect.Value) { |
| if v, proceed := f.getValueForMarshalInterface(rv, f.ti.csIndir); proceed { |
| v.(Selfer).CodecEncodeSelf(f.e) |
| } |
| } |
| |
| func (f *encFnInfo) binaryMarshal(rv reflect.Value) { |
| if v, proceed := f.getValueForMarshalInterface(rv, f.ti.bmIndir); proceed { |
| bs, fnerr := v.(encoding.BinaryMarshaler).MarshalBinary() |
| f.e.marshal(bs, fnerr, false, c_RAW) |
| } |
| } |
| |
| func (f *encFnInfo) textMarshal(rv reflect.Value) { |
| if v, proceed := f.getValueForMarshalInterface(rv, f.ti.tmIndir); proceed { |
| // debugf(">>>> encoding.TextMarshaler: %T", rv.Interface()) |
| bs, fnerr := v.(encoding.TextMarshaler).MarshalText() |
| f.e.marshal(bs, fnerr, false, c_UTF8) |
| } |
| } |
| |
| func (f *encFnInfo) jsonMarshal(rv reflect.Value) { |
| if v, proceed := f.getValueForMarshalInterface(rv, f.ti.jmIndir); proceed { |
| bs, fnerr := v.(jsonMarshaler).MarshalJSON() |
| f.e.marshal(bs, fnerr, true, c_UTF8) |
| } |
| } |
| |
| func (f *encFnInfo) kBool(rv reflect.Value) { |
| f.e.e.EncodeBool(rv.Bool()) |
| } |
| |
| func (f *encFnInfo) kString(rv reflect.Value) { |
| f.e.e.EncodeString(c_UTF8, rv.String()) |
| } |
| |
| func (f *encFnInfo) kFloat64(rv reflect.Value) { |
| f.e.e.EncodeFloat64(rv.Float()) |
| } |
| |
| func (f *encFnInfo) kFloat32(rv reflect.Value) { |
| f.e.e.EncodeFloat32(float32(rv.Float())) |
| } |
| |
| func (f *encFnInfo) kInt(rv reflect.Value) { |
| f.e.e.EncodeInt(rv.Int()) |
| } |
| |
| func (f *encFnInfo) kUint(rv reflect.Value) { |
| f.e.e.EncodeUint(rv.Uint()) |
| } |
| |
| func (f *encFnInfo) kInvalid(rv reflect.Value) { |
| f.e.e.EncodeNil() |
| } |
| |
| func (f *encFnInfo) kErr(rv reflect.Value) { |
| f.e.errorf("unsupported kind %s, for %#v", rv.Kind(), rv) |
| } |
| |
| func (f *encFnInfo) kSlice(rv reflect.Value) { |
| ti := f.ti |
| // array may be non-addressable, so we have to manage with care |
| // (don't call rv.Bytes, rv.Slice, etc). |
| // E.g. type struct S{B [2]byte}; |
| // Encode(S{}) will bomb on "panic: slice of unaddressable array". |
| e := f.e |
| if f.seq != seqTypeArray { |
| if rv.IsNil() { |
| e.e.EncodeNil() |
| return |
| } |
| // If in this method, then there was no extension function defined. |
| // So it's okay to treat as []byte. |
| if ti.rtid == uint8SliceTypId { |
| e.e.EncodeStringBytes(c_RAW, rv.Bytes()) |
| return |
| } |
| } |
| cr := e.cr |
| rtelem := ti.rt.Elem() |
| l := rv.Len() |
| if ti.rtid == uint8SliceTypId || rtelem.Kind() == reflect.Uint8 { |
| switch f.seq { |
| case seqTypeArray: |
| // if l == 0 { e.e.encodeStringBytes(c_RAW, nil) } else |
| if rv.CanAddr() { |
| e.e.EncodeStringBytes(c_RAW, rv.Slice(0, l).Bytes()) |
| } else { |
| var bs []byte |
| if l <= cap(e.b) { |
| bs = e.b[:l] |
| } else { |
| bs = make([]byte, l) |
| } |
| reflect.Copy(reflect.ValueOf(bs), rv) |
| // TODO: Test that reflect.Copy works instead of manual one-by-one |
| // for i := 0; i < l; i++ { |
| // bs[i] = byte(rv.Index(i).Uint()) |
| // } |
| e.e.EncodeStringBytes(c_RAW, bs) |
| } |
| case seqTypeSlice: |
| e.e.EncodeStringBytes(c_RAW, rv.Bytes()) |
| case seqTypeChan: |
| bs := e.b[:0] |
| // do not use range, so that the number of elements encoded |
| // does not change, and encoding does not hang waiting on someone to close chan. |
| // for b := range rv.Interface().(<-chan byte) { |
| // bs = append(bs, b) |
| // } |
| ch := rv.Interface().(<-chan byte) |
| for i := 0; i < l; i++ { |
| bs = append(bs, <-ch) |
| } |
| e.e.EncodeStringBytes(c_RAW, bs) |
| } |
| return |
| } |
| |
| if ti.mbs { |
| if l%2 == 1 { |
| e.errorf("mapBySlice requires even slice length, but got %v", l) |
| return |
| } |
| e.e.EncodeMapStart(l / 2) |
| } else { |
| e.e.EncodeArrayStart(l) |
| } |
| |
| if l > 0 { |
| for rtelem.Kind() == reflect.Ptr { |
| rtelem = rtelem.Elem() |
| } |
| // if kind is reflect.Interface, do not pre-determine the |
| // encoding type, because preEncodeValue may break it down to |
| // a concrete type and kInterface will bomb. |
| var fn *encFn |
| if rtelem.Kind() != reflect.Interface { |
| rtelemid := reflect.ValueOf(rtelem).Pointer() |
| fn = e.getEncFn(rtelemid, rtelem, true, true) |
| } |
| // TODO: Consider perf implication of encoding odd index values as symbols if type is string |
| for j := 0; j < l; j++ { |
| if cr != nil { |
| if ti.mbs { |
| if j%2 == 0 { |
| cr.sendContainerState(containerMapKey) |
| } else { |
| cr.sendContainerState(containerMapValue) |
| } |
| } else { |
| cr.sendContainerState(containerArrayElem) |
| } |
| } |
| if f.seq == seqTypeChan { |
| if rv2, ok2 := rv.Recv(); ok2 { |
| e.encodeValue(rv2, fn) |
| } else { |
| e.encode(nil) // WE HAVE TO DO SOMETHING, so nil if nothing received. |
| } |
| } else { |
| e.encodeValue(rv.Index(j), fn) |
| } |
| } |
| } |
| |
| if cr != nil { |
| if ti.mbs { |
| cr.sendContainerState(containerMapEnd) |
| } else { |
| cr.sendContainerState(containerArrayEnd) |
| } |
| } |
| } |
| |
| func (f *encFnInfo) kStruct(rv reflect.Value) { |
| fti := f.ti |
| e := f.e |
| cr := e.cr |
| tisfi := fti.sfip |
| toMap := !(fti.toArray || e.h.StructToArray) |
| newlen := len(fti.sfi) |
| |
| // Use sync.Pool to reduce allocating slices unnecessarily. |
| // The cost of sync.Pool is less than the cost of new allocation. |
| pool, poolv, fkvs := encStructPoolGet(newlen) |
| |
| // if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct) |
| if toMap { |
| tisfi = fti.sfi |
| } |
| newlen = 0 |
| var kv stringRv |
| recur := e.h.RecursiveEmptyCheck |
| for _, si := range tisfi { |
| kv.r = si.field(rv, false) |
| if toMap { |
| if si.omitEmpty && isEmptyValue(kv.r, recur, recur) { |
| continue |
| } |
| kv.v = si.encName |
| } else { |
| // use the zero value. |
| // if a reference or struct, set to nil (so you do not output too much) |
| if si.omitEmpty && isEmptyValue(kv.r, recur, recur) { |
| switch kv.r.Kind() { |
| case reflect.Struct, reflect.Interface, reflect.Ptr, reflect.Array, reflect.Map, reflect.Slice: |
| kv.r = reflect.Value{} //encode as nil |
| } |
| } |
| } |
| fkvs[newlen] = kv |
| newlen++ |
| } |
| |
| // debugf(">>>> kStruct: newlen: %v", newlen) |
| // sep := !e.be |
| ee := e.e //don't dereference every time |
| |
| if toMap { |
| ee.EncodeMapStart(newlen) |
| // asSymbols := e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0 |
| asSymbols := e.h.AsSymbols == AsSymbolDefault || e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0 |
| for j := 0; j < newlen; j++ { |
| kv = fkvs[j] |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| if asSymbols { |
| ee.EncodeSymbol(kv.v) |
| } else { |
| ee.EncodeString(c_UTF8, kv.v) |
| } |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(kv.r, nil) |
| } |
| if cr != nil { |
| cr.sendContainerState(containerMapEnd) |
| } |
| } else { |
| ee.EncodeArrayStart(newlen) |
| for j := 0; j < newlen; j++ { |
| kv = fkvs[j] |
| if cr != nil { |
| cr.sendContainerState(containerArrayElem) |
| } |
| e.encodeValue(kv.r, nil) |
| } |
| if cr != nil { |
| cr.sendContainerState(containerArrayEnd) |
| } |
| } |
| |
| // do not use defer. Instead, use explicit pool return at end of function. |
| // defer has a cost we are trying to avoid. |
| // If there is a panic and these slices are not returned, it is ok. |
| if pool != nil { |
| pool.Put(poolv) |
| } |
| } |
| |
| // func (f *encFnInfo) kPtr(rv reflect.Value) { |
| // debugf(">>>>>>> ??? encode kPtr called - shouldn't get called") |
| // if rv.IsNil() { |
| // f.e.e.encodeNil() |
| // return |
| // } |
| // f.e.encodeValue(rv.Elem()) |
| // } |
| |
| // func (f *encFnInfo) kInterface(rv reflect.Value) { |
| // println("kInterface called") |
| // debug.PrintStack() |
| // if rv.IsNil() { |
| // f.e.e.EncodeNil() |
| // return |
| // } |
| // f.e.encodeValue(rv.Elem(), nil) |
| // } |
| |
| func (f *encFnInfo) kMap(rv reflect.Value) { |
| ee := f.e.e |
| if rv.IsNil() { |
| ee.EncodeNil() |
| return |
| } |
| |
| l := rv.Len() |
| ee.EncodeMapStart(l) |
| e := f.e |
| cr := e.cr |
| if l == 0 { |
| if cr != nil { |
| cr.sendContainerState(containerMapEnd) |
| } |
| return |
| } |
| var asSymbols bool |
| // determine the underlying key and val encFn's for the map. |
| // This eliminates some work which is done for each loop iteration i.e. |
| // rv.Type(), ref.ValueOf(rt).Pointer(), then check map/list for fn. |
| // |
| // However, if kind is reflect.Interface, do not pre-determine the |
| // encoding type, because preEncodeValue may break it down to |
| // a concrete type and kInterface will bomb. |
| var keyFn, valFn *encFn |
| ti := f.ti |
| rtkey := ti.rt.Key() |
| rtval := ti.rt.Elem() |
| rtkeyid := reflect.ValueOf(rtkey).Pointer() |
| // keyTypeIsString := f.ti.rt.Key().Kind() == reflect.String |
| var keyTypeIsString = rtkeyid == stringTypId |
| if keyTypeIsString { |
| asSymbols = e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0 |
| } else { |
| for rtkey.Kind() == reflect.Ptr { |
| rtkey = rtkey.Elem() |
| } |
| if rtkey.Kind() != reflect.Interface { |
| rtkeyid = reflect.ValueOf(rtkey).Pointer() |
| keyFn = e.getEncFn(rtkeyid, rtkey, true, true) |
| } |
| } |
| for rtval.Kind() == reflect.Ptr { |
| rtval = rtval.Elem() |
| } |
| if rtval.Kind() != reflect.Interface { |
| rtvalid := reflect.ValueOf(rtval).Pointer() |
| valFn = e.getEncFn(rtvalid, rtval, true, true) |
| } |
| mks := rv.MapKeys() |
| // for j, lmks := 0, len(mks); j < lmks; j++ { |
| |
| if e.h.Canonical { |
| e.kMapCanonical(rtkeyid, rtkey, rv, mks, valFn, asSymbols) |
| } else { |
| for j := range mks { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| if keyTypeIsString { |
| if asSymbols { |
| ee.EncodeSymbol(mks[j].String()) |
| } else { |
| ee.EncodeString(c_UTF8, mks[j].String()) |
| } |
| } else { |
| e.encodeValue(mks[j], keyFn) |
| } |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mks[j]), valFn) |
| } |
| } |
| if cr != nil { |
| cr.sendContainerState(containerMapEnd) |
| } |
| } |
| |
| func (e *Encoder) kMapCanonical(rtkeyid uintptr, rtkey reflect.Type, rv reflect.Value, mks []reflect.Value, valFn *encFn, asSymbols bool) { |
| ee := e.e |
| cr := e.cr |
| // we previously did out-of-band if an extension was registered. |
| // This is not necessary, as the natural kind is sufficient for ordering. |
| |
| if rtkeyid == uint8SliceTypId { |
| mksv := make([]bytesRv, len(mks)) |
| for i, k := range mks { |
| v := &mksv[i] |
| v.r = k |
| v.v = k.Bytes() |
| } |
| sort.Sort(bytesRvSlice(mksv)) |
| for i := range mksv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| ee.EncodeStringBytes(c_RAW, mksv[i].v) |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksv[i].r), valFn) |
| } |
| } else { |
| switch rtkey.Kind() { |
| case reflect.Bool: |
| mksv := make([]boolRv, len(mks)) |
| for i, k := range mks { |
| v := &mksv[i] |
| v.r = k |
| v.v = k.Bool() |
| } |
| sort.Sort(boolRvSlice(mksv)) |
| for i := range mksv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| ee.EncodeBool(mksv[i].v) |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksv[i].r), valFn) |
| } |
| case reflect.String: |
| mksv := make([]stringRv, len(mks)) |
| for i, k := range mks { |
| v := &mksv[i] |
| v.r = k |
| v.v = k.String() |
| } |
| sort.Sort(stringRvSlice(mksv)) |
| for i := range mksv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| if asSymbols { |
| ee.EncodeSymbol(mksv[i].v) |
| } else { |
| ee.EncodeString(c_UTF8, mksv[i].v) |
| } |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksv[i].r), valFn) |
| } |
| case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr: |
| mksv := make([]uintRv, len(mks)) |
| for i, k := range mks { |
| v := &mksv[i] |
| v.r = k |
| v.v = k.Uint() |
| } |
| sort.Sort(uintRvSlice(mksv)) |
| for i := range mksv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| ee.EncodeUint(mksv[i].v) |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksv[i].r), valFn) |
| } |
| case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
| mksv := make([]intRv, len(mks)) |
| for i, k := range mks { |
| v := &mksv[i] |
| v.r = k |
| v.v = k.Int() |
| } |
| sort.Sort(intRvSlice(mksv)) |
| for i := range mksv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| ee.EncodeInt(mksv[i].v) |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksv[i].r), valFn) |
| } |
| case reflect.Float32: |
| mksv := make([]floatRv, len(mks)) |
| for i, k := range mks { |
| v := &mksv[i] |
| v.r = k |
| v.v = k.Float() |
| } |
| sort.Sort(floatRvSlice(mksv)) |
| for i := range mksv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| ee.EncodeFloat32(float32(mksv[i].v)) |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksv[i].r), valFn) |
| } |
| case reflect.Float64: |
| mksv := make([]floatRv, len(mks)) |
| for i, k := range mks { |
| v := &mksv[i] |
| v.r = k |
| v.v = k.Float() |
| } |
| sort.Sort(floatRvSlice(mksv)) |
| for i := range mksv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| ee.EncodeFloat64(mksv[i].v) |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksv[i].r), valFn) |
| } |
| default: |
| // out-of-band |
| // first encode each key to a []byte first, then sort them, then record |
| var mksv []byte = make([]byte, 0, len(mks)*16) // temporary byte slice for the encoding |
| e2 := NewEncoderBytes(&mksv, e.hh) |
| mksbv := make([]bytesRv, len(mks)) |
| for i, k := range mks { |
| v := &mksbv[i] |
| l := len(mksv) |
| e2.MustEncode(k) |
| v.r = k |
| v.v = mksv[l:] |
| // fmt.Printf(">>>>> %s\n", mksv[l:]) |
| } |
| sort.Sort(bytesRvSlice(mksbv)) |
| for j := range mksbv { |
| if cr != nil { |
| cr.sendContainerState(containerMapKey) |
| } |
| e.asis(mksbv[j].v) |
| if cr != nil { |
| cr.sendContainerState(containerMapValue) |
| } |
| e.encodeValue(rv.MapIndex(mksbv[j].r), valFn) |
| } |
| } |
| } |
| } |
| |
| // -------------------------------------------------- |
| |
| // 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) |
| } |
| |
| // -------------------------------------------------- |
| |
| type encRtidFn struct { |
| rtid uintptr |
| fn encFn |
| } |
| |
| // An Encoder writes an object to an output stream in the codec format. |
| type Encoder struct { |
| // hopefully, reduce derefencing cost by laying the encWriter inside the Encoder |
| e encDriver |
| // NOTE: Encoder shouldn't call it's write methods, |
| // as the handler MAY need to do some coordination. |
| w encWriter |
| s []encRtidFn |
| ci set |
| be bool // is binary encoding |
| js bool // is json handle |
| |
| wi ioEncWriter |
| wb bytesEncWriter |
| |
| h *BasicHandle |
| hh Handle |
| |
| cr containerStateRecv |
| as encDriverAsis |
| |
| f map[uintptr]*encFn |
| b [scratchByteArrayLen]byte |
| } |
| |
| // 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 { |
| e := newEncoder(h) |
| e.Reset(w) |
| return e |
| } |
| |
| // 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 { |
| e := newEncoder(h) |
| e.ResetBytes(out) |
| return e |
| } |
| |
| func newEncoder(h Handle) *Encoder { |
| e := &Encoder{hh: h, h: h.getBasicHandle(), be: h.isBinary()} |
| _, e.js = h.(*JsonHandle) |
| e.e = h.newEncDriver(e) |
| e.as, _ = e.e.(encDriverAsis) |
| e.cr, _ = e.e.(containerStateRecv) |
| return e |
| } |
| |
| // Reset the Encoder with a new output stream. |
| // |
| // This accommodates using the state of the Encoder, |
| // where it has "cached" information about sub-engines. |
| func (e *Encoder) Reset(w io.Writer) { |
| ww, ok := w.(ioEncWriterWriter) |
| if ok { |
| e.wi.w = ww |
| } else { |
| sww := &e.wi.s |
| sww.w = w |
| sww.bw, _ = w.(io.ByteWriter) |
| sww.sw, _ = w.(ioEncStringWriter) |
| e.wi.w = sww |
| //ww = bufio.NewWriterSize(w, defEncByteBufSize) |
| } |
| e.w = &e.wi |
| e.e.reset() |
| } |
| |
| func (e *Encoder) ResetBytes(out *[]byte) { |
| in := *out |
| if in == nil { |
| in = make([]byte, defEncByteBufSize) |
| } |
| e.wb.b, e.wb.out, e.wb.c = in, out, 0 |
| e.w = &e.wb |
| e.e.reset() |
| } |
| |
| // func (e *Encoder) sendContainerState(c containerState) { |
| // if e.cr != nil { |
| // e.cr.sendContainerState(c) |
| // } |
| // } |
| |
| // Encode writes an object into a stream. |
| // |
| // Encoding can be configured via the 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. |
| // Note that the "json" key is used in the absence of the "codec" key. |
| // |
| // 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 tag is "-", OR |
| // - the field is empty (empty or the zero value) and its 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 tag on the _struct field sets the "toarray" option |
| // Note that omitempty is ignored when encoding struct values as arrays, |
| // as an entry must be encoded for each field, to maintain its position. |
| // |
| // 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 except: |
| // - the struct tag specifies a replacement name (first value) |
| // - the field is of an interface type |
| // |
| // Examples: |
| // |
| // // NOTE: 'json:' can be used as struct tag key, in place 'codec:' below. |
| // 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. |
| // io.Reader //use key "Reader". |
| // MyStruct `codec:"my1" //use key "my1". |
| // MyStruct //inline it |
| // ... |
| // } |
| // |
| // type MyStruct struct { |
| // _struct bool `codec:",toarray"` //encode struct as an array |
| // } |
| // |
| // The mode of encoding is based on the type of the value. When a value is seen: |
| // - If a Selfer, call its CodecEncodeSelf method |
| // - If an extension is registered for it, call that extension function |
| // - If it implements encoding.(Binary|Text|JSON)Marshaler, call its Marshal(Binary|Text|JSON) method |
| // - 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 |
| } |
| |
| // MustEncode is like Encode, but panics if unable to Encode. |
| // This provides insight to the code location that triggered the error. |
| func (e *Encoder) MustEncode(v interface{}) { |
| e.encode(v) |
| e.w.atEndOfEncode() |
| } |
| |
| func (e *Encoder) encode(iv interface{}) { |
| // if ics, ok := iv.(Selfer); ok { |
| // ics.CodecEncodeSelf(e) |
| // return |
| // } |
| |
| switch v := iv.(type) { |
| case nil: |
| e.e.EncodeNil() |
| case Selfer: |
| v.CodecEncodeSelf(e) |
| case Raw: |
| e.raw(v) |
| case reflect.Value: |
| e.encodeValue(v, nil) |
| |
| 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 []uint8: |
| e.e.EncodeStringBytes(c_RAW, 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 *[]uint8: |
| e.e.EncodeStringBytes(c_RAW, *v) |
| |
| default: |
| const checkCodecSelfer1 = true // in case T is passed, where *T is a Selfer, still checkCodecSelfer |
| if !fastpathEncodeTypeSwitch(iv, e) { |
| e.encodeI(iv, false, checkCodecSelfer1) |
| } |
| } |
| } |
| |
| func (e *Encoder) preEncodeValue(rv reflect.Value) (rv2 reflect.Value, sptr uintptr, proceed bool) { |
| // use a goto statement instead of a recursive function for ptr/interface. |
| TOP: |
| switch rv.Kind() { |
| case reflect.Ptr: |
| if rv.IsNil() { |
| e.e.EncodeNil() |
| return |
| } |
| rv = rv.Elem() |
| if e.h.CheckCircularRef && rv.Kind() == reflect.Struct { |
| // TODO: Movable pointers will be an issue here. Future problem. |
| sptr = rv.UnsafeAddr() |
| break TOP |
| } |
| goto TOP |
| case reflect.Interface: |
| if rv.IsNil() { |
| e.e.EncodeNil() |
| return |
| } |
| rv = rv.Elem() |
| goto TOP |
| case reflect.Slice, reflect.Map: |
| if rv.IsNil() { |
| e.e.EncodeNil() |
| return |
| } |
| case reflect.Invalid, reflect.Func: |
| e.e.EncodeNil() |
| return |
| } |
| |
| proceed = true |
| rv2 = rv |
| return |
| } |
| |
| func (e *Encoder) doEncodeValue(rv reflect.Value, fn *encFn, sptr uintptr, |
| checkFastpath, checkCodecSelfer bool) { |
| if sptr != 0 { |
| if (&e.ci).add(sptr) { |
| e.errorf("circular reference found: # %d", sptr) |
| } |
| } |
| if fn == nil { |
| rt := rv.Type() |
| rtid := reflect.ValueOf(rt).Pointer() |
| // fn = e.getEncFn(rtid, rt, true, true) |
| fn = e.getEncFn(rtid, rt, checkFastpath, checkCodecSelfer) |
| } |
| fn.f(&fn.i, rv) |
| if sptr != 0 { |
| (&e.ci).remove(sptr) |
| } |
| } |
| |
| func (e *Encoder) encodeI(iv interface{}, checkFastpath, checkCodecSelfer bool) { |
| if rv, sptr, proceed := e.preEncodeValue(reflect.ValueOf(iv)); proceed { |
| e.doEncodeValue(rv, nil, sptr, checkFastpath, checkCodecSelfer) |
| } |
| } |
| |
| func (e *Encoder) encodeValue(rv reflect.Value, fn *encFn) { |
| // if a valid fn is passed, it MUST BE for the dereferenced type of rv |
| if rv, sptr, proceed := e.preEncodeValue(rv); proceed { |
| e.doEncodeValue(rv, fn, sptr, true, true) |
| } |
| } |
| |
| func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCodecSelfer bool) (fn *encFn) { |
| // rtid := reflect.ValueOf(rt).Pointer() |
| var ok bool |
| if useMapForCodecCache { |
| fn, ok = e.f[rtid] |
| } else { |
| for i := range e.s { |
| v := &(e.s[i]) |
| if v.rtid == rtid { |
| fn, ok = &(v.fn), true |
| break |
| } |
| } |
| } |
| if ok { |
| return |
| } |
| |
| if useMapForCodecCache { |
| if e.f == nil { |
| e.f = make(map[uintptr]*encFn, initCollectionCap) |
| } |
| fn = new(encFn) |
| e.f[rtid] = fn |
| } else { |
| if e.s == nil { |
| e.s = make([]encRtidFn, 0, initCollectionCap) |
| } |
| e.s = append(e.s, encRtidFn{rtid: rtid}) |
| fn = &(e.s[len(e.s)-1]).fn |
| } |
| |
| ti := e.h.getTypeInfo(rtid, rt) |
| fi := &(fn.i) |
| fi.e = e |
| fi.ti = ti |
| |
| if checkCodecSelfer && ti.cs { |
| fn.f = (*encFnInfo).selferMarshal |
| } else if rtid == rawTypId { |
| fn.f = (*encFnInfo).raw |
| } else if rtid == rawExtTypId { |
| fn.f = (*encFnInfo).rawExt |
| } else if e.e.IsBuiltinType(rtid) { |
| fn.f = (*encFnInfo).builtin |
| } else if xfFn := e.h.getExt(rtid); xfFn != nil { |
| fi.xfTag, fi.xfFn = xfFn.tag, xfFn.ext |
| fn.f = (*encFnInfo).ext |
| } else if supportMarshalInterfaces && e.be && ti.bm { |
| fn.f = (*encFnInfo).binaryMarshal |
| } else if supportMarshalInterfaces && !e.be && e.js && ti.jm { |
| //If JSON, we should check JSONMarshal before textMarshal |
| fn.f = (*encFnInfo).jsonMarshal |
| } else if supportMarshalInterfaces && !e.be && ti.tm { |
| fn.f = (*encFnInfo).textMarshal |
| } else { |
| rk := rt.Kind() |
| if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) { |
| if rt.PkgPath() == "" { // un-named slice or map |
| if idx := fastpathAV.index(rtid); idx != -1 { |
| fn.f = fastpathAV[idx].encfn |
| } |
| } else { |
| ok = false |
| // use mapping for underlying type if there |
| var rtu reflect.Type |
| if rk == reflect.Map { |
| rtu = reflect.MapOf(rt.Key(), rt.Elem()) |
| } else { |
| rtu = reflect.SliceOf(rt.Elem()) |
| } |
| rtuid := reflect.ValueOf(rtu).Pointer() |
| if idx := fastpathAV.index(rtuid); idx != -1 { |
| xfnf := fastpathAV[idx].encfn |
| xrt := fastpathAV[idx].rt |
| fn.f = func(xf *encFnInfo, xrv reflect.Value) { |
| xfnf(xf, xrv.Convert(xrt)) |
| } |
| } |
| } |
| } |
| if fn.f == nil { |
| switch 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, reflect.Uintptr: |
| fn.f = (*encFnInfo).kUint |
| case reflect.Invalid: |
| fn.f = (*encFnInfo).kInvalid |
| case reflect.Chan: |
| fi.seq = seqTypeChan |
| fn.f = (*encFnInfo).kSlice |
| case reflect.Slice: |
| fi.seq = seqTypeSlice |
| fn.f = (*encFnInfo).kSlice |
| case reflect.Array: |
| fi.seq = seqTypeArray |
| fn.f = (*encFnInfo).kSlice |
| case reflect.Struct: |
| fn.f = (*encFnInfo).kStruct |
| // reflect.Ptr and reflect.Interface are handled already by preEncodeValue |
| // 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 |
| } |
| } |
| } |
| |
| return |
| } |
| |
| func (e *Encoder) marshal(bs []byte, fnerr error, asis bool, c charEncoding) { |
| if fnerr != nil { |
| panic(fnerr) |
| } |
| if bs == nil { |
| e.e.EncodeNil() |
| } else if asis { |
| e.asis(bs) |
| } else { |
| e.e.EncodeStringBytes(c, bs) |
| } |
| } |
| |
| func (e *Encoder) asis(v []byte) { |
| if e.as == nil { |
| e.w.writeb(v) |
| } else { |
| e.as.EncodeAsis(v) |
| } |
| } |
| |
| func (e *Encoder) raw(vv Raw) { |
| v := []byte(vv) |
| if !e.h.Raw { |
| e.errorf("Raw values cannot be encoded: %v", v) |
| } |
| if e.as == nil { |
| e.w.writeb(v) |
| } else { |
| e.as.EncodeAsis(v) |
| } |
| } |
| |
| func (e *Encoder) errorf(format string, params ...interface{}) { |
| err := fmt.Errorf(format, params...) |
| panic(err) |
| } |
| |
| // ---------------------------------------- |
| |
| const encStructPoolLen = 5 |
| |
| // encStructPool is an array of sync.Pool. |
| // Each element of the array pools one of encStructPool(8|16|32|64). |
| // It allows the re-use of slices up to 64 in length. |
| // A performance cost of encoding structs was collecting |
| // which values were empty and should be omitted. |
| // We needed slices of reflect.Value and string to collect them. |
| // This shared pool reduces the amount of unnecessary creation we do. |
| // The cost is that of locking sometimes, but sync.Pool is efficient |
| // enough to reduce thread contention. |
| var encStructPool [encStructPoolLen]sync.Pool |
| |
| func init() { |
| encStructPool[0].New = func() interface{} { return new([8]stringRv) } |
| encStructPool[1].New = func() interface{} { return new([16]stringRv) } |
| encStructPool[2].New = func() interface{} { return new([32]stringRv) } |
| encStructPool[3].New = func() interface{} { return new([64]stringRv) } |
| encStructPool[4].New = func() interface{} { return new([128]stringRv) } |
| } |
| |
| func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []stringRv) { |
| // if encStructPoolLen != 5 { // constant chec, so removed at build time. |
| // panic(errors.New("encStructPoolLen must be equal to 4")) // defensive, in case it is changed |
| // } |
| // idxpool := newlen / 8 |
| if newlen <= 8 { |
| p = &encStructPool[0] |
| v = p.Get() |
| s = v.(*[8]stringRv)[:newlen] |
| } else if newlen <= 16 { |
| p = &encStructPool[1] |
| v = p.Get() |
| s = v.(*[16]stringRv)[:newlen] |
| } else if newlen <= 32 { |
| p = &encStructPool[2] |
| v = p.Get() |
| s = v.(*[32]stringRv)[:newlen] |
| } else if newlen <= 64 { |
| p = &encStructPool[3] |
| v = p.Get() |
| s = v.(*[64]stringRv)[:newlen] |
| } else if newlen <= 128 { |
| p = &encStructPool[4] |
| v = p.Get() |
| s = v.(*[128]stringRv)[:newlen] |
| } else { |
| s = make([]stringRv, newlen) |
| } |
| return |
| } |
| |
| // ---------------------------------------- |
| |
| // func encErr(format string, params ...interface{}) { |
| // doPanic(msgTagEnc, format, params...) |
| // } |