浏览代码

codec: re-factor and streamline code for publishing standards, consistency and performance

 - separate typical json encoding from generic one (to make it faster).
   hopefully makes the common case faster, by eliding some state management and branches
 - clean up decNaked data structure and handling
 - organize structures and add padding, to possibly reduce false sharing
 - make codecFner a pooled resource, so the state it stores can be reused across multiple (en|de)coders
 - simplify codecFner - so it stores pointers to codecFn, and just uses a single-dimensional array to store them.
   Also, codecFner only needs *BasicHandle to check if extensions registered or find typeInfo
 - make some structures which are not used much, pointers (lazily initialized if needed)
 - use extWrapper, replacing setExtWrapper
 - remove many old comments
 - moved time.go into binc.go - as binc.go was the only consumer of the functions in time.go.
Ugorji Nwoke 8 年之前
父节点
当前提交
4ad9ed71b1
共有 16 个文件被更改,包括 972 次插入1044 次删除
  1. 12 0
      codec/0doc.go
  2. 164 35
      codec/binc.go
  3. 1 5
      codec/cbor.go
  4. 5 3
      codec/codec_test.go
  5. 136 136
      codec/decode.go
  6. 53 120
      codec/encode.go
  7. 1 1
      codec/fast-path.not.go
  8. 6 6
      codec/gen-helper.generated.go
  9. 5 5
      codec/gen-helper.go.tmpl
  10. 19 92
      codec/gen.go
  11. 240 226
      codec/helper.go
  12. 326 204
      codec/json.go
  13. 2 21
      codec/msgpack.go
  14. 1 1
      codec/simple.go
  15. 0 188
      codec/time.go
  16. 1 1
      codec/xml.go

+ 12 - 0
codec/0doc.go

@@ -233,3 +233,15 @@ package codec
 //     as we call panic to raise errors, and panic currently prevents inlining.
 //   - Clean up comments in the codebase
 //     Remove all unnecesssary comments, so code is clean.
+//
+// PUNTED:
+//   - To make Handle comparable, make extHandle in BasicHandle a non-embedded pointer,
+//     and use overlay methods on *BasicHandle to call through to extHandle after initializing
+//     the "xh *extHandle" to point to a real slice.
+//   - Before each release, look through and fix padding for each type, to eliminate false sharing
+//     - pooled objects: decNaked, codecFner, typeInfoLoadArray, typeInfo,
+//     - small objects that we allocate and modify much (should be in owned cache lines)
+//     - Objects used a lot (must live in own cache lines)
+//       Decoder, Encoder, etc
+//     - In all above, arrange values modified together to be close to each other.
+//     Note: we MOSTLY care about the bottom part.

+ 164 - 35
codec/binc.go

@@ -68,27 +68,15 @@ type bincEncDriver struct {
 	// encNoSeparator
 }
 
-// func (e *bincEncDriver) IsBuiltinType(rt uintptr) bool {
-// 	return rt == timeTypId
-// }
-
 func (e *bincEncDriver) EncodeNil() {
 	e.w.writen1(bincVdSpecial<<4 | bincSpNil)
 }
 
-// func (e *bincEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {
-// 	if rt == timeTypId {
-// 		e.EncodeTime(v.(time.Time))
-// 		return
-// 	}
-// 	e.e.errorf("binc error encoding builtin: expect time.Time, received %T", v)
-// }
-
 func (e *bincEncDriver) EncodeTime(t time.Time) {
 	if t.IsZero() {
 		e.EncodeNil()
 	} else {
-		bs := encodeTime(t)
+		bs := bincEncodeTime(t)
 		e.w.writen1(bincVdTimestamp<<4 | uint8(len(bs)))
 		e.w.writeb(bs)
 	}
@@ -405,10 +393,6 @@ func (d *bincDecDriver) TryDecodeAsNil() bool {
 	return false
 }
 
-// func (d *bincDecDriver) IsBuiltinType(rt uintptr) bool {
-// 	return rt == timeTypId
-// }
-
 func (d *bincDecDriver) DecodeTime() (t time.Time) {
 	if !d.bdRead {
 		d.readNextBd()
@@ -421,7 +405,7 @@ func (d *bincDecDriver) DecodeTime() (t time.Time) {
 		d.d.errorf("Invalid d.vd. Expecting 0x%x. Received: 0x%x", bincVdTimestamp, d.vd)
 		return
 	}
-	t, err := decodeTime(d.r.readx(int(d.vs)))
+	t, err := bincDecodeTime(d.r.readx(int(d.vs)))
 	if err != nil {
 		panic(err)
 	}
@@ -429,15 +413,6 @@ func (d *bincDecDriver) DecodeTime() (t time.Time) {
 	return
 }
 
-// func (d *bincDecDriver) DecodeBuiltin(rt uintptr, v interface{}) {
-// 	if rt == timeTypId {
-// 		var vt = v.(*time.Time)
-// 		*vt = d.DecodeTime()
-// 		return
-// 	}
-// 	d.d.errorf("binc error decoding builtin: expect *time.Time, received %T", v)
-// }
-
 func (d *bincDecDriver) decFloatPre(vs, defaultLen byte) {
 	if vs&0x8 == 0 {
 		d.r.readb(d.b[0:defaultLen])
@@ -880,7 +855,7 @@ func (d *bincDecDriver) DecodeNaked() {
 		n.l = d.DecodeBytes(nil, false)
 	case bincVdTimestamp:
 		n.v = valueTypeTime
-		tt, err := decodeTime(d.r.readx(int(d.vs)))
+		tt, err := bincDecodeTime(d.r.readx(int(d.vs)))
 		if err != nil {
 			panic(err)
 		}
@@ -955,7 +930,7 @@ func (h *BincHandle) Name() string { return "binc" }
 
 // SetBytesExt sets an extension
 func (h *BincHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
-	return h.SetExt(rt, tag, &setExtWrapper{b: ext})
+	return h.SetExt(rt, tag, &extWrapper{ext, interfaceExtFailer{}})
 }
 
 func (h *BincHandle) newEncDriver(e *Encoder) encDriver {
@@ -966,12 +941,6 @@ func (h *BincHandle) newDecDriver(d *Decoder) decDriver {
 	return &bincDecDriver{d: d, h: h, r: d.r, br: d.bytes}
 }
 
-// // IsBuiltinType returns true for time.Time, else false.
-// // only time.Time is builtin.
-// func (h *BincHandle) IsBuiltinType(rt uintptr) bool {
-// 	return rt == timeTypId
-// }
-
 func (e *bincEncDriver) reset() {
 	e.w = e.e.w
 	e.s = 0
@@ -985,5 +954,165 @@ func (d *bincDecDriver) reset() {
 	d.bd, d.bdRead, d.vd, d.vs = 0, false, 0, 0
 }
 
+// var timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
+
+// EncodeTime encodes a time.Time as a []byte, including
+// information on the instant in time and UTC offset.
+//
+// Format Description
+//
+//   A timestamp is composed of 3 components:
+//
+//   - secs: signed integer representing seconds since unix epoch
+//   - nsces: unsigned integer representing fractional seconds as a
+//     nanosecond offset within secs, in the range 0 <= nsecs < 1e9
+//   - tz: signed integer representing timezone offset in minutes east of UTC,
+//     and a dst (daylight savings time) flag
+//
+//   When encoding a timestamp, the first byte is the descriptor, which
+//   defines which components are encoded and how many bytes are used to
+//   encode secs and nsecs components. *If secs/nsecs is 0 or tz is UTC, it
+//   is not encoded in the byte array explicitly*.
+//
+//       Descriptor 8 bits are of the form `A B C DDD EE`:
+//           A:   Is secs component encoded? 1 = true
+//           B:   Is nsecs component encoded? 1 = true
+//           C:   Is tz component encoded? 1 = true
+//           DDD: Number of extra bytes for secs (range 0-7).
+//                If A = 1, secs encoded in DDD+1 bytes.
+//                    If A = 0, secs is not encoded, and is assumed to be 0.
+//                    If A = 1, then we need at least 1 byte to encode secs.
+//                    DDD says the number of extra bytes beyond that 1.
+//                    E.g. if DDD=0, then secs is represented in 1 byte.
+//                         if DDD=2, then secs is represented in 3 bytes.
+//           EE:  Number of extra bytes for nsecs (range 0-3).
+//                If B = 1, nsecs encoded in EE+1 bytes (similar to secs/DDD above)
+//
+//   Following the descriptor bytes, subsequent bytes are:
+//
+//       secs component encoded in `DDD + 1` bytes (if A == 1)
+//       nsecs component encoded in `EE + 1` bytes (if B == 1)
+//       tz component encoded in 2 bytes (if C == 1)
+//
+//   secs and nsecs components are integers encoded in a BigEndian
+//   2-complement encoding format.
+//
+//   tz component is encoded as 2 bytes (16 bits). Most significant bit 15 to
+//   Least significant bit 0 are described below:
+//
+//       Timezone offset has a range of -12:00 to +14:00 (ie -720 to +840 minutes).
+//       Bit 15 = have\_dst: set to 1 if we set the dst flag.
+//       Bit 14 = dst\_on: set to 1 if dst is in effect at the time, or 0 if not.
+//       Bits 13..0 = timezone offset in minutes. It is a signed integer in Big Endian format.
+//
+func bincEncodeTime(t time.Time) []byte {
+	//t := rv.Interface().(time.Time)
+	tsecs, tnsecs := t.Unix(), t.Nanosecond()
+	var (
+		bd   byte
+		btmp [8]byte
+		bs   [16]byte
+		i    int = 1
+	)
+	l := t.Location()
+	if l == time.UTC {
+		l = nil
+	}
+	if tsecs != 0 {
+		bd = bd | 0x80
+		bigen.PutUint64(btmp[:], uint64(tsecs))
+		f := pruneSignExt(btmp[:], tsecs >= 0)
+		bd = bd | (byte(7-f) << 2)
+		copy(bs[i:], btmp[f:])
+		i = i + (8 - f)
+	}
+	if tnsecs != 0 {
+		bd = bd | 0x40
+		bigen.PutUint32(btmp[:4], uint32(tnsecs))
+		f := pruneSignExt(btmp[:4], true)
+		bd = bd | byte(3-f)
+		copy(bs[i:], btmp[f:4])
+		i = i + (4 - f)
+	}
+	if l != nil {
+		bd = bd | 0x20
+		// Note that Go Libs do not give access to dst flag.
+		_, zoneOffset := t.Zone()
+		//zoneName, zoneOffset := t.Zone()
+		zoneOffset /= 60
+		z := uint16(zoneOffset)
+		bigen.PutUint16(btmp[:2], z)
+		// clear dst flags
+		bs[i] = btmp[0] & 0x3f
+		bs[i+1] = btmp[1]
+		i = i + 2
+	}
+	bs[0] = bd
+	return bs[0:i]
+}
+
+// bincDecodeTime decodes a []byte into a time.Time.
+func bincDecodeTime(bs []byte) (tt time.Time, err error) {
+	bd := bs[0]
+	var (
+		tsec  int64
+		tnsec uint32
+		tz    uint16
+		i     byte = 1
+		i2    byte
+		n     byte
+	)
+	if bd&(1<<7) != 0 {
+		var btmp [8]byte
+		n = ((bd >> 2) & 0x7) + 1
+		i2 = i + n
+		copy(btmp[8-n:], bs[i:i2])
+		//if first bit of bs[i] is set, then fill btmp[0..8-n] with 0xff (ie sign extend it)
+		if bs[i]&(1<<7) != 0 {
+			copy(btmp[0:8-n], bsAll0xff)
+			//for j,k := byte(0), 8-n; j < k; j++ {	btmp[j] = 0xff }
+		}
+		i = i2
+		tsec = int64(bigen.Uint64(btmp[:]))
+	}
+	if bd&(1<<6) != 0 {
+		var btmp [4]byte
+		n = (bd & 0x3) + 1
+		i2 = i + n
+		copy(btmp[4-n:], bs[i:i2])
+		i = i2
+		tnsec = bigen.Uint32(btmp[:])
+	}
+	if bd&(1<<5) == 0 {
+		tt = time.Unix(tsec, int64(tnsec)).UTC()
+		return
+	}
+	// In stdlib time.Parse, when a date is parsed without a zone name, it uses "" as zone name.
+	// However, we need name here, so it can be shown when time is printed.
+	// Zone name is in form: UTC-08:00.
+	// Note that Go Libs do not give access to dst flag, so we ignore dst bits
+
+	i2 = i + 2
+	tz = bigen.Uint16(bs[i:i2])
+	// i = i2
+	// sign extend sign bit into top 2 MSB (which were dst bits):
+	if tz&(1<<13) == 0 { // positive
+		tz = tz & 0x3fff //clear 2 MSBs: dst bits
+	} else { // negative
+		tz = tz | 0xc000 //set 2 MSBs: dst bits
+	}
+	tzint := int16(tz)
+	if tzint == 0 {
+		tt = time.Unix(tsec, int64(tnsec)).UTC()
+	} else {
+		// For Go Time, do not use a descriptive timezone.
+		// It's unnecessary, and makes it harder to do a reflect.DeepEqual.
+		// The Offset already tells what the offset should be, if not on UTC and unknown zone name.
+		// var zoneName = timeLocUTCName(tzint)
+		tt = time.Unix(tsec, int64(tnsec)).In(time.FixedZone("", int(tzint)*60))
+	}
+	return
+}
+
 var _ decDriver = (*bincDecDriver)(nil)
 var _ encDriver = (*bincEncDriver)(nil)

+ 1 - 5
codec/cbor.go

@@ -193,10 +193,6 @@ func (e *cborEncDriver) WriteArrayEnd() {
 	}
 }
 
-// func (e *cborEncDriver) EncodeSymbol(v string) {
-// 	e.encStringBytesS(cborBaseString, v)
-// }
-
 func (e *cborEncDriver) EncodeString(c charEncoding, v string) {
 	e.encStringBytesS(cborBaseString, v)
 }
@@ -687,7 +683,7 @@ func (h *CborHandle) Name() string { return "cbor" }
 
 // SetInterfaceExt sets an extension
 func (h *CborHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
-	return h.SetExt(rt, tag, &setExtWrapper{i: ext})
+	return h.SetExt(rt, tag, &extWrapper{bytesExtFailer{}, ext})
 }
 
 func (h *CborHandle) newEncDriver(e *Encoder) encDriver {

+ 5 - 3
codec/codec_test.go

@@ -274,21 +274,23 @@ func (x *wrapBytesExt) UpdateExt(dest interface{}, v interface{}) {
 
 // ----
 
+// timeExt is an extension handler for time.Time, that uses binc model for encoding/decoding time.
+// we used binc model, as that is the only custom time representation that we designed ourselves.
 type timeExt struct{}
 
 func (x timeExt) WriteExt(v interface{}) (bs []byte) {
 	switch v2 := v.(type) {
 	case time.Time:
-		bs = encodeTime(v2)
+		bs = bincEncodeTime(v2)
 	case *time.Time:
-		bs = encodeTime(*v2)
+		bs = bincEncodeTime(*v2)
 	default:
 		panic(fmt.Errorf("unsupported format for time conversion: expecting time.Time; got %T", v2))
 	}
 	return
 }
 func (x timeExt) ReadExt(v interface{}, bs []byte) {
-	tt, err := decodeTime(bs)
+	tt, err := bincDecodeTime(bs)
 	if err != nil {
 		panic(err)
 	}

+ 136 - 136
codec/decode.go

@@ -154,6 +154,11 @@ type DecodeOptions struct {
 	// Instead, we provision up to MaxInitLen, fill that up, and start appending after that.
 	MaxInitLen int
 
+	// ReaderBufferSize is the size of the buffer used when reading.
+	//
+	// if > 0, we use a smart buffer internally for performance purposes.
+	ReaderBufferSize int
+
 	// If ErrorIfNoField, return an error when decoding a map
 	// from a codec stream into a struct, and no matching struct field is found.
 	ErrorIfNoField bool
@@ -224,11 +229,6 @@ type DecodeOptions struct {
 	// If true, we will delete the mapping of the key.
 	// Else, just set the mapping to the zero value of the type.
 	DeleteOnNilMapValue bool
-
-	// ReaderBufferSize is the size of the buffer used when reading.
-	//
-	// if > 0, we use a smart buffer internally for performance purposes.
-	ReaderBufferSize int
 }
 
 // ------------------------------------
@@ -241,10 +241,9 @@ type bufioDecReader struct {
 	n   int // num read
 	err error
 
-	trb bool
 	tr  []byte
-
-	b [8]byte
+	trb bool
+	b   [4]byte
 }
 
 func (z *bufioDecReader) reset(r io.Reader) {
@@ -544,16 +543,15 @@ type ioDecReader struct {
 	rr io.Reader
 	br io.ByteScanner
 
-	l   byte    // last byte
-	ls  byte    // last byte status. 0: init-canDoNothing, 1: canRead, 2: canUnread
+	l   byte // last byte
+	ls  byte // last byte status. 0: init-canDoNothing, 1: canRead, 2: canUnread
+	trb bool // tracking bytes turned on
+	_   bool
 	b   [4]byte // tiny buffer for reading single bytes
-	trb bool    // tracking bytes turned on
 
-	// temp byte array re-used internally for efficiency during read.
-	// shares buffer with Decoder, so we keep size of struct within 8 words.
-	x  *[scratchByteArrayLen]byte
-	n  int    // num read
-	tr []byte // tracking bytes read
+	x  [scratchByteArrayLen]byte // for: get struct field name, swallow valueTypeBytes, etc
+	n  int                       // num read
+	tr []byte                    // tracking bytes read
 }
 
 func (z *ioDecReader) reset(r io.Reader) {
@@ -972,9 +970,10 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 			}
 		}
 		if mtid == mapIntfIntfTypId {
+			n.initContainers()
 			if n.lm < arrayCacheLen {
 				n.ma[n.lm] = nil
-				rvn = n.rr[decNakedMapIntfIntfIdx*arrayCacheLen+n.lm]
+				rvn = n.rma[n.lm]
 				n.lm++
 				d.decode(&n.ma[n.lm-1])
 				n.lm--
@@ -984,9 +983,10 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 				rvn = reflect.ValueOf(&v2).Elem()
 			}
 		} else if mtid == mapStrIntfTypId { // for json performance
+			n.initContainers()
 			if n.ln < arrayCacheLen {
 				n.na[n.ln] = nil
-				rvn = n.rr[decNakedMapStrIntfIdx*arrayCacheLen+n.ln]
+				rvn = n.rna[n.ln]
 				n.ln++
 				d.decode(&n.na[n.ln-1])
 				n.ln--
@@ -1007,9 +1007,10 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 		}
 	case valueTypeArray:
 		if d.stid == 0 || d.stid == intfSliceTypId {
+			n.initContainers()
 			if n.ls < arrayCacheLen {
 				n.sa[n.ls] = nil
-				rvn = n.rr[decNakedSliceIntfIdx*arrayCacheLen+n.ls]
+				rvn = n.rsa[n.ls]
 				n.ls++
 				d.decode(&n.sa[n.ls-1])
 				n.ls--
@@ -1037,6 +1038,7 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 		var v interface{}
 		tag, bytes := n.u, n.l // calling decode below might taint the values
 		if bytes == nil {
+			n.initContainers()
 			if n.li < arrayCacheLen {
 				n.ia[n.li] = nil
 				n.li++
@@ -1068,19 +1070,19 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 	case valueTypeNil:
 		// no-op
 	case valueTypeInt:
-		rvn = n.rr[decNakedIntIdx] // d.np.get(&n.i)
+		rvn = n.ri
 	case valueTypeUint:
-		rvn = n.rr[decNakedUintIdx] // d.np.get(&n.u)
+		rvn = n.ru
 	case valueTypeFloat:
-		rvn = n.rr[decNakedFloatIdx] // d.np.get(&n.f)
+		rvn = n.rf
 	case valueTypeBool:
-		rvn = n.rr[decNakedBoolIdx] // d.np.get(&n.b)
+		rvn = n.rb
 	case valueTypeString, valueTypeSymbol:
-		rvn = n.rr[decNakedStringIdx] // d.np.get(&n.s)
+		rvn = n.rs
 	case valueTypeBytes:
-		rvn = n.rr[decNakedBytesIdx] // d.np.get(&n.l)
+		rvn = n.rl
 	case valueTypeTime:
-		rvn = n.rr[decNakedTimeIdx] // d.np.get(&n.t)
+		rvn = n.rt
 	default:
 		panicv.errorf("kInterfaceNaked: unexpected valueType: %d", n.v)
 	}
@@ -1152,20 +1154,6 @@ func decStructFieldKey(dd decDriver, keyType valueType, b *[scratchByteArrayLen]
 		rvkencname = dd.DecodeStringAsBytes()
 	}
 	return rvkencname
-
-	// switch keyType {
-	// case valueTypeString:
-	// 	return dd.DecodeStringAsBytes()
-	// case valueTypeInt:
-	// 	return strconv.AppendInt(b[:0], dd.DecodeInt64(), 10)
-	// case valueTypeUint:
-	// 	return strconv.AppendUint(b[:0], dd.DecodeUint64(), 10)
-	// case valueTypeFloat:
-	// 	return strconv.AppendFloat(b[:0], dd.DecodeFloat64(), 'f', -1, 64)
-	// 	// default: // string
-	// 	// 	return dd.DecodeStringAsBytes()
-	// }
-	// return dd.DecodeStringAsBytes()
 }
 
 func (d *Decoder) kStruct(f *codecFnInfo, rv reflect.Value) {
@@ -1362,6 +1350,7 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 	var rtelem0ZeroValid bool
 	var decodeAsNil bool
 	var j int
+	d.cfer()
 	for ; (hasLen && j < containerLenS) || !(hasLen || dd.CheckBreak()); j++ {
 		if j == 0 && (f.seq == seqTypeSlice || f.seq == seqTypeChan) && rv.IsNil() {
 			if hasLen {
@@ -1520,6 +1509,7 @@ func (d *Decoder) kMap(f *codecFnInfo, rv reflect.Value) {
 	ktypeIsIntf := ktypeId == intfTypId
 	hasLen := containerLen > 0
 	var kstrbs []byte
+	d.cfer()
 	for j := 0; (hasLen && j < containerLen) || !(hasLen || dd.CheckBreak()); j++ {
 		if rvkMut || !rvkp.IsValid() {
 			rvkp = reflect.New(ktype)
@@ -1643,78 +1633,80 @@ func (d *Decoder) kMap(f *codecFnInfo, rv reflect.Value) {
 //
 // kInterfaceNaked will ensure that there is no allocation for the common
 // uses.
+
+type decNakedContainers struct {
+	// array/stacks for reducing allocation
+	// keep arrays at the bottom? Chance is that they are not used much.
+	ia [arrayCacheLen]interface{}
+	ma [arrayCacheLen]map[interface{}]interface{}
+	na [arrayCacheLen]map[string]interface{}
+	sa [arrayCacheLen][]interface{}
+
+	// ria [arrayCacheLen]reflect.Value // not needed, as we decode directly into &ia[n]
+	rma, rna, rsa [arrayCacheLen]reflect.Value // reflect.Value mapping to above
+}
+
+func (n *decNakedContainers) init() {
+	for i := 0; i < arrayCacheLen; i++ {
+		// n.ria[i] = reflect.ValueOf(&(n.ia[i])).Elem()
+		n.rma[i] = reflect.ValueOf(&(n.ma[i])).Elem()
+		n.rna[i] = reflect.ValueOf(&(n.na[i])).Elem()
+		n.rsa[i] = reflect.ValueOf(&(n.sa[i])).Elem()
+	}
+}
+
 type decNaked struct {
 	// r RawExt // used for RawExt, uint, []byte.
+
+	// primitives below
 	u uint64
 	i int64
 	f float64
 	l []byte
 	s string
-	t time.Time
 
+	// ---- cpu cache line boundary?
+	t time.Time
 	b bool
 
-	inited bool
+	// state
+	v              valueType
+	li, lm, ln, ls int8
+	inited         bool
 
-	v valueType
+	*decNakedContainers
 
-	li, lm, ln, ls int8
+	ru reflect.Value // map to primitive above
 
-	// array/stacks for reducing allocation
-	// keep arrays at the bottom? Chance is that they are not used much.
-	ia [arrayCacheLen]interface{}
-	ma [arrayCacheLen]map[interface{}]interface{}
-	na [arrayCacheLen]map[string]interface{}
-	sa [arrayCacheLen][]interface{}
-	// ra [2]RawExt
+	// ---- cpu cache line boundary?
+	ri, rf, rl, rs, rt, rb reflect.Value // mapping to the primitives above
 
-	rr [5 * arrayCacheLen]reflect.Value
+	// _ [6 * wordSize]byte // padding // TODO: ??? too big padding???
 }
 
-const (
-	decNakedUintIdx = iota
-	decNakedIntIdx
-	decNakedFloatIdx
-	decNakedBytesIdx
-	decNakedStringIdx
-	decNakedTimeIdx
-	decNakedBoolIdx
-)
-const (
-	_ = iota // maps to the scalars above
-	decNakedIntfIdx
-	decNakedMapIntfIntfIdx
-	decNakedMapStrIntfIdx
-	decNakedSliceIntfIdx
-)
-
 func (n *decNaked) init() {
 	if n.inited {
 		return
 	}
-	// n.ms = n.ma[:0]
-	// n.is = n.ia[:0]
-	// n.ns = n.na[:0]
-	// n.ss = n.sa[:0]
-
-	n.rr[decNakedUintIdx] = reflect.ValueOf(&n.u).Elem()
-	n.rr[decNakedIntIdx] = reflect.ValueOf(&n.i).Elem()
-	n.rr[decNakedFloatIdx] = reflect.ValueOf(&n.f).Elem()
-	n.rr[decNakedBytesIdx] = reflect.ValueOf(&n.l).Elem()
-	n.rr[decNakedStringIdx] = reflect.ValueOf(&n.s).Elem()
-	n.rr[decNakedTimeIdx] = reflect.ValueOf(&n.t).Elem()
-	n.rr[decNakedBoolIdx] = reflect.ValueOf(&n.b).Elem()
-
-	for i := range [arrayCacheLen]struct{}{} {
-		n.rr[decNakedIntfIdx*arrayCacheLen+i] = reflect.ValueOf(&(n.ia[i])).Elem()
-		n.rr[decNakedMapIntfIntfIdx*arrayCacheLen+i] = reflect.ValueOf(&(n.ma[i])).Elem()
-		n.rr[decNakedMapStrIntfIdx*arrayCacheLen+i] = reflect.ValueOf(&(n.na[i])).Elem()
-		n.rr[decNakedSliceIntfIdx*arrayCacheLen+i] = reflect.ValueOf(&(n.sa[i])).Elem()
-	}
+	n.ru = reflect.ValueOf(&n.u).Elem()
+	n.ri = reflect.ValueOf(&n.i).Elem()
+	n.rf = reflect.ValueOf(&n.f).Elem()
+	n.rl = reflect.ValueOf(&n.l).Elem()
+	n.rs = reflect.ValueOf(&n.s).Elem()
+	n.rt = reflect.ValueOf(&n.t).Elem()
+	n.rb = reflect.ValueOf(&n.b).Elem()
+
 	n.inited = true
 	// n.rr[] = reflect.ValueOf(&n.)
 }
 
+func (n *decNaked) initContainers() {
+	if n.decNakedContainers == nil {
+		n.decNakedContainers = new(decNakedContainers)
+		n.decNakedContainers.init()
+	}
+}
+
 func (n *decNaked) reset() {
 	if n == nil {
 		return
@@ -1732,7 +1724,7 @@ type rtid2rv struct {
 type decReaderSwitch struct {
 	rb bytesDecReader
 	// ---- cpu cache line boundary?
-	ri       ioDecReader
+	ri       *ioDecReader
 	mtr, str bool // whether maptype or slicetype are known types
 
 	be    bool // is binary encoding
@@ -1818,33 +1810,29 @@ type Decoder struct {
 	// NOTE: Decoder shouldn't call it's read methods,
 	// as the handler MAY need to do some coordination.
 	r  decReader
-	hh Handle
 	h  *BasicHandle
-
-	// ---- cpu cache line boundary?
-	decReaderSwitch
-	// ---- cpu cache line boundary?
-	bi bufioDecReader
-	// ---- cpu cache line boundary?
-	cf codecFner
-	// ---- cpu cache line boundary?
-	// cr containerStateRecv
-	is map[string]string // used for interning strings
+	bi *bufioDecReader
 	// cache the mapTypeId and sliceTypeId for faster comparisons
 	mtid uintptr
 	stid uintptr
-	// _  uintptr // for alignment purposes, so next one starts from a cache line
 
-	// ---- writable fields during execution --- *try* to keep in sep cache line
+	// ---- cpu cache line boundary?
+	decReaderSwitch
 
 	// ---- cpu cache line boundary?
-	b   [scratchByteArrayLen]byte
+	codecFnPooler
+	// cr containerStateRecv
 	n   *decNaked
 	nsp *sync.Pool
 	err error
+
 	// ---- cpu cache line boundary?
+	b  [scratchByteArrayLen]byte
+	is map[string]string                             // used for interning strings
+	_  [cacheLineSize - 8 - scratchByteArrayLen]byte // padding
 
-	// _ [64]byte // force alignment???
+	// padding - false sharing help // modify 232 if Decoder struct changes.
+	// _ [cacheLineSize - 232%cacheLineSize]byte
 }
 
 // NewDecoder returns a Decoder for decoding a stream of bytes from an io.Reader.
@@ -1868,7 +1856,8 @@ func NewDecoderBytes(in []byte, h Handle) *Decoder {
 var defaultDecNaked decNaked
 
 func newDecoder(h Handle) *Decoder {
-	d := &Decoder{hh: h, h: h.getBasicHandle(), err: errDecoderNotInitialized}
+	d := &Decoder{h: h.getBasicHandle(), err: errDecoderNotInitialized}
+	d.hh = h
 	d.be = h.isBinary()
 	// NOTE: do not initialize d.n here. It is lazily initialized in d.naked()
 	var jh *JsonHandle
@@ -1885,29 +1874,9 @@ func newDecoder(h Handle) *Decoder {
 	return d
 }
 
-// naked must be called before each call to .DecodeNaked,
-// as they will use it.
-func (d *Decoder) naked() *decNaked {
-	if d.n == nil {
-		// consider one of:
-		//   - get from sync.Pool  (if GC is frequent, there's no value here)
-		//   - new alloc           (safest. only init'ed if it a naked decode will be done)
-		//   - field in Decoder    (makes the Decoder struct very big)
-		// To support using a decoder where a DecodeNaked is not needed,
-		// we prefer #1 or #2.
-		// d.n = new(decNaked) // &d.nv // new(decNaked) // grab from a sync.Pool
-		// d.n.init()
-		var v interface{}
-		d.nsp, v = pool.decNaked()
-		d.n = v.(*decNaked)
-	}
-	return d.n
-}
-
 func (d *Decoder) resetCommon() {
 	d.n.reset()
 	d.d.reset()
-	d.cf.reset(d.hh)
 	d.err = nil
 	// reset all things which were cached from the Handle, but could change
 	d.mtid, d.stid = 0, 0
@@ -1928,16 +1897,22 @@ func (d *Decoder) Reset(r io.Reader) {
 	if r == nil {
 		return
 	}
+	if d.bi == nil {
+		d.bi = new(bufioDecReader)
+	}
 	d.bytes = false
 	if d.h.ReaderBufferSize > 0 {
 		d.bi.buf = make([]byte, 0, d.h.ReaderBufferSize)
 		d.bi.reset(r)
-		d.r = &d.bi
+		d.r = d.bi
 	} else {
-		d.ri.x = &d.b
+		// d.ri.x = &d.b
 		// d.s = d.sa[:0]
+		if d.ri == nil {
+			d.ri = new(ioDecReader)
+		}
 		d.ri.reset(r)
-		d.r = &d.ri
+		d.r = d.ri
 	}
 	d.resetCommon()
 }
@@ -1954,6 +1929,25 @@ func (d *Decoder) ResetBytes(in []byte) {
 	d.resetCommon()
 }
 
+// naked must be called before each call to .DecodeNaked,
+// as they will use it.
+func (d *Decoder) naked() *decNaked {
+	if d.n == nil {
+		// consider one of:
+		//   - get from sync.Pool  (if GC is frequent, there's no value here)
+		//   - new alloc           (safest. only init'ed if it a naked decode will be done)
+		//   - field in Decoder    (makes the Decoder struct very big)
+		// To support using a decoder where a DecodeNaked is not needed,
+		// we prefer #1 or #2.
+		// d.n = new(decNaked) // &d.nv // new(decNaked) // grab from a sync.Pool
+		// d.n.init()
+		var v interface{}
+		d.nsp, v = pool.decNaked()
+		d.n = v.(*decNaked)
+	}
+	return d.n
+}
+
 // Decode decodes the stream from reader and stores the result in the
 // value pointed to by v. v cannot be a nil pointer. v can also be
 // a reflect.Value of a pointer.
@@ -2007,7 +2001,9 @@ func (d *Decoder) ResetBytes(in []byte) {
 // Note: we allow nil values in the stream anywhere except for map keys.
 // A nil value in the encoded stream where a map key is expected is treated as an error.
 func (d *Decoder) Decode(v interface{}) (err error) {
+	// need to call defer directly, else it seems the recover is not fully handled
 	defer panicToErrs2(d, &d.err, &err)
+	defer d.alwaysAtEnd()
 	d.MustDecode(v)
 	return
 }
@@ -2024,13 +2020,7 @@ func (d *Decoder) MustDecode(v interface{}) {
 	} else {
 		d.decode(v)
 	}
-	if d.n != nil {
-		// if d.n != nil { }
-		// if nsp != nil, then n != nil (they are always set together)
-		d.nsp.Put(d.n)
-		d.n = nil
-		d.nsp = nil
-	}
+	d.alwaysAtEnd()
 	// xprintf(">>>>>>>> >>>>>>>> num decFns: %v\n", d.cf.sn)
 }
 
@@ -2040,6 +2030,15 @@ func (d *Decoder) MustDecode(v interface{}) {
 // 	d.decodeValueNoFn(reflect.ValueOf(&blank).Elem())
 // }
 
+func (d *Decoder) alwaysAtEnd() {
+	if d.n != nil {
+		// if n != nil, then nsp != nil (they are always set together)
+		d.nsp.Put(d.n)
+		d.n, d.nsp = nil, nil
+	}
+	d.codecFnPooler.alwaysAtEnd()
+}
+
 func (d *Decoder) swallow() {
 	// smarter decode that just swallows the content
 	dd := d.d
@@ -2083,6 +2082,7 @@ func (d *Decoder) swallow() {
 		n := d.naked()
 		dd.DecodeNaked()
 		if n.v == valueTypeExt && n.l == nil {
+			n.initContainers()
 			if n.li < arrayCacheLen {
 				n.ia[n.li] = nil
 				n.li++
@@ -2250,7 +2250,7 @@ func (d *Decoder) decodeValue(rv reflect.Value, fn *codecFn, chkAll bool) {
 
 	if fn == nil {
 		// always pass checkCodecSelfer=true, in case T or ****T is passed, where *T is a Selfer
-		fn = d.cf.get(rv.Type(), chkAll, true) // chkAll, chkAll)
+		fn = d.cfer().get(rv.Type(), chkAll, true) // chkAll, chkAll)
 	}
 	if fn.i.addrD {
 		if rvpValid {

+ 53 - 120
codec/encode.go

@@ -20,26 +20,6 @@ const defEncByteBufSize = 1 << 6 // 4:16, 6:64, 8:256, 10:1024
 
 var errEncoderNotInitialized = errors.New("Encoder not initialized")
 
-// // AsSymbolFlag defines what should be encoded as symbols.
-// type AsSymbolFlag uint8
-
-// const (
-// 	// AsSymbolDefault means only encode struct field names as symbols.
-// 	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
-
-// 	// AsSymbolMapStringKeysFlag means encode keys in map[string]XXX as symbols.
-// 	AsSymbolMapStringKeysFlag
-
-// 	// AsSymbolStructFieldNameFlag 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)
@@ -51,11 +31,6 @@ type encWriter interface {
 
 // encDriver abstracts the actual codec (binc vs msgpack, etc)
 type encDriver interface {
-	// IsBuiltinType(rt uintptr) bool
-
-	// Deprecated: left here for now so that old codecgen'ed filed will work.
-	// EncodeBuiltin(rt uintptr, v interface{})
-
 	EncodeNil()
 	EncodeInt(i int64)
 	EncodeUint(i uint64)
@@ -123,6 +98,11 @@ func (e *encDriverTrackContainerWriter) atEndOfEncode()             {}
 
 // EncodeOptions captures configuration options during encode.
 type EncodeOptions struct {
+	// WriterBufferSize is the size of the buffer used when writing.
+	//
+	// if > 0, we use a smart buffer internally for performance purposes.
+	WriterBufferSize int
+
 	// Encode a struct as an array, and not as a map
 	StructToArray bool
 
@@ -178,11 +158,6 @@ type EncodeOptions struct {
 	// //   AsSymbolMapStringKeys
 	// //   AsSymbolMapStringKeysFlag | AsSymbolStructFieldNameFlag
 	// AsSymbols AsSymbolFlag
-
-	// WriterBufferSize is the size of the buffer used when writing.
-	//
-	// if > 0, we use a smart buffer internally for performance purposes.
-	WriterBufferSize int
 }
 
 // ---------------------------------------------
@@ -202,8 +177,6 @@ type ioEncWriter struct {
 }
 
 func (z *ioEncWriter) WriteByte(b byte) (err error) {
-	// x.bs[0] = b
-	// _, err = x.ww.Write(x.bs[:])
 	z.b[0] = b
 	_, err = z.w.Write(z.b[:1])
 	return
@@ -214,18 +187,12 @@ func (z *ioEncWriter) WriteString(s string) (n int, err error) {
 }
 
 func (z *ioEncWriter) writeb(bs []byte) {
-	// if len(bs) == 0 {
-	// 	return
-	// }
 	if _, err := z.ww.Write(bs); err != nil {
 		panic(err)
 	}
 }
 
 func (z *ioEncWriter) writestr(s string) {
-	// if len(s) == 0 {
-	// 	return
-	// }
 	if _, err := z.sw.WriteString(s); err != nil {
 		panic(err)
 	}
@@ -290,10 +257,6 @@ func (z *bytesEncAppender) reset(in []byte, out *[]byte) {
 
 // ---------------------------------------------
 
-// func (e *Encoder) builtin(f *codecFnInfo, rv reflect.Value) {
-// 	e.e.EncodeBuiltin(f.ti.rtid, rv2i(rv))
-// }
-
 func (e *Encoder) rawExt(f *codecFnInfo, rv reflect.Value) {
 	e.e.EncodeRawExt(rv2i(rv).(*RawExt), e)
 }
@@ -432,7 +395,7 @@ func (e *Encoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 		// encoding type, because preEncodeValue may break it down to
 		// a concrete type and kInterface will bomb.
 		if rtelem.Kind() != reflect.Interface {
-			fn = e.cf.get(rtelem, true, true)
+			fn = e.cfer().get(rtelem, true, true)
 		}
 		for j := 0; j < l; j++ {
 			if elemsep {
@@ -478,8 +441,6 @@ func (e *Encoder) kStructNoOmitempty(f *codecFnInfo, rv reflect.Value) {
 	sfn := structFieldNode{v: rv, update: false}
 	if toMap {
 		ee.WriteMapStart(len(tisfi))
-		// asSymbols := e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
-		// asSymbols := e.h.AsSymbols == AsSymbolDefault || e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
 		if elemsep {
 			for _, si := range tisfi {
 				ee.WriteMapElemKey()
@@ -529,19 +490,6 @@ func encStructFieldKey(ee encDriver, keyType valueType, s string) {
 	} else {
 		ee.EncodeString(cUTF8, s)
 	}
-
-	// switch keyType {
-	// case valueTypeString:
-	// 	ee.EncodeString(cUTF8, s)
-	// case valueTypeInt:
-	// 	ee.EncodeInt(m.Int(strconv.ParseInt(s, 10, 64)))
-	// case valueTypeUint:
-	// 	ee.EncodeUint(m.Uint(strconv.ParseUint(s, 10, 64)))
-	// case valueTypeFloat:
-	// 	ee.EncodeFloat64(m.Float(strconv.ParseFloat(s, 64)))
-	// default: // string
-	// 	ee.EncodeString(cUTF8, s)
-	// }
 }
 
 func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
@@ -571,6 +519,7 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 	var spool *sync.Pool
 	var poolv interface{}
 	var fkvs []stringRv
+	// fmt.Printf(">>>>>>>>>>>>>> encode.kStruct: newlen: %d\n", newlen)
 	if newlen <= 8 {
 		spool, poolv = pool.stringRv8()
 		fkvs = poolv.(*[8]stringRv)[:newlen]
@@ -618,8 +567,6 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 
 	if toMap {
 		ee.WriteMapStart(newlen)
-		// asSymbols := e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
-		// asSymbols := e.h.AsSymbols == AsSymbolDefault || e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
 		if elemsep {
 			for j := 0; j < newlen; j++ {
 				kv = fkvs[j]
@@ -694,7 +641,7 @@ func (e *Encoder) kMap(f *codecFnInfo, rv reflect.Value) {
 		rtval = rtval.Elem()
 	}
 	if rtval.Kind() != reflect.Interface {
-		valFn = e.cf.get(rtval, true, true)
+		valFn = e.cfer().get(rtval, true, true)
 	}
 	mks := rv.MapKeys()
 
@@ -705,15 +652,13 @@ func (e *Encoder) kMap(f *codecFnInfo, rv reflect.Value) {
 	}
 
 	var keyTypeIsString = stringTypId == rt2id(rtkey0) // rtkeyid
-	if keyTypeIsString {
-		// asSymbols = e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
-	} else {
+	if !keyTypeIsString {
 		for rtkey.Kind() == reflect.Ptr {
 			rtkey = rtkey.Elem()
 		}
 		if rtkey.Kind() != reflect.Interface {
 			// rtkeyid = rt2id(rtkey)
-			keyFn = e.cf.get(rtkey, true, true)
+			keyFn = e.cfer().get(rtkey, true, true)
 		}
 	}
 
@@ -742,28 +687,6 @@ func (e *Encoder) kMapCanonical(rtkey reflect.Type, rv reflect.Value, mks []refl
 	// we previously did out-of-band if an extension was registered.
 	// This is not necessary, as the natural kind is sufficient for ordering.
 
-	// WHAT IS THIS? rtkeyid can never be a []uint8, per spec
-	// 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 elemsep {
-	// 			ee.WriteMapElemKey()
-	// 		}
-	// 		ee.EncodeStringBytes(cRAW, mksv[i].v)
-	// 		if elemsep {
-	// 			ee.WriteMapElemValue()
-	// 		}
-	// 		e.encodeValue(rv.MapIndex(mksv[i].r), valFn, true)
-	// 	}
-	// 	return
-	// }
-
 	switch rtkey.Kind() {
 	case reflect.Bool:
 		mksv := make([]boolRv, len(mks))
@@ -925,8 +848,7 @@ func (e *Encoder) kMapCanonical(rtkey reflect.Type, rv reflect.Value, mks []refl
 // // --------------------------------------------------
 
 type encWriterSwitch struct {
-	wi ioEncWriter
-	// ---- cpu cache line boundary?
+	wi *ioEncWriter
 	// wb bytesEncWriter
 	wb   bytesEncAppender
 	wx   bool // if bytes, wx=true
@@ -934,8 +856,8 @@ type encWriterSwitch struct {
 	isas bool // whether e.as != nil
 }
 
-// TODO: Uncomment after mid-stack inlining enabled in go 1.10
-//
+// // TODO: Uncomment after mid-stack inlining enabled in go 1.10
+
 // func (z *encWriterSwitch) writeb(s []byte) {
 // 	if z.wx {
 // 		z.wb.writeb(s)
@@ -974,29 +896,28 @@ type Encoder struct {
 	// as the handler MAY need to do some coordination.
 	w encWriter
 
-	// ho Handle // original handle
-	hh Handle
 	h  *BasicHandle
-
-	// ---- cpu cache line boundary?
-	// cr containerStateRecv
+	bw *bufio.Writer
 	as encDriverAsis
-	ci set
 
 	// ---- cpu cache line boundary?
-	encWriterSwitch
 
 	// ---- cpu cache line boundary?
-	bw bufio.Writer
+	encWriterSwitch
+	err error
 
 	// ---- cpu cache line boundary?
-	cf codecFner
+	codecFnPooler
+	ci set
+	js bool    // here, so that no need to piggy back on *codecFner for this
+	be bool    // here, so that no need to piggy back on *codecFner for this
+	_  [6]byte // padding
 
 	// ---- writable fields during execution --- *try* to keep in sep cache line
 
 	// ---- cpu cache line boundary?
-	b   [scratchByteArrayLen]byte
-	err error
+	b [scratchByteArrayLen]byte                 // used for encoding a chan or (non-addressable) array of bytes
+	_ [cacheLineSize - scratchByteArrayLen]byte // padding
 }
 
 // NewEncoder returns an Encoder for encoding into an io.Writer.
@@ -1021,17 +942,21 @@ func NewEncoderBytes(out *[]byte, h Handle) *Encoder {
 }
 
 func newEncoder(h Handle) *Encoder {
-	e := &Encoder{hh: h, h: h.getBasicHandle(), err: errEncoderNotInitialized}
+	e := &Encoder{h: h.getBasicHandle(), err: errEncoderNotInitialized}
+	e.hh = h
 	e.esep = h.hasElemSeparators()
-	e.e = h.newEncDriver(e)
-	e.as, e.isas = e.e.(encDriverAsis)
-	// e.cr, _ = e.e.(containerStateRecv)
 	return e
 }
 
-func (e *Encoder) postReset() {
+func (e *Encoder) resetCommon() {
+	if e.e == nil || e.hh.recreateEncDriver(e.e) {
+		e.e = e.hh.newEncDriver(e)
+		e.as, e.isas = e.e.(encDriverAsis)
+		// e.cr, _ = e.e.(containerStateRecv)
+	}
+	e.be = e.hh.isBinary()
+	_, e.js = e.hh.(*JsonHandle)
 	e.e.reset()
-	e.cf.reset(e.hh)
 	e.err = nil
 }
 
@@ -1043,28 +968,30 @@ func (e *Encoder) Reset(w io.Writer) {
 	if w == nil {
 		return
 	}
+	if e.wi == nil {
+		e.wi = new(ioEncWriter)
+	}
 	var ok bool
 	e.wx = false
 	e.wi.w = w
 	if e.h.WriterBufferSize > 0 {
-		bw := bufio.NewWriterSize(w, e.h.WriterBufferSize)
-		e.bw = *bw
-		e.wi.bw = &e.bw
-		e.wi.sw = &e.bw
-		e.wi.fw = &e.bw
-		e.wi.ww = &e.bw
+		e.bw = bufio.NewWriterSize(w, e.h.WriterBufferSize)
+		e.wi.bw = e.bw
+		e.wi.sw = e.bw
+		e.wi.fw = e.bw
+		e.wi.ww = e.bw
 	} else {
 		if e.wi.bw, ok = w.(io.ByteWriter); !ok {
-			e.wi.bw = &e.wi
+			e.wi.bw = e.wi
 		}
 		if e.wi.sw, ok = w.(ioEncStringWriter); !ok {
-			e.wi.sw = &e.wi
+			e.wi.sw = e.wi
 		}
 		e.wi.fw, _ = w.(ioFlusher)
 		e.wi.ww = w
 	}
-	e.w = &e.wi
-	e.postReset()
+	e.w = e.wi
+	e.resetCommon()
 }
 
 // ResetBytes resets the Encoder with a new destination output []byte.
@@ -1082,7 +1009,7 @@ func (e *Encoder) ResetBytes(out *[]byte) {
 	e.wx = true
 	e.wb.reset(in, out)
 	e.w = &e.wb
-	e.postReset()
+	e.resetCommon()
 }
 
 // Encode writes an object into a stream.
@@ -1167,6 +1094,7 @@ func (e *Encoder) ResetBytes(out *[]byte) {
 // only once in the stream, and use a tag to refer to it thereafter.
 func (e *Encoder) Encode(v interface{}) (err error) {
 	defer panicToErrs2(e, &e.err, &err)
+	defer e.alwaysAtEnd()
 	e.MustEncode(v)
 	return
 }
@@ -1180,8 +1108,13 @@ func (e *Encoder) MustEncode(v interface{}) {
 	e.encode(v)
 	e.e.atEndOfEncode()
 	e.w.atEndOfEncode()
+	e.alwaysAtEnd() // TODO: why does this cause test failures???
 }
 
+// func (e *Encoder) alwaysAtEnd() {
+// 	e.codecFnPooler.alwaysAtEnd()
+// }
+
 func (e *Encoder) encode(iv interface{}) {
 	if iv == nil || definitelyNil(iv) {
 		e.e.EncodeNil()
@@ -1329,7 +1262,7 @@ TOP:
 	if fn == nil {
 		rt := rv.Type()
 		// always pass checkCodecSelfer=true, in case T or ****T is passed, where *T is a Selfer
-		fn = e.cf.get(rt, checkFastpath, true)
+		fn = e.cfer().get(rt, checkFastpath, true)
 	}
 	if fn.i.addrE {
 		if rvpValid {

+ 1 - 1
codec/fast-path.not.go

@@ -35,7 +35,7 @@ type fastpathA [0]fastpathE
 func (x fastpathA) index(rtid uintptr) int { return -1 }
 
 func (_ fastpathT) DecSliceUint8V(v []uint8, canChange bool, d *Decoder) (_ []uint8, changed bool) {
-	fn := d.cf.get(uint8SliceTyp, true, true)
+	fn := d.cfer().get(uint8SliceTyp, true, true)
 	d.kSlice(&fn.i, reflect.ValueOf(&v).Elem())
 	return v, true
 }

+ 6 - 6
codec/gen-helper.generated.go

@@ -106,7 +106,12 @@ func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) EncBinary() bool {
-	return f.e.cf.be // f.e.hh.isBinaryEncoding()
+	return f.e.be // f.e.hh.isBinaryEncoding()
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) IsJSONHandle() bool {
+	return f.e.js
 }
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
@@ -149,11 +154,6 @@ func (f genHelperEncoder) TimeRtidIfBinc() (v uintptr) { return }
 // 	}
 // }
 
-// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperEncoder) IsJSONHandle() bool {
-	return f.e.cf.js
-}
-
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) I2Rtid(v interface{}) uintptr {
 	return i2rtid(v)

+ 5 - 5
codec/gen-helper.go.tmpl

@@ -105,7 +105,11 @@ func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) EncBinary() bool {
-	return f.e.cf.be // f.e.hh.isBinaryEncoding()
+	return f.e.be // f.e.hh.isBinaryEncoding()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) IsJSONHandle() bool {
+	return f.e.js
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) EncFallback(iv interface{}) {
@@ -141,10 +145,6 @@ func (f genHelperEncoder) TimeRtidIfBinc() (v uintptr) { return }
 // 	}
 // }
 
-// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperEncoder) IsJSONHandle() bool {
-	return f.e.cf.js
-}
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) I2Rtid(v interface{}) uintptr {
 	return i2rtid(v)

+ 19 - 92
codec/gen.go

@@ -270,19 +270,9 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, noExtensions bool,
 		x.linef("codecSelferValueType%s%s = %v", vt.String(), x.xs, int64(vt))
 	}
 
-	// x.linef("codecSelferValueTypeArray%s = %v", x.xs, int64(valueTypeArray))
-	// x.linef("codecSelferValueTypeMap%s = %v", x.xs, int64(valueTypeMap))
-	// // These are no longer needed, as there are functions created for them
-	// x.linef("// ----- containerStateValues ----")
-	// x.linef("codecSelferKcontainerMapKey%s = %v", x.xs, int64(containerMapKey))
-	// x.linef("codecSelferKcontainerMapValue%s = %v", x.xs, int64(containerMapValue))
-	// x.linef("codecSelferKcontainerMapEnd%s = %v", x.xs, int64(containerMapEnd))
-	// x.linef("codecSelferKcontainerArrayElem%s = %v", x.xs, int64(containerArrayElem))
-	// x.linef("codecSelferKcontainerArrayEnd%s = %v", x.xs, int64(containerArrayEnd))
 	x.linef("codecSelferBitsize%s = uint8(strconv.IntSize) // uint8(32 << (^uint(0) >> 63))", x.xs)
 	x.line(")")
 	x.line("var (")
-	// x.line("codecSelferBitsize" + x.xs + " = uint8(reflect.TypeOf(uint(0)).Bits())")
 	x.line("errCodecSelferOnlyMapOrArrayEncodeToStruct" + x.xs + " = errors.New(`only encoded map or array can be decoded into a struct`)")
 	x.line(")")
 	x.line("")
@@ -392,7 +382,6 @@ func (x *genRunner) genRefPkgs(t reflect.Type) {
 	if _, ok := x.is[t]; ok {
 		return
 	}
-	// fmt.Printf(">>>>>>: PkgPath: '%v', Name: '%s'\n", genImportPath(t), t.Name())
 	x.is[t] = struct{}{}
 	tpkg, tname := genImportPath(t), t.Name()
 	if tpkg != "" && tpkg != x.bp && tpkg != x.cp && tname != "" && tname[0] >= 'A' && tname[0] <= 'Z' {
@@ -714,8 +703,6 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
 	//   - type is time.Time, RawExt, Raw
 	//   - the type implements (Text|JSON|Binary)(Unm|M)arshal
 
-	// x.linef("%sm%s := z.EncBinary()", genTempVarPfx, mi)
-	// x.linef("_ = %sm%s", genTempVarPfx, mi)
 	x.line("if false {")           //start if block
 	defer func() { x.line("}") }() //end if block
 
@@ -731,30 +718,9 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
 		x.linef("} else { r.EncodeRawExt(%s, e)", varname)
 		return
 	}
-	// HACK: Support for Builtins.
-	//       Currently, only Binc supports builtins, and the only builtin type is time.Time.
-	//       Have a method that returns the rtid for time.Time if Handle is Binc.
-	// 2017-11-12: builtin no longer supported - comment out
-	// if t == timeTyp {
-	// 	vrtid := genTempVarPfx + "m" + x.varsfx()
-	// 	x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
-	// 	x.linef("r.EncodeBuiltin(%s, *%s)", vrtid, varname)
-	// }
 	// only check for extensions if the type is named, and has a packagePath.
 	var arrayOrStruct = tk == reflect.Array || tk == reflect.Struct // meaning varname if of type *T
 	if !x.nx && genImportPath(t) != "" && t.Name() != "" {
-		// first check if extensions are configued, before doing the interface conversion
-		// x.linef("} else if z.HasExtensions() && z.EncExt(%s) {", varname)
-		//
-		// yy := fmt.Sprintf("%sxt%s", genTempVarPfx, mi)
-		// // always pass a ptr, so if not array or struct, then take the address
-		// var zz string
-		// if !arrayOrStruct {
-		// 	// zz = "&" // taking address is more expensive. instead, let it optimize itself.
-		// }
-		// x.linef("} else if %s := z.Extension(z.I2Rtid(%s%s)); %s != nil { z.EncExtension(%s%s, %s) ",
-		// 	yy, zz, varname, yy, zz, varname, yy)
-		//
 		yy := fmt.Sprintf("%sxt%s", genTempVarPfx, mi)
 		x.linef("} else if %s := z.Extension(z.I2Rtid(%s)); %s != nil { z.EncExtension(%s, %s) ", yy, varname, yy, varname, yy)
 	}
@@ -958,7 +924,6 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 					// that is done subsequently (right after - below).
 					if uint8(ij+1) < si.nis && t2typ.Kind() == reflect.Ptr {
 						omitline.s(varname3).s(" != nil && ")
-						// omitline += varname3 + " != nil && "
 					}
 				}
 			}
@@ -996,7 +961,6 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 				if uint8(ij) == si.nis {
 					break
 				}
-				// fmt.Printf("%%%% %v, ix: %v\n", t2typ, ix)
 				for t2typ.Kind() == reflect.Ptr {
 					t2typ = t2typ.Elem()
 				}
@@ -1024,9 +988,8 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 		x.linef("if %s || %s {", ti2arrayvar, struct2arrvar) // if ti.toArray
 		if labelUsed {
 			x.linef("if %s { r.WriteArrayElem(); r.EncodeNil() } else { ", isNilVarName)
-			// x.linef("if %s { z.EncSendContainerState(codecSelferKcontainerArrayElem%s); r.EncodeNil() } else { ", isNilVarName, x.xs)
 		}
-		x.line("r.WriteArrayElem()") // x.linef("z.EncSendContainerState(codecSelferKcontainerArrayElem%s)", x.xs)
+		x.line("r.WriteArrayElem()")
 		if si.omitEmpty {
 			x.linef("if %s[%v] {", numfieldsvar, j)
 		}
@@ -1045,7 +1008,7 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 		if si.omitEmpty {
 			x.linef("if %s[%v] {", numfieldsvar, j)
 		}
-		x.line("r.WriteMapElemKey()") // x.linef("z.EncSendContainerState(codecSelferKcontainerMapKey%s)", x.xs)
+		x.line("r.WriteMapElemKey()")
 
 		// x.line("r.EncodeString(codecSelferCcUTF8" + x.xs + ", `" + si.encName + "`)")
 		// emulate EncStructFieldKey
@@ -1060,7 +1023,7 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 			x.linef("r.EncodeString(codecSelferCcUTF8%s, `%s`)", x.xs, si.encName)
 		}
 		// x.linef("r.EncStructFieldKey(codecSelferValueType%s%s, `%s`)", ti.keyType.String(), x.xs, si.encName)
-		x.line("r.WriteMapElemValue()") // x.linef("z.EncSendContainerState(codecSelferKcontainerMapValue%s)", x.xs)
+		x.line("r.WriteMapElemValue()")
 		if labelUsed {
 			x.line("if " + isNilVarName + " { r.EncodeNil() } else { ")
 			x.encVar(varname+"."+t2.Name, t2.Type)
@@ -1074,9 +1037,9 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 		x.linef("} ") // end if/else ti.toArray
 	}
 	x.linef("if %s || %s {", ti2arrayvar, struct2arrvar) // if ti.toArray {
-	x.line("r.WriteArrayEnd()")                          // x.linef("z.EncSendContainerState(codecSelferKcontainerArrayEnd%s)", x.xs)
+	x.line("r.WriteArrayEnd()")
 	x.line("} else {")
-	x.line("r.WriteMapEnd()") // x.linef("z.EncSendContainerState(codecSelferKcontainerMapEnd%s)", x.xs)
+	x.line("r.WriteMapEnd()")
 	x.line("}")
 
 }
@@ -1095,16 +1058,15 @@ func (x *genRunner) encListFallback(varname string, t reflect.Type) {
 	x.line("r.WriteArrayStart(len(" + varname + "))")
 	if t.Kind() == reflect.Chan {
 		x.linef("for %si%s, %si2%s := 0, len(%s); %si%s < %si2%s; %si%s++ {", g, i, g, i, varname, g, i, g, i, g, i)
-		x.line("r.WriteArrayElem()") // x.linef("z.EncSendContainerState(codecSelferKcontainerArrayElem%s)", x.xs)
+		x.line("r.WriteArrayElem()")
 		x.linef("%sv%s := <-%s", g, i, varname)
 	} else {
-		// x.linef("for %si%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
 		x.linef("for _, %sv%s := range %s {", genTempVarPfx, i, varname)
-		x.line("r.WriteArrayElem()") // x.linef("z.EncSendContainerState(codecSelferKcontainerArrayElem%s)", x.xs)
+		x.line("r.WriteArrayElem()")
 	}
 	x.encVar(genTempVarPfx+"v"+i, t.Elem())
 	x.line("}")
-	x.line("r.WriteArrayEnd()") // x.linef("z.EncSendContainerState(codecSelferKcontainerArrayEnd%s)", x.xs)
+	x.line("r.WriteArrayEnd()")
 }
 
 func (x *genRunner) encMapFallback(varname string, t reflect.Type) {
@@ -1112,13 +1074,12 @@ func (x *genRunner) encMapFallback(varname string, t reflect.Type) {
 	i := x.varsfx()
 	x.line("r.WriteMapStart(len(" + varname + "))")
 	x.linef("for %sk%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
-	// x.line("for " + genTempVarPfx + "k" + i + ", " + genTempVarPfx + "v" + i + " := range " + varname + " {")
-	x.line("r.WriteMapElemKey()") // f("z.EncSendContainerState(codecSelferKcontainerMapKey%s)", x.xs)
+	x.line("r.WriteMapElemKey()")
 	x.encVar(genTempVarPfx+"k"+i, t.Key())
-	x.line("r.WriteMapElemValue()") // f("z.EncSendContainerState(codecSelferKcontainerMapValue%s)", x.xs)
+	x.line("r.WriteMapElemValue()")
 	x.encVar(genTempVarPfx+"v"+i, t.Elem())
 	x.line("}")
-	x.line("r.WriteMapEnd()") // f("z.EncSendContainerState(codecSelferKcontainerMapEnd%s)", x.xs)
+	x.line("r.WriteMapEnd()")
 }
 
 func (x *genRunner) decVar(varname, decodedNilVarname string, t reflect.Type, canBeNil bool) {
@@ -1250,15 +1211,6 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 		return
 	}
 
-	// HACK: Support for Builtins.
-	//       Currently, only Binc supports builtins, and the only builtin type is time.Time.
-	//       Have a method that returns the rtid for time.Time if Handle is Binc.
-	// 2017-11-12: builtin no longer supported - comment out
-	// if t == timeTyp {
-	// 	vrtid := genTempVarPfx + "m" + x.varsfx()
-	// 	x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
-	// 	x.linef("r.DecodeBuiltin(%s, %s)", vrtid, varname)
-	// }
 	// only check for extensions if the type is named, and has a packagePath.
 	if !x.nx && genImportPath(t) != "" && t.Name() != "" {
 		// first check if extensions are configued, before doing the interface conversion
@@ -1282,54 +1234,39 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 	switch t.Kind() {
 	case reflect.Int:
 		x.line("*((*int)(" + varname + ")) = int(z.C.IntV(r.DecodeInt64(), codecSelferBitsize" + x.xs + "))")
-		// x.line("z.DecInt((*int)(" + varname + "))")
 	case reflect.Int8:
 		x.line("*((*int8)(" + varname + ")) = int8(z.C.IntV(r.DecodeInt64(), 8))")
-		// x.line("z.DecInt8((*int8)(" + varname + "))")
 	case reflect.Int16:
 		x.line("*((*int16)(" + varname + ")) = int16(z.C.IntV(r.DecodeInt64(), 16))")
-		// x.line("z.DecInt16((*int16)(" + varname + "))")
 	case reflect.Int32:
 		x.line("*((*int32)(" + varname + ")) = int32(z.C.IntV(r.DecodeInt64(), 32))")
-		// x.line("z.DecInt32((*int32)(" + varname + "))")
 	case reflect.Int64:
 		x.line("*((*int64)(" + varname + ")) = int64(r.DecodeInt64())")
-		// x.line("z.DecInt64((*int64)(" + varname + "))")
 
 	case reflect.Uint:
 		x.line("*((*uint)(" + varname + ")) = uint(z.C.UintV(r.DecodeUint64(), codecSelferBitsize" + x.xs + "))")
-		// x.line("z.DecUint((*uint)(" + varname + "))")
 	case reflect.Uint8:
 		x.line("*((*uint8)(" + varname + ")) = uint8(z.C.UintV(r.DecodeUint64(), 8))")
-		// x.line("z.DecUint8((*uint8)(" + varname + "))")
 	case reflect.Uint16:
 		x.line("*((*uint16)(" + varname + ")) = uint16(z.C.UintV(r.DecodeUint64(), 16))")
-		//x.line("z.DecUint16((*uint16)(" + varname + "))")
 	case reflect.Uint32:
 		x.line("*((*uint32)(" + varname + ")) = uint32(z.C.UintV(r.DecodeUint64(), 32))")
-		//x.line("z.DecUint32((*uint32)(" + varname + "))")
 	case reflect.Uint64:
 		x.line("*((*uint64)(" + varname + ")) = uint64(r.DecodeUint64())")
-		//x.line("z.DecUint64((*uint64)(" + varname + "))")
 	case reflect.Uintptr:
 		x.line("*((*uintptr)(" + varname + ")) = uintptr(z.C.UintV(r.DecodeUint64(), codecSelferBitsize" + x.xs + "))")
 
 	case reflect.Float32:
 		x.line("*((*float32)(" + varname + ")) = float32(r.DecodeFloat32As64())")
-		//x.line("z.DecFloat32((*float32)(" + varname + "))")
 	case reflect.Float64:
 		x.line("*((*float64)(" + varname + ")) = r.DecodeFloat64()")
-		// x.line("z.DecFloat64((*float64)(" + varname + "))")
 
 	case reflect.Bool:
 		x.line("*((*bool)(" + varname + ")) = r.DecodeBool()")
-		// x.line("z.DecBool((*bool)(" + varname + "))")
 	case reflect.String:
 		x.line("*((*string)(" + varname + ")) = r.DecodeString()")
-		// x.line("z.DecString((*string)(" + varname + "))")
 	case reflect.Array, reflect.Chan:
 		x.xtraSM(varname, false, t)
-		// x.decListFallback(varname, rtid, true, t)
 	case reflect.Slice:
 		// if a []uint8, call dedicated function
 		// if a known fastpath slice, call dedicated function
@@ -1577,12 +1514,6 @@ func (x *genRunner) decStructMap(varname, lenvarname string, rtid uintptr, t ref
 	i := x.varsfx()
 	kName := tpfx + "s" + i
 
-	// x.line("var " + kName + "Arr = [32]byte{} // default string to decode into")
-	// x.line("var " + kName + "Slc = " + kName + "Arr[:] // default slice to decode into")
-	// use the scratch buffer to avoid allocation (most field names are < 32).
-
-	// x.line("var " + kName + "Slc = z.DecScratchBuffer() // default slice to decode into")
-	// x.line("_ = " + kName + "Slc")
 	switch style {
 	case genStructMapStyleLenPrefix:
 		x.linef("for %sj%s := 0; %sj%s < %s; %sj%s++ {", tpfx, i, tpfx, i, lenvarname, tpfx, i)
@@ -1594,10 +1525,7 @@ func (x *genRunner) decStructMap(varname, lenvarname string, rtid uintptr, t ref
 		x.linef("if %shl%s { if %sj%s >= %s { break }", tpfx, i, tpfx, i, lenvarname)
 		x.line("} else { if r.CheckBreak() { break }; }")
 	}
-	x.line("r.ReadMapElemKey()") // f("z.DecSendContainerState(codecSelferKcontainerMapKey%s)", x.xs)
-	// x.line(kName + "Slc = r.DecodeStringAsBytes()")
-	// let string be scoped to this loop alone, so it doesn't escape.
-	// x.line(kName + " := string(" + kName + "Slc)")
+	x.line("r.ReadMapElemKey()")
 
 	// emulate decstructfieldkey
 	switch ti.keyType {
@@ -1612,11 +1540,11 @@ func (x *genRunner) decStructMap(varname, lenvarname string, rtid uintptr, t ref
 	}
 	// x.linef("%s := z.StringView(r.DecStructFieldKey(codecSelferValueType%s%s, z.DecScratchArrayBuffer()))", kName, ti.keyType.String(), x.xs)
 
-	x.line("r.ReadMapElemValue()") // f("z.DecSendContainerState(codecSelferKcontainerMapValue%s)", x.xs)
+	x.line("r.ReadMapElemValue()")
 	x.decStructMapSwitch(kName, varname, rtid, t)
 
 	x.line("} // end for " + tpfx + "j" + i)
-	x.line("r.ReadMapEnd()") // f("z.DecSendContainerState(codecSelferKcontainerMapEnd%s)", x.xs)
+	x.line("r.ReadMapEnd()")
 }
 
 func (x *genRunner) decStructArray(varname, lenvarname, breakString string, rtid uintptr, t reflect.Type) {
@@ -1654,8 +1582,7 @@ func (x *genRunner) decStructArray(varname, lenvarname, breakString string, rtid
 			tpfx, i, tpfx, i, tpfx, i,
 			tpfx, i, lenvarname, tpfx, i)
 		x.linef("if %sb%s { r.ReadArrayEnd(); %s }", tpfx, i, breakString)
-		// x.linef("if %sb%s { z.DecSendContainerState(codecSelferKcontainerArrayEnd%s); %s }", tpfx, i, x.xs, breakString)
-		x.line("r.ReadArrayElem()") // f("z.DecSendContainerState(codecSelferKcontainerArrayElem%s)", x.xs)
+		x.line("r.ReadArrayElem()")
 		x.decVar(varname+"."+t2.Name, "", t2.Type, true)
 	}
 	// read remaining values and throw away.
@@ -1664,10 +1591,10 @@ func (x *genRunner) decStructArray(varname, lenvarname, breakString string, rtid
 		tpfx, i, tpfx, i, tpfx, i,
 		tpfx, i, lenvarname, tpfx, i)
 	x.linef("if %sb%s { break }", tpfx, i)
-	x.line("r.ReadArrayElem()") // f("z.DecSendContainerState(codecSelferKcontainerArrayElem%s)", x.xs)
+	x.line("r.ReadArrayElem()")
 	x.linef(`z.DecStructFieldNotFound(%sj%s - 1, "")`, tpfx, i)
 	x.line("}")
-	x.line("r.ReadArrayEnd()") // f("z.DecSendContainerState(codecSelferKcontainerArrayEnd%s)", x.xs)
+	x.line("r.ReadArrayEnd()")
 }
 
 func (x *genRunner) decStruct(varname string, rtid uintptr, t reflect.Type) {
@@ -1677,7 +1604,7 @@ func (x *genRunner) decStruct(varname string, rtid uintptr, t reflect.Type) {
 	x.linef("if %sct%s == codecSelferValueTypeMap%s {", genTempVarPfx, i, x.xs)
 	x.line(genTempVarPfx + "l" + i + " := r.ReadMapStart()")
 	x.linef("if %sl%s == 0 {", genTempVarPfx, i)
-	x.line("r.ReadMapEnd()") // f("z.DecSendContainerState(codecSelferKcontainerMapEnd%s)", x.xs)
+	x.line("r.ReadMapEnd()")
 	if genUseOneFunctionForDecStructMap {
 		x.line("} else { ")
 		x.linef("x.codecDecodeSelfFromMap(%sl%s, d)", genTempVarPfx, i)
@@ -1693,7 +1620,7 @@ func (x *genRunner) decStruct(varname string, rtid uintptr, t reflect.Type) {
 	x.linef("} else if %sct%s == codecSelferValueTypeArray%s {", genTempVarPfx, i, x.xs)
 	x.line(genTempVarPfx + "l" + i + " := r.ReadArrayStart()")
 	x.linef("if %sl%s == 0 {", genTempVarPfx, i)
-	x.line("r.ReadArrayEnd()") // f("z.DecSendContainerState(codecSelferKcontainerArrayEnd%s)", x.xs)
+	x.line("r.ReadArrayEnd()")
 	x.line("} else { ")
 	x.linef("x.codecDecodeSelfFromArray(%sl%s, d)", genTempVarPfx, i)
 	x.line("}")

+ 240 - 226
codec/helper.go

@@ -104,7 +104,6 @@ import (
 	"fmt"
 	"io"
 	"math"
-	"os"
 	"reflect"
 	"sort"
 	"strconv"
@@ -131,6 +130,13 @@ const (
 
 	// always set xDebug = false before releasing software
 	xDebug = true
+
+	// size of the cacheline: defaulting to value for archs: amd64, arm64, 386
+	// should use "runtime/internal/sys".CacheLineSize, but that is not exposed.
+	cacheLineSize = 64
+
+	wordSizeBits = strconv.IntSize
+	wordSize     = strconv.IntSize / 8
 )
 
 var (
@@ -151,14 +157,6 @@ func init() {
 	refBitset.set(byte(reflect.Chan))
 }
 
-// type findCodecFnMode uint8
-
-// const (
-// 	findCodecFnModeMap findCodecFnMode = iota
-// 	findCodecFnModeBinarySearch
-// 	findCodecFnModeLinearSearch
-// )
-
 type charEncoding uint8
 
 const (
@@ -268,14 +266,12 @@ type typeInfoLoadArray struct {
 	etypes   [typeInfoLoadArrayLen]uintptr
 	sfis     [typeInfoLoadArrayLen]*structFieldInfo
 	sfiidx   [typeInfoLoadArrayLen]sfiIdx
+	_        [32]bool // padding
 }
 
-// type containerStateRecv interface {
-// 	sendContainerState(containerState)
-// }
-
 // mirror json.Marshaler and json.Unmarshaler here,
 // so we don't import the encoding/json package
+
 type jsonMarshaler interface {
 	MarshalJSON() ([]byte, error)
 }
@@ -298,12 +294,17 @@ var (
 	intfSliceTyp   = reflect.TypeOf([]interface{}(nil))
 	intfTyp        = intfSliceTyp.Elem()
 
+	reflectValTyp = reflect.TypeOf((*reflect.Value)(nil)).Elem()
+
 	stringTyp     = reflect.TypeOf("")
 	timeTyp       = reflect.TypeOf(time.Time{})
 	rawExtTyp     = reflect.TypeOf(RawExt{})
 	rawTyp        = reflect.TypeOf(Raw{})
+	uintptrTyp    = reflect.TypeOf(uintptr(0))
 	uint8Typ      = reflect.TypeOf(uint8(0))
 	uint8SliceTyp = reflect.TypeOf([]uint8(nil))
+	uintTyp       = reflect.TypeOf(uint(0))
+	intTyp        = reflect.TypeOf(int(0))
 
 	mapBySliceTyp = reflect.TypeOf((*MapBySlice)(nil)).Elem()
 
@@ -332,8 +333,8 @@ var (
 	intfSliceTypId   = rt2id(intfSliceTyp)
 	// mapBySliceTypId  = rt2id(mapBySliceTyp)
 
-	intBitsize  uint8 = uint8(reflect.TypeOf(int(0)).Bits())
-	uintBitsize uint8 = uint8(reflect.TypeOf(uint(0)).Bits())
+	intBitsize  = uint8(intTyp.Bits())
+	uintBitsize = uint8(uintTyp.Bits())
 
 	bsAll0x00 = []byte{0, 0, 0, 0, 0, 0, 0, 0}
 	bsAll0xff = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
@@ -416,21 +417,15 @@ type BasicHandle struct {
 	TypeInfos *TypeInfos
 
 	extHandle
+	// xh *extHandle // consider making this a pointer, if needed for all handles to be comparable
+	// xha [2]extTypeTagFn // init of xh. DONT DO THIS - makes this struct bigger by len*7 words
+
 	EncodeOptions
 	DecodeOptions
 	RPCOptions
 	noBuiltInTypeChecker
 }
 
-// func (x *BasicHandle) postCopy() {
-// 	if len(x.extHandle) == 0 {
-// 		return
-// 	}
-// 	v := make(extHandle, len(x.extHandle))
-// 	copy(v, x.extHandle)
-// 	x.extHandle = v
-// }
-
 func (x *BasicHandle) getBasicHandle() *BasicHandle {
 	return x
 }
@@ -450,6 +445,7 @@ func (x *BasicHandle) getTypeInfo(rtid uintptr, rt reflect.Type) (pti *typeInfo)
 type Handle interface {
 	Name() string
 	getBasicHandle() *BasicHandle
+	recreateEncDriver(encDriver) bool
 	newEncDriver(w *Encoder) encDriver
 	newDecDriver(r *Decoder) decDriver
 	isBinary() bool
@@ -457,33 +453,6 @@ type Handle interface {
 	IsBuiltinType(rtid uintptr) bool
 }
 
-// func copyHandle(h Handle) (h2 Handle) {
-// 	_ = h.getBasicHandle().postCopy // ensure postCopy function on basicHandle isn't commented
-// 	switch hh := h.(type) {
-// 	case *JsonHandle:
-// 		hh2 := *hh
-// 		hh2.postCopy()
-// 		h2 = &hh2
-// 	case *SimpleHandle:
-// 		hh2 := *hh
-// 		hh2.postCopy()
-// 		h2 = &hh2
-// 	case *CborHandle:
-// 		hh2 := *hh
-// 		hh2.postCopy()
-// 		h2 = &hh2
-// 	case *MsgpackHandle:
-// 		hh2 := *hh
-// 		hh2.postCopy()
-// 		h2 = &hh2
-// 	case *BincHandle:
-// 		hh2 := *hh
-// 		hh2.postCopy()
-// 		h2 = &hh2
-// 	}
-// 	return
-// }
-
 // Raw represents raw formatted bytes.
 // We "blindly" store it during encode and retrieve the raw bytes during decode.
 // Note: it is dangerous during encode, so we may gate the behaviour behind an Encode flag which must be explicitly set.
@@ -563,34 +532,29 @@ func (x addExtWrapper) UpdateExt(dest interface{}, v interface{}) {
 	x.ReadExt(dest, v.([]byte))
 }
 
-type setExtWrapper struct {
-	b BytesExt
-	i InterfaceExt
+type extWrapper struct {
+	BytesExt
+	InterfaceExt
 }
 
-func (x *setExtWrapper) check(v bool, s string) {
-	if v {
-		panicv.errorf("%s is not supported", s)
-	}
+type bytesExtFailer struct{}
+
+func (bytesExtFailer) WriteExt(v interface{}) []byte {
+	panicv.errorstr("BytesExt.WriteExt is not supported")
+	return nil
 }
-func (x *setExtWrapper) WriteExt(v interface{}) []byte {
-	x.check(x.b == nil, "BytesExt.WriteExt")
-	return x.b.WriteExt(v)
+func (bytesExtFailer) ReadExt(v interface{}, bs []byte) {
+	panicv.errorstr("BytesExt.ReadExt is not supported")
 }
 
-func (x *setExtWrapper) ReadExt(v interface{}, bs []byte) {
-	x.check(x.b == nil, "BytesExt.ReadExt")
-	x.b.ReadExt(v, bs)
-}
+type interfaceExtFailer struct{}
 
-func (x *setExtWrapper) ConvertExt(v interface{}) interface{} {
-	x.check(x.i == nil, "InterfaceExt.ConvertExt")
-	return x.i.ConvertExt(v)
+func (interfaceExtFailer) ConvertExt(v interface{}) interface{} {
+	panicv.errorstr("InterfaceExt.ConvertExt is not supported")
+	return nil
 }
-
-func (x *setExtWrapper) UpdateExt(dest interface{}, v interface{}) {
-	x.check(x.i == nil, "InterfaceExt.UpdateExt")
-	x.i.UpdateExt(dest, v)
+func (interfaceExtFailer) UpdateExt(dest interface{}, v interface{}) {
+	panicv.errorstr("InterfaceExt.UpdateExt is not supported")
 }
 
 type binaryEncodingType struct{}
@@ -619,7 +583,8 @@ func (noBuiltInTypes) DecodeBuiltin(rt uintptr, v interface{}) {}
 
 type noElemSeparators struct{}
 
-func (noElemSeparators) hasElemSeparators() (v bool) { return }
+func (noElemSeparators) hasElemSeparators() (v bool)            { return }
+func (noElemSeparators) recreateEncDriver(e encDriver) (v bool) { return }
 
 // bigenHelper.
 // Users must already slice the x completely, because we will not reslice.
@@ -654,22 +619,21 @@ type extTypeTagFn struct {
 type extHandle []extTypeTagFn
 
 // AddExt registes an encode and decode function for a reflect.Type.
-// AddExt internally calls SetExt.
 // To deregister an Ext, call AddExt with nil encfn and/or nil decfn.
 //
 // Deprecated: Use SetBytesExt or SetInterfaceExt on the Handle instead.
 func (o *extHandle) AddExt(
 	rt reflect.Type, tag byte,
-	encfn func(reflect.Value) ([]byte, error), decfn func(reflect.Value, []byte) error,
-) (err error) {
+	encfn func(reflect.Value) ([]byte, error), decfn func(reflect.Value, []byte) error) (err error) {
 	if encfn == nil || decfn == nil {
 		return o.SetExt(rt, uint64(tag), nil)
 	}
 	return o.SetExt(rt, uint64(tag), addExtWrapper{encfn, decfn})
 }
 
-// Note that the type must be a named type, and specifically not
-// a pointer or Interface. An error is returned if that is not honored.
+// SetExt will set the extension for a tag and reflect.Type.
+// Note that the type must be a named type, and specifically not a pointer or Interface.
+// An error is returned if that is not honored.
 // To Deregister an ext, call SetExt with nil Ext.
 //
 // Deprecated: Use SetBytesExt or SetInterfaceExt on the Handle instead.
@@ -691,17 +655,18 @@ func (o *extHandle) SetExt(rt reflect.Type, tag uint64, ext Ext) (err error) {
 		// all natively supported type, so cannot have an extension
 		return // TODO: should we silently ignore, or return an error???
 	}
+	if o == nil {
+		return errors.New("codec.Handle.SetExt: extHandle not initialized")
+	}
 	o2 := *o
-	if o2 == nil {
-		o2 = make([]extTypeTagFn, 0, 4)
-		*o = o2
-	} else {
-		for i := range o2 {
-			v := &o2[i]
-			if v.rtid == rtid {
-				v.tag, v.ext = tag, ext
-				return
-			}
+	// if o2 == nil {
+	// 	return errors.New("codec.Handle.SetExt: extHandle not initialized")
+	// }
+	for i := range o2 {
+		v := &o2[i]
+		if v.rtid == rtid {
+			v.tag, v.ext = tag, ext
+			return
 		}
 	}
 	rtidptr := rt2id(reflect.PtrTo(rt))
@@ -731,7 +696,7 @@ func (o extHandle) getExtForTag(tag uint64) *extTypeTagFn {
 	return nil
 }
 
-const maxLevelsEmbedding = 16
+const maxLevelsEmbedding = 15 // use this, so structFieldInfo fits into 8 bytes
 
 type structFieldInfo struct {
 	encName   string // encode name
@@ -740,7 +705,6 @@ type structFieldInfo struct {
 	is        [maxLevelsEmbedding]uint16 // (recursive/embedded) field index in struct
 	nis       uint8                      // num levels of embedding. if 1, then it's not embedded.
 	omitEmpty bool
-	// toArray   bool // if field is _struct, is the toArray set?
 }
 
 func (si *structFieldInfo) setToZeroValue(v reflect.Value) {
@@ -823,7 +787,6 @@ func parseStructFieldInfo(fname string, stag string) (si *structFieldInfo) {
 			}
 		}
 	}
-	// si.encNameBs = []byte(si.encName)
 	return
 }
 
@@ -850,7 +813,6 @@ type structFieldNodeCache struct {
 }
 
 func (x *structFieldNodeCache) get(key uint32) (fv reflect.Value, valid bool) {
-	// defer func() { fmt.Printf(">>>> found in cache2? %v\n", valid) }()
 	for i, k := range &x.idx {
 		if uint8(i) == x.num {
 			return // break
@@ -944,36 +906,40 @@ type typeInfo struct {
 	sfi  []*structFieldInfo // sorted. Used when enc/dec struct to map.
 	sfip []*structFieldInfo // unsorted. Used when enc/dec struct to array.
 
-	rt   reflect.Type
+	rt reflect.Type
+
+	// ---- cpu cache line boundary?
 	rtid uintptr
 	// rv0  reflect.Value // saved zero value, used if immutableKind
 
 	numMeth uint16 // number of methods
 
-	comparable   bool // true if a struct, and is comparable
-	anyOmitEmpty bool // true if a struct, and any of the fields are tagged "omitempty"
+	comparable   bool      // true if a struct, and is comparable
+	anyOmitEmpty bool      // true if a struct, and any of the fields are tagged "omitempty"
+	toArray      bool      // whether this (struct) type should be encoded as an array
+	keyType      valueType // if struct, how is the field name stored in a stream? default is string
 
 	mbs bool // base type (T or *T) is a MapBySlice
 
+	// ---- cpu cache line boundary?
 	// format of marshal type fields below: [btj][mu]p? OR csp?
 
-	bm  bool // T is a binaryMarshaler
-	bmp bool // *T is a binaryMarshaler
-	bu  bool // T is a binaryUnmarshaler
-	bup bool // *T is a binaryUnmarshaler
-	tm  bool // T is a textMarshaler
-	tmp bool // *T is a textMarshaler
-	tu  bool // T is a textUnmarshaler
-	tup bool // *T is a textUnmarshaler
-	jm  bool // T is a jsonMarshaler
-	jmp bool // *T is a jsonMarshaler
-	ju  bool // T is a jsonUnmarshaler
-	jup bool // *T is a jsonUnmarshaler
-	cs  bool // T is a Selfer
-	csp bool // *T is a Selfer
-
-	toArray bool      // whether this (struct) type should be encoded as an array
-	keyType valueType // if struct, how is the field name stored in a stream? default is string
+	bm  bool    // T is a binaryMarshaler
+	bmp bool    // *T is a binaryMarshaler
+	bu  bool    // T is a binaryUnmarshaler
+	bup bool    // *T is a binaryUnmarshaler
+	tm  bool    // T is a textMarshaler
+	tmp bool    // *T is a textMarshaler
+	tu  bool    // T is a textUnmarshaler
+	tup bool    // *T is a textUnmarshaler
+	jm  bool    // T is a jsonMarshaler
+	jmp bool    // *T is a jsonMarshaler
+	ju  bool    // T is a jsonUnmarshaler
+	jup bool    // *T is a jsonUnmarshaler
+	cs  bool    // T is a Selfer
+	csp bool    // *T is a Selfer
+	_   [3]byte // padding
+
 }
 
 // define length beyond which we do a binary search instead of a linear search.
@@ -1133,29 +1099,12 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 			x.infos.store(&vs)
 		}
 	}
-	// if sp == nil {
-	// 	pti = &ti
-	// 	vs := []rtid2ti{{rtid, pti}}
-	// 	x.infos.store(&vs)
-	// } else {
-	// 	idx, pti = x.find(sp, rtid)
-	// 	if pti == nil {
-	// 		s := *sp
-	// 		pti = &ti
-	// 		vs := make([]rtid2ti, len(s)+1)
-	// 		copy(vs, s[:idx])
-	// 		vs[idx] = rtid2ti{rtid, pti}
-	// 		copy(vs[idx+1:], s[idx:])
-	// 		x.infos.store(&vs)
-	// 	}
-	// }
 	x.mu.Unlock()
 	return
 }
 
 func (x *TypeInfos) rget(rt reflect.Type, rtid uintptr, omitEmpty bool,
-	indexstack []uint16, pv *typeInfoLoad,
-) {
+	indexstack []uint16, pv *typeInfoLoad) {
 	// Read up fields and store how to access the value.
 	//
 	// It uses go's rules for message selectors,
@@ -1332,7 +1281,7 @@ func implIntf(rt, iTyp reflect.Type) (base bool, indir bool) {
 	return rt.Implements(iTyp), reflect.PtrTo(rt).Implements(iTyp)
 }
 
-// func round(x float64) float64 {
+// func roundFloat(x float64) float64 {
 // 	t := math.Trunc(x)
 // 	if math.Abs(x-t) >= 0.5 {
 // 		return t + math.Copysign(1, x)
@@ -1340,19 +1289,11 @@ func implIntf(rt, iTyp reflect.Type) (base bool, indir bool) {
 // 	return t
 // }
 
-func xprintf(format string, a ...interface{}) {
-	if xDebug {
-		fmt.Fprintf(os.Stderr, format, a...)
-	}
-}
-
 func panicToErr(h errstrDecorator, err *error) {
 	if recoverPanicToErr {
 		if x := recover(); x != nil {
-			// if false && xDebug {
-			// 	fmt.Printf("panic'ing with: %v\n", x)
-			// 	debug.PrintStack()
-			// }
+			// fmt.Printf("panic'ing with: %v\n", x)
+			// debug.PrintStack()
 			panicValToErr(h, x, err)
 		}
 	}
@@ -1392,13 +1333,6 @@ func panicValToErr(h errstrDecorator, v interface{}, err *error) {
 	}
 }
 
-// func doPanic(tag string, format string, params ...interface{}) {
-// 	params2 := make([]interface{}, len(params)+1)
-// 	params2[0] = tag
-// 	copy(params2[1:], params)
-// 	panicv.errorf("%s: "+format, params2...)
-// }
-
 func isImmutableKind(k reflect.Kind) (v bool) {
 	return immutableKindsSet[k]
 	// return false ||
@@ -1419,14 +1353,29 @@ func isImmutableKind(k reflect.Kind) (v bool) {
 	// 	k == reflect.String
 }
 
-// ----
+// func timeLocUTCName(tzint int16) string {
+// 	if tzint == 0 {
+// 		return "UTC"
+// 	}
+// 	var tzname = []byte("UTC+00:00")
+// 	//tzname := fmt.Sprintf("UTC%s%02d:%02d", tzsign, tz/60, tz%60) //perf issue using Sprintf. inline below.
+// 	//tzhr, tzmin := tz/60, tz%60 //faster if u convert to int first
+// 	var tzhr, tzmin int16
+// 	if tzint < 0 {
+// 		tzname[3] = '-' // (TODO: verify. this works here)
+// 		tzhr, tzmin = -tzint/60, (-tzint)%60
+// 	} else {
+// 		tzhr, tzmin = tzint/60, tzint%60
+// 	}
+// 	tzname[4] = timeDigits[tzhr/10]
+// 	tzname[5] = timeDigits[tzhr%10]
+// 	tzname[7] = timeDigits[tzmin/10]
+// 	tzname[8] = timeDigits[tzmin%10]
+// 	return string(tzname)
+// 	//return time.FixedZone(string(tzname), int(tzint)*60)
+// }
 
-// type codecFnInfoAddrKind uint8
-// const (
-// 	codecFnInfoAddrAddr codecFnInfoAddrKind = iota // default
-// 	codecFnInfoAddrBase
-// 	codecFnInfoAddrAddrElseBase
-// )
+// ----
 
 type codecFnInfo struct {
 	ti    *typeInfo
@@ -1436,6 +1385,7 @@ type codecFnInfo struct {
 	addrD bool
 	addrF bool // if addrD, this says whether decode function can take a value or a ptr
 	addrE bool
+	ready bool // ready to use
 }
 
 // codecFn encapsulates the captured variables and the encode function.
@@ -1450,63 +1400,67 @@ type codecFn struct {
 
 type codecRtidFn struct {
 	rtid uintptr
-	fn   codecFn
+	fn   *codecFn
 }
 
 type codecFner struct {
-	hh Handle
+	// hh Handle
 	h  *BasicHandle
-	cs [arrayCacheLen]*[arrayCacheLen]codecRtidFn
-	s  []*[arrayCacheLen]codecRtidFn
-	sn uint32
+	s  []codecRtidFn
 	be bool
 	js bool
-	cf [arrayCacheLen]codecRtidFn
 }
 
 func (c *codecFner) reset(hh Handle) {
-	c.hh = hh
-	c.h = hh.getBasicHandle()
-	_, c.js = hh.(*JsonHandle)
-	c.be = hh.isBinary()
+	bh := hh.getBasicHandle()
+	// only reset iff extensions changed or *TypeInfos changed
+	var hhSame = true &&
+		c.h == bh && c.h.TypeInfos == bh.TypeInfos &&
+		len(c.h.extHandle) == len(bh.extHandle) &&
+		(len(c.h.extHandle) == 0 || &c.h.extHandle[0] == &bh.extHandle[0])
+	if !hhSame {
+		// c.hh = hh
+		c.h, bh = bh, c.h // swap both
+		_, c.js = hh.(*JsonHandle)
+		c.be = hh.isBinary()
+		for i := range c.s {
+			c.s[i].fn.i.ready = false
+		}
+	}
 }
 
 func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (fn *codecFn) {
 	rtid := rt2id(rt)
-	var j uint32
-	var sn uint32 = c.sn
-	if sn == 0 {
-		c.s = c.cs[:1]
-		c.s[0] = &c.cf
-		c.cf[0].rtid = rtid
-		fn = &(c.cf[0].fn)
-		c.sn = 1
-	} else {
-	LOOP1:
-		for _, x := range c.s {
-			for i := range x {
-				if j == sn {
-					break LOOP1
-				}
-				if x[i].rtid == rtid {
-					fn = &(x[i].fn)
-					return
-				}
-				j++
+
+	for _, x := range c.s {
+		if x.rtid == rtid {
+			// if rtid exists, then there's a *codenFn attached (non-nil)
+			fn = x.fn
+			if fn.i.ready {
+				return
 			}
+			break
 		}
-		sx, sy := sn/arrayCacheLen, sn%arrayCacheLen
-		if sy == 0 {
-			c.s = append(c.s, &[arrayCacheLen]codecRtidFn{})
+	}
+	var ti *typeInfo
+	if fn == nil {
+		fn = new(codecFn)
+		if c.s == nil {
+			c.s = make([]codecRtidFn, 0, 8)
 		}
-		c.s[sx][sy].rtid = rtid
-		fn = &(c.s[sx][sy].fn)
-		c.sn++
+		c.s = append(c.s, codecRtidFn{rtid, fn})
+	} else {
+		ti = fn.i.ti
+		*fn = codecFn{}
+		fn.i.ti = ti
+		// fn.fe, fn.fd = nil, nil
 	}
-
-	ti := c.h.getTypeInfo(rtid, rt)
 	fi := &(fn.i)
-	fi.ti = ti
+	fi.ready = true
+	if ti == nil {
+		ti = c.h.getTypeInfo(rtid, rt)
+		fi.ti = ti
+	}
 
 	rk := rt.Kind()
 
@@ -1528,12 +1482,6 @@ func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (
 		fi.addrF = true
 		fi.addrD = true
 		fi.addrE = true
-	} else if false && c.hh.IsBuiltinType(rtid) {
-		// TODO: remove this whole block. currently turned off with the "false &&"
-		// fn.fe = (*Encoder).builtin
-		// fn.fd = (*Decoder).builtin
-		// fi.addrF = true
-		// fi.addrD = true
 	} else if xfFn := c.h.getExt(rtid); xfFn != nil {
 		fi.xfTag, fi.xfFn = xfFn.tag, xfFn.ext
 		fn.fe = (*Encoder).ext
@@ -1587,10 +1535,14 @@ func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (
 						xfnf(e, xf, xrv.Convert(xrt))
 					}
 					fi.addrD = true
-					fi.addrF = false
+					fi.addrF = false // meaning it can be an address(ptr) or a value
 					xfnf2 := fastpathAV[idx].decfn
 					fn.fd = func(d *Decoder, xf *codecFnInfo, xrv reflect.Value) {
-						xfnf2(d, xf, xrv.Convert(reflect.PtrTo(xrt)))
+						if xrv.Kind() == reflect.Ptr {
+							xfnf2(d, xf, xrv.Convert(reflect.PtrTo(xrt)))
+						} else {
+							xfnf2(d, xf, xrv.Convert(xrt))
+						}
 					}
 				}
 			}
@@ -1633,8 +1585,6 @@ func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (
 			case reflect.Uint64:
 				fn.fe = (*Encoder).kUint64
 				fn.fd = (*Decoder).kUint64
-				// case reflect.Ptr:
-				// 	fn.fd = (*Decoder).kPtr
 			case reflect.Uintptr:
 				fn.fe = (*Encoder).kUintptr
 				fn.fd = (*Decoder).kUintptr
@@ -1662,9 +1612,7 @@ func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (
 				fi.addrD = false
 				rt2 := reflect.SliceOf(rt.Elem())
 				fn.fd = func(d *Decoder, xf *codecFnInfo, xrv reflect.Value) {
-					// println(">>>>>> decoding an array ... ")
-					d.cf.get(rt2, true, false).fd(d, xf, xrv.Slice(0, xrv.Len()))
-					// println(">>>>>> decoding an array ... DONE")
+					d.cfer().get(rt2, true, false).fd(d, xf, xrv.Slice(0, xrv.Len()))
 				}
 				// fn.fd = (*Decoder).kArray
 			case reflect.Struct:
@@ -1674,11 +1622,6 @@ func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (
 					fn.fe = (*Encoder).kStructNoOmitempty
 				}
 				fn.fd = (*Decoder).kStruct
-				// reflect.Ptr and reflect.Interface are handled already by preEncodeValue
-				// case reflect.Ptr:
-				// 	fn.fe = (*Encoder).kPtr
-				// case reflect.Interface:
-				// 	fn.fe = (*Encoder).kInterface
 			case reflect.Map:
 				fn.fe = (*Encoder).kMap
 				fn.fd = (*Decoder).kMap
@@ -1687,15 +1630,38 @@ func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (
 				fn.fd = (*Decoder).kInterface
 				fn.fe = (*Encoder).kErr
 			default:
+				// reflect.Ptr and reflect.Interface are handled already by preEncodeValue
 				fn.fe = (*Encoder).kErr
 				fn.fd = (*Decoder).kErr
 			}
 		}
 	}
-
 	return
 }
 
+type codecFnPooler struct {
+	cf  *codecFner
+	cfp *sync.Pool
+	hh  Handle
+}
+
+func (d *codecFnPooler) cfer() *codecFner {
+	if d.cf == nil {
+		var v interface{}
+		d.cfp, v = pool.codecFner()
+		d.cf = v.(*codecFner)
+		d.cf.reset(d.hh)
+	}
+	return d.cf
+}
+
+func (d *codecFnPooler) alwaysAtEnd() {
+	if d.cf != nil {
+		d.cfp.Put(d.cf)
+		d.cf, d.cfp = nil, nil
+	}
+}
+
 // ----
 
 // these "checkOverflow" functions must be inlinable, and not call anybody.
@@ -1800,11 +1766,13 @@ type ioBuffered interface {
 
 type intSlice []int64
 type uintSlice []uint64
-type uintptrSlice []uintptr
+
+// type uintptrSlice []uintptr
 type floatSlice []float64
 type boolSlice []bool
 type stringSlice []string
-type bytesSlice [][]byte
+
+// type bytesSlice [][]byte
 
 func (p intSlice) Len() int           { return len(p) }
 func (p intSlice) Less(i, j int) bool { return p[i] < p[j] }
@@ -1814,9 +1782,9 @@ func (p uintSlice) Len() int           { return len(p) }
 func (p uintSlice) Less(i, j int) bool { return p[i] < p[j] }
 func (p uintSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
-func (p uintptrSlice) Len() int           { return len(p) }
-func (p uintptrSlice) Less(i, j int) bool { return p[i] < p[j] }
-func (p uintptrSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+// func (p uintptrSlice) Len() int           { return len(p) }
+// func (p uintptrSlice) Less(i, j int) bool { return p[i] < p[j] }
+// func (p uintptrSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
 func (p floatSlice) Len() int { return len(p) }
 func (p floatSlice) Less(i, j int) bool {
@@ -1828,9 +1796,9 @@ func (p stringSlice) Len() int           { return len(p) }
 func (p stringSlice) Less(i, j int) bool { return p[i] < p[j] }
 func (p stringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
-func (p bytesSlice) Len() int           { return len(p) }
-func (p bytesSlice) Less(i, j int) bool { return bytes.Compare(p[i], p[j]) == -1 }
-func (p bytesSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+// func (p bytesSlice) Len() int           { return len(p) }
+// func (p bytesSlice) Less(i, j int) bool { return bytes.Compare(p[i], p[j]) == -1 }
+// func (p bytesSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
 func (p boolSlice) Len() int           { return len(p) }
 func (p boolSlice) Less(i, j int) bool { return !p[i] && p[j] }
@@ -1998,6 +1966,9 @@ type bitset256 [32]byte
 func (x *bitset256) isset(pos byte) bool {
 	return x[pos>>3]&(1<<(pos&7)) != 0
 }
+func (x *bitset256) issetv(pos byte) byte {
+	return x[pos>>3] & (1 << (pos & 7))
+}
 func (x *bitset256) set(pos byte) {
 	x[pos>>3] |= (1 << (pos & 7))
 }
@@ -2032,14 +2003,29 @@ func (x *bitset32) set(pos byte) {
 // 	x[pos>>3] &^= (1 << (pos & 7))
 // }
 
+// type bit2set256 [64]byte
+
+// func (x *bit2set256) set(pos byte, v1, v2 bool) {
+// 	var pos2 uint8 = (pos & 3) << 1 // returning 0, 2, 4 or 6
+// 	if v1 {
+// 		x[pos>>2] |= 1 << (pos2 + 1)
+// 	}
+// 	if v2 {
+// 		x[pos>>2] |= 1 << pos2
+// 	}
+// }
+// func (x *bit2set256) get(pos byte) uint8 {
+// 	var pos2 uint8 = (pos & 3) << 1     // returning 0, 2, 4 or 6
+// 	return x[pos>>2] << (6 - pos2) >> 6 // 11000000 -> 00000011
+// }
+
 // ------------
 
 type pooler struct {
-	// for stringRV
-	strRv8, strRv16, strRv32, strRv64, strRv128 sync.Pool
-	// for the decNaked
-	dn     sync.Pool
-	tiload sync.Pool
+	dn                                          sync.Pool // for decNaked
+	cfn                                         sync.Pool // for codecFner
+	tiload                                      sync.Pool
+	strRv8, strRv16, strRv32, strRv64, strRv128 sync.Pool // for stringRV
 }
 
 func (p *pooler) init() {
@@ -2050,6 +2036,7 @@ func (p *pooler) init() {
 	p.strRv128.New = func() interface{} { return new([128]stringRv) }
 	p.dn.New = func() interface{} { x := new(decNaked); x.init(); return x }
 	p.tiload.New = func() interface{} { return new(typeInfoLoadArray) }
+	p.cfn.New = func() interface{} { return new(codecFner) }
 }
 
 func (p *pooler) stringRv8() (sp *sync.Pool, v interface{}) {
@@ -2070,10 +2057,37 @@ func (p *pooler) stringRv128() (sp *sync.Pool, v interface{}) {
 func (p *pooler) decNaked() (sp *sync.Pool, v interface{}) {
 	return &p.dn, p.dn.Get()
 }
+func (p *pooler) codecFner() (sp *sync.Pool, v interface{}) {
+	return &p.cfn, p.cfn.Get()
+}
 func (p *pooler) tiLoad() (sp *sync.Pool, v interface{}) {
 	return &p.tiload, p.tiload.Get()
 }
 
+// func (p *pooler) decNaked() (v *decNaked, f func(*decNaked) ) {
+// 	sp := &(p.dn)
+// 	vv := sp.Get()
+// 	return vv.(*decNaked), func(x *decNaked) { sp.Put(vv) }
+// }
+// func (p *pooler) decNakedGet() (v interface{}) {
+// 	return p.dn.Get()
+// }
+// func (p *pooler) codecFnerGet() (v interface{}) {
+// 	return p.cfn.Get()
+// }
+// func (p *pooler) tiLoadGet() (v interface{}) {
+// 	return p.tiload.Get()
+// }
+// func (p *pooler) decNakedPut(v interface{}) {
+// 	p.dn.Put(v)
+// }
+// func (p *pooler) codecFnerPut(v interface{}) {
+// 	p.cfn.Put(v)
+// }
+// func (p *pooler) tiLoadPut(v interface{}) {
+// 	p.tiload.Put(v)
+// }
+
 type panicHdl struct{}
 
 func (panicHdl) errorv(err error) {

+ 326 - 204
codec/json.go

@@ -66,18 +66,15 @@ const (
 	jsonLitNull   = 14
 )
 
-var (
-	// jsonFloat64Pow10 = [...]float64{
-	// 	1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
-	// 	1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
-	// 	1e20, 1e21, 1e22,
-	// }
+const (
+	jsonU4Chk2 = '0'
+	jsonU4Chk1 = 'a' - 10
+	jsonU4Chk0 = 'A' - 10
 
-	// jsonUint64Pow10 = [...]uint64{
-	// 	1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
-	// 	1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
-	// }
+	jsonScratchArrayLen = 64
+)
 
+var (
 	// jsonTabs and jsonSpaces are used as caches for indents
 	jsonTabs, jsonSpaces string
 
@@ -86,9 +83,6 @@ var (
 	jsonCharWhitespaceSet bitset256
 	jsonNumSet            bitset256
 	// jsonIsFloatSet        bitset256
-
-	jsonU4Set        [256]byte
-	jsonContainerSet [256]valueType
 )
 
 const (
@@ -141,58 +135,111 @@ func init() {
 			jsonNumSet.set(i)
 		}
 	}
-	for j := range jsonU4Set {
-		switch i = byte(j); i {
-		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-			jsonU4Set[i] = i - '0'
-		case 'a', 'b', 'c', 'd', 'e', 'f':
-			jsonU4Set[i] = i - 'a' + 10
-		case 'A', 'B', 'C', 'D', 'E', 'F':
-			jsonU4Set[i] = i - 'A' + 10
-		default:
-			jsonU4Set[i] = jsonU4SetErrVal
-		}
-		// switch i = byte(j); i {
-		// case 'e', 'E', '.':
-		// 	jsonIsFloatSet.set(i)
-		// }
-	}
-	for j := range jsonContainerSet {
-		switch i = byte(j); i {
-		case '{':
-			jsonContainerSet[j] = valueTypeMap
-		case '[':
-			jsonContainerSet[j] = valueTypeArray
-		case 'n':
-			jsonContainerSet[j] = valueTypeNil
-		case '"':
-			jsonContainerSet[j] = valueTypeString
-		default:
-			jsonContainerSet[j] = valueTypeUnset
-		}
+}
+
+// ----------------
+
+type jsonEncDriverTypical struct {
+	w encWriter
+	// w  *encWriterSwitch
+	b  *[jsonScratchArrayLen]byte
+	tw bool // term white space
+	c  containerState
+}
+
+func (e *jsonEncDriverTypical) typical() {}
+
+func (e *jsonEncDriverTypical) reset(ee *jsonEncDriver) {
+	e.w = ee.ew
+	// e.w = &ee.e.encWriterSwitch
+	e.b = &ee.b
+	e.tw = ee.h.TermWhitespace
+	e.c = 0
+}
+
+func (e *jsonEncDriverTypical) WriteArrayStart(length int) {
+	e.w.writen1('[')
+	e.c = containerArrayStart
+}
+
+func (e *jsonEncDriverTypical) WriteArrayElem() {
+	if e.c != containerArrayStart {
+		e.w.writen1(',')
+	}
+	e.c = containerArrayElem
+}
+
+func (e *jsonEncDriverTypical) WriteArrayEnd() {
+	e.w.writen1(']')
+	e.c = containerArrayEnd
+}
+
+func (e *jsonEncDriverTypical) WriteMapStart(length int) {
+	e.w.writen1('{')
+	e.c = containerMapStart
+}
+
+func (e *jsonEncDriverTypical) WriteMapElemKey() {
+	if e.c != containerMapStart {
+		e.w.writen1(',')
+	}
+	e.c = containerMapKey
+}
+
+func (e *jsonEncDriverTypical) WriteMapElemValue() {
+	e.w.writen1(':')
+	e.c = containerMapValue
+}
+
+func (e *jsonEncDriverTypical) WriteMapEnd() {
+	e.w.writen1('}')
+	e.c = containerMapEnd
+}
+
+func (e *jsonEncDriverTypical) EncodeBool(b bool) {
+	if b {
+		e.w.writeb(jsonLiterals[jsonLitTrue : jsonLitTrue+4])
+	} else {
+		e.w.writeb(jsonLiterals[jsonLitFalse : jsonLitFalse+5])
 	}
+}
 
-	// jsonU4Set[255] = jsonU4SetErrVal
+func (e *jsonEncDriverTypical) EncodeFloat64(f float64) {
+	fmt, prec := jsonFloatStrconvFmtPrec(f)
+	e.w.writeb(strconv.AppendFloat(e.b[:0], f, fmt, prec, 64))
 }
 
-type jsonEncDriver struct {
-	noBuiltInTypes
-	e  *Encoder
-	h  *JsonHandle
+func (e *jsonEncDriverTypical) EncodeInt(v int64) {
+	e.w.writeb(strconv.AppendInt(e.b[:0], v, 10))
+}
+
+func (e *jsonEncDriverTypical) EncodeUint(v uint64) {
+	e.w.writeb(strconv.AppendUint(e.b[:0], v, 10))
+}
+
+func (e *jsonEncDriverTypical) EncodeFloat32(f float32) {
+	e.EncodeFloat64(float64(f))
+}
+
+func (e *jsonEncDriverTypical) atEndOfEncode() {
+	if e.tw {
+		e.w.writen1(' ')
+	}
+}
+
+// ----------------
+
+type jsonEncDriverGeneric struct {
 	w  encWriter // encWriter // *encWriterSwitch
-	se setExtWrapper
-	// ---- cpu cache line boundary?
+	b  *[jsonScratchArrayLen]byte
 	ds string // indent string
 	d  bool   // indent
 	dt bool   // indent using tabs
-
-	// ---- writable fields during execution --- *try* to keep in sep cache line
-
-	c  containerState
 	dl uint16 // indent level
-	bs []byte // scratch
-	// ---- cpu cache line boundary?
-	b [64]byte // scratch
+	ks bool   // map key as string
+	is byte   // integer as string
+	tw bool   // term white space
+	c  containerState
 }
 
 // indent is done as below:
@@ -200,7 +247,26 @@ type jsonEncDriver struct {
 //   - newline and indent are added before each ending,
 //     except there was no entry (so we can have {} or [])
 
-func (e *jsonEncDriver) WriteArrayStart(length int) {
+func (e *jsonEncDriverGeneric) reset(ee *jsonEncDriver) {
+	e.w = ee.ew
+	e.b = &ee.b
+	e.tw = ee.h.TermWhitespace
+	e.c = 0
+	e.d, e.dt, e.dl, e.ds = false, false, 0, ""
+	h := ee.h
+	if h.Indent > 0 {
+		e.d = true
+		e.ds = jsonSpaces[:h.Indent]
+	} else if h.Indent < 0 {
+		e.d = true
+		e.dt = true
+		e.ds = jsonTabs[:-(h.Indent)]
+	}
+	e.ks = h.MapKeyAsString
+	e.is = h.IntegerAsString
+}
+
+func (e *jsonEncDriverGeneric) WriteArrayStart(length int) {
 	if e.d {
 		e.dl++
 	}
@@ -208,7 +274,7 @@ func (e *jsonEncDriver) WriteArrayStart(length int) {
 	e.c = containerArrayStart
 }
 
-func (e *jsonEncDriver) WriteArrayElem() {
+func (e *jsonEncDriverGeneric) WriteArrayElem() {
 	if e.c != containerArrayStart {
 		e.w.writen1(',')
 	}
@@ -218,7 +284,7 @@ func (e *jsonEncDriver) WriteArrayElem() {
 	e.c = containerArrayElem
 }
 
-func (e *jsonEncDriver) WriteArrayEnd() {
+func (e *jsonEncDriverGeneric) WriteArrayEnd() {
 	if e.d {
 		e.dl--
 		if e.c != containerArrayStart {
@@ -229,7 +295,7 @@ func (e *jsonEncDriver) WriteArrayEnd() {
 	e.c = containerArrayEnd
 }
 
-func (e *jsonEncDriver) WriteMapStart(length int) {
+func (e *jsonEncDriverGeneric) WriteMapStart(length int) {
 	if e.d {
 		e.dl++
 	}
@@ -237,7 +303,7 @@ func (e *jsonEncDriver) WriteMapStart(length int) {
 	e.c = containerMapStart
 }
 
-func (e *jsonEncDriver) WriteMapElemKey() {
+func (e *jsonEncDriverGeneric) WriteMapElemKey() {
 	if e.c != containerMapStart {
 		e.w.writen1(',')
 	}
@@ -247,7 +313,7 @@ func (e *jsonEncDriver) WriteMapElemKey() {
 	e.c = containerMapKey
 }
 
-func (e *jsonEncDriver) WriteMapElemValue() {
+func (e *jsonEncDriverGeneric) WriteMapElemValue() {
 	if e.d {
 		e.w.writen2(':', ' ')
 	} else {
@@ -256,7 +322,7 @@ func (e *jsonEncDriver) WriteMapElemValue() {
 	e.c = containerMapValue
 }
 
-func (e *jsonEncDriver) WriteMapEnd() {
+func (e *jsonEncDriverGeneric) WriteMapEnd() {
 	if e.d {
 		e.dl--
 		if e.c != containerMapStart {
@@ -267,7 +333,7 @@ func (e *jsonEncDriver) WriteMapEnd() {
 	e.c = containerMapEnd
 }
 
-func (e *jsonEncDriver) writeIndent() {
+func (e *jsonEncDriverGeneric) writeIndent() {
 	e.w.writen1('\n')
 	if x := len(e.ds) * int(e.dl); x <= jsonSpacesOrTabsLen {
 		if e.dt {
@@ -282,36 +348,8 @@ func (e *jsonEncDriver) writeIndent() {
 	}
 }
 
-func (e *jsonEncDriver) EncodeNil() {
-	// We always encode nil as just null (never in quotes)
-	// This allows us to easily decode if a nil in the json stream
-	// ie if initial token is n.
-	e.w.writeb(jsonLiterals[jsonLitNull : jsonLitNull+4])
-
-	// if e.h.MapKeyAsString && e.c == containerMapKey {
-	// 	e.w.writeb(jsonLiterals[jsonLitNullQ : jsonLitNullQ+6])
-	// } else {
-	// 	e.w.writeb(jsonLiterals[jsonLitNull : jsonLitNull+4])
-	// }
-}
-
-func (e *jsonEncDriver) EncodeTime(t time.Time) {
-	// Do NOT use MarshalJSON, as it allocates internally.
-	// instead, we call AppendFormat directly, using our scratch buffer (e.b)
-	if t.IsZero() {
-		e.EncodeNil()
-	} else {
-		e.b[0] = '"'
-		b := t.AppendFormat(e.b[1:1], time.RFC3339Nano)
-		e.b[len(b)+1] = '"'
-		e.w.writeb(e.b[:len(b)+2])
-	}
-	// fmt.Printf(">>>> time as a string: '%s'\n", e.b[:len(b)+2])
-	// v, err := t.MarshalJSON(); if err != nil { e.e.error(err) } e.w.writeb(v)
-}
-
-func (e *jsonEncDriver) EncodeBool(b bool) {
-	if e.h.MapKeyAsString && e.c == containerMapKey {
+func (e *jsonEncDriverGeneric) EncodeBool(b bool) {
+	if e.ks && e.c == containerMapKey {
 		if b {
 			e.w.writeb(jsonLiterals[jsonLitTrueQ : jsonLitTrueQ+6])
 		} else {
@@ -326,37 +364,12 @@ func (e *jsonEncDriver) EncodeBool(b bool) {
 	}
 }
 
-func (e *jsonEncDriver) EncodeFloat32(f float32) {
-	// e.encodeFloat(float64(f), 32)
-	// always encode all floats as IEEE 64-bit floating point.
-	// It also ensures that we can decode in full precision even if into a float32,
-	// as what is written is always to float64 precision.
-	e.EncodeFloat64(float64(f))
-}
-
-func (e *jsonEncDriver) EncodeFloat64(f float64) {
-	var blen int
+func (e *jsonEncDriverGeneric) EncodeFloat64(f float64) {
 	// instead of using 'g', specify whether to use 'e' or 'f'
-	var abs = math.Abs(f)
-	var fmt byte
-	var prec int = -1
-	if abs != 0 && (abs < 1e-6 || abs >= 1e21) {
-		fmt = 'e'
-	} else {
-		fmt = 'f'
-		// set prec to 1 iff mod is 0.
-		//     better than using jsonIsFloatBytesB2 to check if a . or E in the float bytes.
-		// this ensures that every float has an e or .0 in it.
-		if abs <= 1 {
-			if abs == 0 || abs == 1 {
-				prec = 1
-			}
-		} else if _, mod := math.Modf(abs); mod == 0 {
-			prec = 1
-		}
-	}
+	fmt, prec := jsonFloatStrconvFmtPrec(f)
 
-	if e.h.MapKeyAsString && e.c == containerMapKey {
+	var blen int
+	if e.ks && e.c == containerMapKey {
 		blen = 2 + len(strconv.AppendFloat(e.b[1:1], f, fmt, prec, 64))
 		e.b[0] = '"'
 		e.b[blen-1] = '"'
@@ -366,9 +379,9 @@ func (e *jsonEncDriver) EncodeFloat64(f float64) {
 	e.w.writeb(e.b[:blen])
 }
 
-func (e *jsonEncDriver) EncodeInt(v int64) {
-	x := e.h.IntegerAsString
-	if x == 'A' || x == 'L' && (v > 1<<53 || v < -(1<<53)) || (e.h.MapKeyAsString && e.c == containerMapKey) {
+func (e *jsonEncDriverGeneric) EncodeInt(v int64) {
+	x := e.is
+	if x == 'A' || x == 'L' && (v > 1<<53 || v < -(1<<53)) || (e.ks && e.c == containerMapKey) {
 		blen := 2 + len(strconv.AppendInt(e.b[1:1], v, 10))
 		e.b[0] = '"'
 		e.b[blen-1] = '"'
@@ -378,9 +391,9 @@ func (e *jsonEncDriver) EncodeInt(v int64) {
 	e.w.writeb(strconv.AppendInt(e.b[:0], v, 10))
 }
 
-func (e *jsonEncDriver) EncodeUint(v uint64) {
-	x := e.h.IntegerAsString
-	if x == 'A' || x == 'L' && v > 1<<53 || (e.h.MapKeyAsString && e.c == containerMapKey) {
+func (e *jsonEncDriverGeneric) EncodeUint(v uint64) {
+	x := e.is
+	if x == 'A' || x == 'L' && v > 1<<53 || (e.ks && e.c == containerMapKey) {
 		blen := 2 + len(strconv.AppendUint(e.b[1:1], v, 10))
 		e.b[0] = '"'
 		e.b[blen-1] = '"'
@@ -390,6 +403,65 @@ func (e *jsonEncDriver) EncodeUint(v uint64) {
 	e.w.writeb(strconv.AppendUint(e.b[:0], v, 10))
 }
 
+func (e *jsonEncDriverGeneric) EncodeFloat32(f float32) {
+	// e.encodeFloat(float64(f), 32)
+	// always encode all floats as IEEE 64-bit floating point.
+	// It also ensures that we can decode in full precision even if into a float32,
+	// as what is written is always to float64 precision.
+	e.EncodeFloat64(float64(f))
+}
+
+func (e *jsonEncDriverGeneric) atEndOfEncode() {
+	if e.tw {
+		if e.d {
+			e.w.writen1('\n')
+		} else {
+			e.w.writen1(' ')
+		}
+	}
+}
+
+// --------------------
+
+type jsonEncDriver struct {
+	noBuiltInTypes
+	e  *Encoder
+	h  *JsonHandle
+	ew encWriter // encWriter // *encWriterSwitch
+	se extWrapper
+	// ---- cpu cache line boundary?
+	bs []byte // scratch
+	// ---- cpu cache line boundary?
+	b [jsonScratchArrayLen]byte // scratch (encode time,
+}
+
+func (e *jsonEncDriver) EncodeNil() {
+	// We always encode nil as just null (never in quotes)
+	// This allows us to easily decode if a nil in the json stream
+	// ie if initial token is n.
+	e.ew.writeb(jsonLiterals[jsonLitNull : jsonLitNull+4])
+
+	// if e.h.MapKeyAsString && e.c == containerMapKey {
+	// 	e.ew.writeb(jsonLiterals[jsonLitNullQ : jsonLitNullQ+6])
+	// } else {
+	// 	e.ew.writeb(jsonLiterals[jsonLitNull : jsonLitNull+4])
+	// }
+}
+
+func (e *jsonEncDriver) EncodeTime(t time.Time) {
+	// Do NOT use MarshalJSON, as it allocates internally.
+	// instead, we call AppendFormat directly, using our scratch buffer (e.b)
+	if t.IsZero() {
+		e.EncodeNil()
+	} else {
+		e.b[0] = '"'
+		b := t.AppendFormat(e.b[1:1], time.RFC3339Nano)
+		e.b[len(b)+1] = '"'
+		e.ew.writeb(e.b[:len(b)+2])
+	}
+	// v, err := t.MarshalJSON(); if err != nil { e.e.error(err) } e.ew.writeb(v)
+}
+
 func (e *jsonEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, en *Encoder) {
 	if v := ext.ConvertExt(rv); v == nil {
 		e.EncodeNil()
@@ -411,10 +483,6 @@ func (e *jsonEncDriver) EncodeString(c charEncoding, v string) {
 	e.quoteStr(v)
 }
 
-// func (e *jsonEncDriver) EncodeSymbol(v string) {
-// 	e.quoteStr(v)
-// }
-
 func (e *jsonEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
 	// if encoding raw bytes and RawBytesExt is configured, use it to encode
 	if v == nil {
@@ -422,34 +490,35 @@ func (e *jsonEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
 		return
 	}
 	if c == cRAW {
-		if e.se.i != nil {
+		if e.se.InterfaceExt != nil {
 			e.EncodeExt(v, 0, &e.se, e.e)
 			return
 		}
 
 		slen := base64.StdEncoding.EncodedLen(len(v))
-		if cap(e.bs) >= slen {
-			e.bs = e.bs[:slen]
+		if cap(e.bs) >= slen+2 {
+			e.bs = e.bs[:slen+2]
 		} else {
-			e.bs = make([]byte, slen)
+			e.bs = make([]byte, slen+2)
 		}
-		base64.StdEncoding.Encode(e.bs, v)
-		e.w.writen1('"')
-		e.w.writeb(e.bs)
-		e.w.writen1('"')
+		e.bs[0] = '"'
+		base64.StdEncoding.Encode(e.bs[1:], v)
+		e.bs[slen+1] = '"'
+		e.ew.writeb(e.bs)
 	} else {
 		e.quoteStr(stringView(v))
 	}
 }
 
 func (e *jsonEncDriver) EncodeAsis(v []byte) {
-	e.w.writeb(v)
+	e.ew.writeb(v)
 }
 
 func (e *jsonEncDriver) quoteStr(s string) {
 	// adapted from std pkg encoding/json
 	const hex = "0123456789abcdef"
-	w := e.w
+	w := e.ew
+	htmlasis := e.h.HTMLCharsAsIs
 	w.writen1('"')
 	var start int
 	for i, slen := 0, len(s); i < slen; {
@@ -457,7 +526,8 @@ func (e *jsonEncDriver) quoteStr(s string) {
 		// also encode < > & to prevent security holes when served to some browsers.
 		if b := s[i]; b < utf8.RuneSelf {
 			// if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
-			if jsonCharHtmlSafeSet.isset(b) || (e.h.HTMLCharsAsIs && jsonCharSafeSet.isset(b)) {
+			// if (htmlasis && jsonCharSafeSet.isset(b)) || jsonCharHtmlSafeSet.isset(b) {
+			if jsonCharHtmlSafeSet.isset(b) || (htmlasis && jsonCharSafeSet.isset(b)) {
 				i++
 				continue
 			}
@@ -515,22 +585,12 @@ func (e *jsonEncDriver) quoteStr(s string) {
 	w.writen1('"')
 }
 
-func (e *jsonEncDriver) atEndOfEncode() {
-	if e.h.TermWhitespace {
-		if e.d {
-			e.w.writen1('\n')
-		} else {
-			e.w.writen1(' ')
-		}
-	}
-}
-
 type jsonDecDriver struct {
 	noBuiltInTypes
 	d  *Decoder
 	h  *JsonHandle
 	r  decReader // *decReaderSwitch // decReader
-	se setExtWrapper
+	se extWrapper
 
 	// ---- writable fields during execution --- *try* to keep in sep cache line
 
@@ -541,8 +601,8 @@ type jsonDecDriver struct {
 	bs    []byte  // scratch. Initialized from b. Used for parsing strings or numbers.
 	bstr  [8]byte // scratch used for string \UXXX parsing
 	// ---- cpu cache line boundary?
-	b  [64]byte // scratch 1, used for parsing strings or numbers or time.Time
-	b2 [64]byte // scratch 2, used only for readUntil, decNumBytes
+	b  [jsonScratchArrayLen]byte // scratch 1, used for parsing strings or numbers or time.Time
+	b2 [jsonScratchArrayLen]byte // scratch 2, used only for readUntil, decNumBytes
 
 	// n jsonNum
 }
@@ -738,18 +798,6 @@ func (d *jsonDecDriver) ContainerType() (vt valueType) {
 	// optimize this, so we don't do 4 checks but do one computation.
 	// return jsonContainerSet[d.tok]
 
-	// switch d.tok {
-	// case '{':
-	// 	return valueTypeMap
-	// case '[':
-	// 	return valueTypeArray
-	// case 'n':
-	// 	return valueTypeNil
-	// case '"':
-	// 	return valueTypeString
-	// }
-	// return valueTypeUnset
-
 	// ContainerType is mostly called for Map and Array,
 	// so this conditional is good enough (max 2 checks typically)
 	if b := d.tok; b == '{' {
@@ -762,9 +810,6 @@ func (d *jsonDecDriver) ContainerType() (vt valueType) {
 		return valueTypeString
 	}
 	return valueTypeUnset
-
-	// d.d.errorf("isContainerType: unsupported parameter: %v", vt)
-	// return false // "unreachable"
 }
 
 func (d *jsonDecDriver) decNumBytes() (bs []byte) {
@@ -826,7 +871,7 @@ func (d *jsonDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxta
 
 func (d *jsonDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte) {
 	// if decoding into raw bytes, and the RawBytesExt is configured, use it to decode.
-	if d.se.i != nil {
+	if d.se.InterfaceExt != nil {
 		bsOut = bs
 		d.DecodeExt(&bsOut, 0, &d.se)
 		return
@@ -967,14 +1012,19 @@ func (d *jsonDecDriver) appendStringAsBytes() {
 			}
 			// c = cs[i+4] // may help reduce bounds-checking
 			for j := 1; j < 5; j++ {
-				c = jsonU4Set[cs[i+j]]
-				if c == jsonU4SetErrVal {
-					// d.d.errorf(`json: unquoteStr: invalid hex char in \u unicode sequence: %q`, c)
+				// best to use this, as others involve memory loads, array lookup with bounds checks, etc
+				c = cs[i+j]
+				if c >= '0' && c <= '9' {
+					rr = rr*16 + uint32(c-jsonU4Chk2)
+				} else if c >= 'a' && c <= 'f' {
+					rr = rr*16 + uint32(c-jsonU4Chk1)
+				} else if c >= 'A' && c <= 'F' {
+					rr = rr*16 + uint32(c-jsonU4Chk0)
+				} else {
 					r = unicode.ReplacementChar
 					i += 4
 					goto encode_rune
 				}
-				rr = rr*16 + uint32(c)
 			}
 			r = rune(rr)
 			i += 4
@@ -984,14 +1034,19 @@ func (d *jsonDecDriver) appendStringAsBytes() {
 					// c = cs[i+4] // may help reduce bounds-checking
 					var rr1 uint32
 					for j := 1; j < 5; j++ {
-						c = jsonU4Set[cs[i+j]]
-						if c == jsonU4SetErrVal {
-							// d.d.errorf(`json: unquoteStr: invalid hex char in \u unicode sequence: %q`, c)
+						// best to use this, as others involve memory loads, array lookup with bounds checks, etc
+						c = cs[i+j]
+						if c >= '0' && c <= '9' {
+							rr = rr*16 + uint32(c-jsonU4Chk2)
+						} else if c >= 'a' && c <= 'f' {
+							rr = rr*16 + uint32(c-jsonU4Chk1)
+						} else if c >= 'A' && c <= 'F' {
+							rr = rr*16 + uint32(c-jsonU4Chk0)
+						} else {
 							r = unicode.ReplacementChar
 							i += 4
 							goto encode_rune
 						}
-						rr1 = rr1*16 + uint32(c)
 					}
 					r = utf16.DecodeRune(r, rune(rr1))
 					i += 4
@@ -1013,8 +1068,8 @@ func (d *jsonDecDriver) appendStringAsBytes() {
 }
 
 func (d *jsonDecDriver) nakedNum(z *decNaked, bs []byte) (err error) {
+	// if d.h.PreferFloat || bytes.ContainsAny(bs, ".eE") {
 	if d.h.PreferFloat || jsonIsFloatBytesB3(bs) { // bytes.IndexByte(bs, '.') != -1 ||...
-		// } else if d.h.PreferFloat || bytes.ContainsAny(bs, ".eE") {
 		z.v = valueTypeFloat
 		z.f, err = strconv.ParseFloat(stringView(bs), 64)
 	} else if d.h.SignedInteger || bs[0] == '-' {
@@ -1179,53 +1234,92 @@ type JsonHandle struct {
 }
 
 // Name returns the name of the handle: json
-func (h *JsonHandle) Name() string { return "json" }
-
+func (h *JsonHandle) Name() string            { return "json" }
 func (h *JsonHandle) hasElemSeparators() bool { return true }
+func (h *JsonHandle) typical() bool {
+	return h.Indent == 0 && !h.MapKeyAsString && h.IntegerAsString != 'A' && h.IntegerAsString != 'L'
+}
+
+type jsonTypical interface {
+	typical()
+}
+
+func (h *JsonHandle) recreateEncDriver(ed encDriver) (v bool) {
+	_, v = ed.(jsonTypical)
+	return v != h.typical()
+}
 
 // SetInterfaceExt sets an extension
 func (h *JsonHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
-	return h.SetExt(rt, tag, &setExtWrapper{i: ext})
+	return h.SetExt(rt, tag, &extWrapper{bytesExtFailer{}, ext})
+	// return h.SetExt(rt, tag, &setExtWrapper{i: ext})
 }
 
-func (h *JsonHandle) newEncDriver(e *Encoder) encDriver {
-	hd := jsonEncDriver{e: e, h: h}
-	hd.bs = hd.b[:0]
+type jsonEncDriverTypicalImpl struct {
+	jsonEncDriver
+	jsonEncDriverTypical
+	_ [8]byte // padding
+}
 
-	hd.reset()
+func (x *jsonEncDriverTypicalImpl) reset() {
+	x.jsonEncDriver.reset()
+	x.jsonEncDriverTypical.reset(&x.jsonEncDriver)
+}
 
-	return &hd
+type jsonEncDriverGenericImpl struct {
+	jsonEncDriver
+	jsonEncDriverGeneric
+}
+
+func (x *jsonEncDriverGenericImpl) reset() {
+	x.jsonEncDriver.reset()
+	x.jsonEncDriverGeneric.reset(&x.jsonEncDriver)
+}
+
+func (h *JsonHandle) newEncDriver(e *Encoder) (ee encDriver) {
+	// var hd jsonEncDriver
+	// hd.e = e
+	// hd.h = h
+	// hd.bs = hd.b[:0]
+	// hd.reset()
+	var hd *jsonEncDriver
+	if h.typical() {
+		// println(">>>>>>> typical enc driver")
+		var v jsonEncDriverTypicalImpl
+		ee = &v
+		hd = &v.jsonEncDriver
+	} else {
+		// println(">>>>>>> generic enc driver")
+		var v jsonEncDriverGenericImpl
+		ee = &v
+		hd = &v.jsonEncDriver
+	}
+	hd.e, hd.h, hd.bs = e, h, hd.b[:0]
+	hd.se.BytesExt = bytesExtFailer{}
+	ee.reset()
+	return
 }
 
 func (h *JsonHandle) newDecDriver(d *Decoder) decDriver {
 	// d := jsonDecDriver{r: r.(*bytesDecReader), h: h}
 	hd := jsonDecDriver{d: d, h: h}
+	hd.se.BytesExt = bytesExtFailer{}
 	hd.bs = hd.b[:0]
 	hd.reset()
 	return &hd
 }
 
 func (e *jsonEncDriver) reset() {
-	e.w = e.e.w // e.e.w // &e.e.encWriterSwitch
-	e.se.i = e.h.RawBytesExt
+	e.ew = e.e.w // e.e.w // &e.e.encWriterSwitch
+	e.se.InterfaceExt = e.h.RawBytesExt
 	if e.bs != nil {
 		e.bs = e.bs[:0]
 	}
-	e.d, e.dt, e.dl, e.ds = false, false, 0, ""
-	e.c = 0
-	if e.h.Indent > 0 {
-		e.d = true
-		e.ds = jsonSpaces[:e.h.Indent]
-	} else if e.h.Indent < 0 {
-		e.d = true
-		e.dt = true
-		e.ds = jsonTabs[:-(e.h.Indent)]
-	}
 }
 
 func (d *jsonDecDriver) reset() {
 	d.r = d.d.r // &d.d.decReaderSwitch // d.d.r
-	d.se.i = d.h.RawBytesExt
+	d.se.InterfaceExt = d.h.RawBytesExt
 	if d.bs != nil {
 		d.bs = d.bs[:0]
 	}
@@ -1254,5 +1348,33 @@ func jsonIsFloatBytesB3(bs []byte) bool {
 		bytes.IndexByte(bs, 'e') != -1
 }
 
+func jsonFloatStrconvFmtPrec(f float64) (fmt byte, prec int) {
+	prec = -1
+	var abs = math.Abs(f)
+	if abs != 0 && (abs < 1e-6 || abs >= 1e21) {
+		fmt = 'e'
+	} else {
+		fmt = 'f'
+		// set prec to 1 iff mod is 0.
+		//     better than using jsonIsFloatBytesB2 to check if a . or E in the float bytes.
+		// this ensures that every float has an e or .0 in it.
+		if abs <= 1 {
+			if abs == 0 || abs == 1 {
+				prec = 1
+			}
+		} else if _, mod := math.Modf(abs); mod == 0 {
+			prec = 1
+		}
+	}
+	return
+}
+
+// // jsonU4SetChk returns 0, 1, 2 or 3
+// func jsonU4SetChk(b byte) (v byte) {
+// 	return json2U4Set.get(b)
+// }
+
 var _ decDriver = (*jsonDecDriver)(nil)
-var _ encDriver = (*jsonEncDriver)(nil)
+var _ encDriver = (*jsonEncDriverGenericImpl)(nil)
+var _ encDriver = (*jsonEncDriverTypicalImpl)(nil)
+var _ jsonTypical = (*jsonEncDriverTypical)(nil)

+ 2 - 21
codec/msgpack.go

@@ -291,10 +291,6 @@ func (e *msgpackEncDriver) EncodeString(c charEncoding, s string) {
 	}
 }
 
-// func (e *msgpackEncDriver) EncodeSymbol(v string) {
-// 	e.EncodeString(cUTF8, v)
-// }
-
 func (e *msgpackEncDriver) EncodeStringBytes(c charEncoding, bs []byte) {
 	if bs == nil {
 		e.EncodeNil()
@@ -600,28 +596,12 @@ func (d *msgpackDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte)
 	case valueTypeArray:
 		bsOut, _ = fastpathTV.DecSliceUint8V(bs, true, d.d)
 		return
-		// clen = d.readContainerLen(msgpackContainerList)
-		// // ensure everything after is one byte each
-		// for i := 0; i < clen; i++ {
-		// 	d.readNextBd()
-		// 	if d.bd == mpNil {
-		// 		bs = append(bs, 0)
-		// 	} else if d.bd == mpUint8 {
-		// 		bs = append(bs, d.r.readn1())
-		// 	} else {
-		// 		d.d.errorf("cannot read non-byte into a byte array")
-		// 		return
-		// 	}
-		// }
-		// d.bdRead = false
-		// return bs
 	default:
 		d.d.errorf("invalid container type: expecting bin|str|array, got: 0x%x", uint8(vt))
 		return
 	}
 
 	// these are (bin|str)(8|16|32)
-	// println("DecodeBytes: clen: ", clen)
 	d.bdRead = false
 	// bytes may be nil, so handle it. if nil, clen=-1.
 	if clen < 0 {
@@ -868,6 +848,7 @@ type MsgpackHandle struct {
 	// type is provided (e.g. decoding into a nil interface{}), you get back
 	// a []byte or string based on the setting of RawToString.
 	WriteExt bool
+
 	binaryEncodingType
 	noElemSeparators
 }
@@ -877,7 +858,7 @@ func (h *MsgpackHandle) Name() string { return "msgpack" }
 
 // SetBytesExt sets an extension
 func (h *MsgpackHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
-	return h.SetExt(rt, tag, &setExtWrapper{b: ext})
+	return h.SetExt(rt, tag, &extWrapper{ext, interfaceExtFailer{}})
 }
 
 func (h *MsgpackHandle) newEncDriver(e *Encoder) encDriver {

+ 1 - 1
codec/simple.go

@@ -609,7 +609,7 @@ func (h *SimpleHandle) Name() string { return "simple" }
 
 // SetBytesExt sets an extension
 func (h *SimpleHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
-	return h.SetExt(rt, tag, &setExtWrapper{b: ext})
+	return h.SetExt(rt, tag, &extWrapper{ext, interfaceExtFailer{}})
 }
 
 func (h *SimpleHandle) hasElemSeparators() bool { return true } // as it implements Write(Map|Array)XXX

+ 0 - 188
codec/time.go

@@ -1,188 +0,0 @@
-// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
-// Use of this source code is governed by a MIT license found in the LICENSE file.
-
-package codec
-
-import "time"
-
-// var timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
-
-// EncodeTime encodes a time.Time as a []byte, including
-// information on the instant in time and UTC offset.
-//
-// Format Description
-//
-//   A timestamp is composed of 3 components:
-//
-//   - secs: signed integer representing seconds since unix epoch
-//   - nsces: unsigned integer representing fractional seconds as a
-//     nanosecond offset within secs, in the range 0 <= nsecs < 1e9
-//   - tz: signed integer representing timezone offset in minutes east of UTC,
-//     and a dst (daylight savings time) flag
-//
-//   When encoding a timestamp, the first byte is the descriptor, which
-//   defines which components are encoded and how many bytes are used to
-//   encode secs and nsecs components. *If secs/nsecs is 0 or tz is UTC, it
-//   is not encoded in the byte array explicitly*.
-//
-//       Descriptor 8 bits are of the form `A B C DDD EE`:
-//           A:   Is secs component encoded? 1 = true
-//           B:   Is nsecs component encoded? 1 = true
-//           C:   Is tz component encoded? 1 = true
-//           DDD: Number of extra bytes for secs (range 0-7).
-//                If A = 1, secs encoded in DDD+1 bytes.
-//                    If A = 0, secs is not encoded, and is assumed to be 0.
-//                    If A = 1, then we need at least 1 byte to encode secs.
-//                    DDD says the number of extra bytes beyond that 1.
-//                    E.g. if DDD=0, then secs is represented in 1 byte.
-//                         if DDD=2, then secs is represented in 3 bytes.
-//           EE:  Number of extra bytes for nsecs (range 0-3).
-//                If B = 1, nsecs encoded in EE+1 bytes (similar to secs/DDD above)
-//
-//   Following the descriptor bytes, subsequent bytes are:
-//
-//       secs component encoded in `DDD + 1` bytes (if A == 1)
-//       nsecs component encoded in `EE + 1` bytes (if B == 1)
-//       tz component encoded in 2 bytes (if C == 1)
-//
-//   secs and nsecs components are integers encoded in a BigEndian
-//   2-complement encoding format.
-//
-//   tz component is encoded as 2 bytes (16 bits). Most significant bit 15 to
-//   Least significant bit 0 are described below:
-//
-//       Timezone offset has a range of -12:00 to +14:00 (ie -720 to +840 minutes).
-//       Bit 15 = have\_dst: set to 1 if we set the dst flag.
-//       Bit 14 = dst\_on: set to 1 if dst is in effect at the time, or 0 if not.
-//       Bits 13..0 = timezone offset in minutes. It is a signed integer in Big Endian format.
-//
-func encodeTime(t time.Time) []byte {
-	//t := rv.Interface().(time.Time)
-	tsecs, tnsecs := t.Unix(), t.Nanosecond()
-	var (
-		bd   byte
-		btmp [8]byte
-		bs   [16]byte
-		i    int = 1
-	)
-	l := t.Location()
-	if l == time.UTC {
-		l = nil
-	}
-	if tsecs != 0 {
-		bd = bd | 0x80
-		bigen.PutUint64(btmp[:], uint64(tsecs))
-		f := pruneSignExt(btmp[:], tsecs >= 0)
-		bd = bd | (byte(7-f) << 2)
-		copy(bs[i:], btmp[f:])
-		i = i + (8 - f)
-	}
-	if tnsecs != 0 {
-		bd = bd | 0x40
-		bigen.PutUint32(btmp[:4], uint32(tnsecs))
-		f := pruneSignExt(btmp[:4], true)
-		bd = bd | byte(3-f)
-		copy(bs[i:], btmp[f:4])
-		i = i + (4 - f)
-	}
-	if l != nil {
-		bd = bd | 0x20
-		// Note that Go Libs do not give access to dst flag.
-		_, zoneOffset := t.Zone()
-		//zoneName, zoneOffset := t.Zone()
-		zoneOffset /= 60
-		z := uint16(zoneOffset)
-		bigen.PutUint16(btmp[:2], z)
-		// clear dst flags
-		bs[i] = btmp[0] & 0x3f
-		bs[i+1] = btmp[1]
-		i = i + 2
-	}
-	bs[0] = bd
-	return bs[0:i]
-}
-
-// DecodeTime decodes a []byte into a time.Time.
-func decodeTime(bs []byte) (tt time.Time, err error) {
-	bd := bs[0]
-	var (
-		tsec  int64
-		tnsec uint32
-		tz    uint16
-		i     byte = 1
-		i2    byte
-		n     byte
-	)
-	if bd&(1<<7) != 0 {
-		var btmp [8]byte
-		n = ((bd >> 2) & 0x7) + 1
-		i2 = i + n
-		copy(btmp[8-n:], bs[i:i2])
-		//if first bit of bs[i] is set, then fill btmp[0..8-n] with 0xff (ie sign extend it)
-		if bs[i]&(1<<7) != 0 {
-			copy(btmp[0:8-n], bsAll0xff)
-			//for j,k := byte(0), 8-n; j < k; j++ {	btmp[j] = 0xff }
-		}
-		i = i2
-		tsec = int64(bigen.Uint64(btmp[:]))
-	}
-	if bd&(1<<6) != 0 {
-		var btmp [4]byte
-		n = (bd & 0x3) + 1
-		i2 = i + n
-		copy(btmp[4-n:], bs[i:i2])
-		i = i2
-		tnsec = bigen.Uint32(btmp[:])
-	}
-	if bd&(1<<5) == 0 {
-		tt = time.Unix(tsec, int64(tnsec)).UTC()
-		return
-	}
-	// In stdlib time.Parse, when a date is parsed without a zone name, it uses "" as zone name.
-	// However, we need name here, so it can be shown when time is printed.
-	// Zone name is in form: UTC-08:00.
-	// Note that Go Libs do not give access to dst flag, so we ignore dst bits
-
-	i2 = i + 2
-	tz = bigen.Uint16(bs[i:i2])
-	// i = i2
-	// sign extend sign bit into top 2 MSB (which were dst bits):
-	if tz&(1<<13) == 0 { // positive
-		tz = tz & 0x3fff //clear 2 MSBs: dst bits
-	} else { // negative
-		tz = tz | 0xc000 //set 2 MSBs: dst bits
-	}
-	tzint := int16(tz)
-	if tzint == 0 {
-		tt = time.Unix(tsec, int64(tnsec)).UTC()
-	} else {
-		// For Go Time, do not use a descriptive timezone.
-		// It's unnecessary, and makes it harder to do a reflect.DeepEqual.
-		// The Offset already tells what the offset should be, if not on UTC and unknown zone name.
-		// var zoneName = timeLocUTCName(tzint)
-		tt = time.Unix(tsec, int64(tnsec)).In(time.FixedZone("", int(tzint)*60))
-	}
-	return
-}
-
-// func timeLocUTCName(tzint int16) string {
-// 	if tzint == 0 {
-// 		return "UTC"
-// 	}
-// 	var tzname = []byte("UTC+00:00")
-// 	//tzname := fmt.Sprintf("UTC%s%02d:%02d", tzsign, tz/60, tz%60) //perf issue using Sprintf. inline below.
-// 	//tzhr, tzmin := tz/60, tz%60 //faster if u convert to int first
-// 	var tzhr, tzmin int16
-// 	if tzint < 0 {
-// 		tzname[3] = '-' // (TODO: verify. this works here)
-// 		tzhr, tzmin = -tzint/60, (-tzint)%60
-// 	} else {
-// 		tzhr, tzmin = tzint/60, tzint%60
-// 	}
-// 	tzname[4] = timeDigits[tzhr/10]
-// 	tzname[5] = timeDigits[tzhr%10]
-// 	tzname[7] = timeDigits[tzmin/10]
-// 	tzname[8] = timeDigits[tzmin%10]
-// 	return string(tzname)
-// 	//return time.FixedZone(string(tzname), int(tzint)*60)
-// }

+ 1 - 1
codec/xml.go

@@ -501,7 +501,7 @@ func (h *XMLHandle) newDecDriver(d *Decoder) decDriver {
 }
 
 func (h *XMLHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
-	return h.SetExt(rt, tag, &setExtWrapper{i: ext})
+	return h.SetExt(rt, tag, &extWrapper{bytesExtFailer{}, ext})
 }
 
 var _ decDriver = (*xmlDecDriver)(nil)