Browse Source

codec: faster perf, reduced allocations and re-organized codebase.

The general idea now for decode:
- dereference pointers till you get down to an actual value you can use
- decode into that value

The general idea now for encode is:
- dereference pointers till you get down to an actual value you can use
- If while dereferencing, you see a nil value, encode as nil
- Else encode the base value (non-pointer)

With this simplification, we can now dereference at the top of the
encodeValue or decodeValue function, and this reduces the number of times
we recursively call these functions.

We also made some changes to reduce allocations:
- set values on pre-declared variables

In addition, we improved performance by using a slice for extHandle (not map).
Typically, only a few extensions are added to an Encoder/Decoder.
A slice (array) with linear search is a better representation.

These changes together improved performance by 5-10%.
Ugorji Nwoke 12 years ago
parent
commit
3e78e5c66e
5 changed files with 242 additions and 218 deletions
  1. 10 15
      codec/0doc.go
  2. 1 1
      codec/codecs_test.go
  3. 91 84
      codec/decode.go
  4. 61 57
      codec/encode.go
  5. 79 61
      codec/helper.go

+ 10 - 15
codec/0doc.go

@@ -141,7 +141,6 @@ A sample run of benchmark using "go test -bi -bench=.":
       	Struct recursive Depth:             1
       	Struct recursive Depth:             1
       	ApproxDeepSize Of benchmark Struct: 4694 bytes
       	ApproxDeepSize Of benchmark Struct: 4694 bytes
    Benchmark One-Pass Run:
    Benchmark One-Pass Run:
-      	 v-msgpack: len: 1600 bytes
       	      bson: len: 3025 bytes
       	      bson: len: 3025 bytes
       	   msgpack: len: 1560 bytes
       	   msgpack: len: 1560 bytes
       	      binc: len: 1187 bytes
       	      binc: len: 1187 bytes
@@ -149,20 +148,16 @@ A sample run of benchmark using "go test -bi -bench=.":
       	      json: len: 2538 bytes
       	      json: len: 2538 bytes
    ..............................................
    ..............................................
    PASS
    PASS
-   Benchmark__Msgpack__Encode	   50000	     62774 ns/op	   16336 B/op	      93 allocs/op
-   Benchmark__Msgpack__Decode	   10000	    113152 ns/op	   16195 B/op	     434 allocs/op
-   Benchmark__Binc_____Encode	   50000	     73546 ns/op	   18515 B/op	      98 allocs/op
-   Benchmark__Binc_____Decode	   10000	    112489 ns/op	   16906 B/op	     315 allocs/op
-   Benchmark__Gob______Encode	   10000	    139114 ns/op	   21143 B/op	     237 allocs/op
-   Benchmark__Gob______Decode	    5000	    412988 ns/op	   82900 B/op	    1840 allocs/op
-   Benchmark__Json_____Encode	   20000	     80286 ns/op	   13866 B/op	     102 allocs/op
-   Benchmark__Json_____Decode	   10000	    249694 ns/op	   14153 B/op	     493 allocs/op
-   Benchmark__Bson_____Encode	   10000	    123965 ns/op	   27739 B/op	     514 allocs/op
-   Benchmark__Bson_____Decode	   10000	    157703 ns/op	   16441 B/op	     789 allocs/op
-   Benchmark__VMsgpack_Encode	   50000	     67791 ns/op	   12358 B/op	     343 allocs/op
-   Benchmark__VMsgpack_Decode	   10000	    151476 ns/op	   20264 B/op	     571 allocs/op
-   ok  	ugorji.net/codec	27.609s
-
+   Benchmark__Msgpack__Encode	   50000	     61683 ns/op	   15395 B/op	      91 allocs/op
+   Benchmark__Msgpack__Decode	   10000	    111090 ns/op	   15591 B/op	     437 allocs/op
+   Benchmark__Binc_____Encode	   50000	     73577 ns/op	   17572 B/op	      95 allocs/op
+   Benchmark__Binc_____Decode	   10000	    112656 ns/op	   16474 B/op	     318 allocs/op
+   Benchmark__Gob______Encode	   10000	    138140 ns/op	   21164 B/op	     237 allocs/op
+   Benchmark__Gob______Decode	    5000	    408067 ns/op	   83202 B/op	    1840 allocs/op
+   Benchmark__Json_____Encode	   20000	     80442 ns/op	   13861 B/op	     102 allocs/op
+   Benchmark__Json_____Decode	   10000	    249169 ns/op	   14169 B/op	     493 allocs/op
+   Benchmark__Bson_____Encode	   10000	    121970 ns/op	   27717 B/op	     514 allocs/op
+   Benchmark__Bson_____Decode	   10000	    161103 ns/op	   16444 B/op	     788 allocs/op
 
 
 To run full benchmark suite (including against vmsgpack and bson), 
 To run full benchmark suite (including against vmsgpack and bson), 
 see notes in ext_dep_test.go
 see notes in ext_dep_test.go

+ 1 - 1
codec/codecs_test.go

@@ -522,7 +522,7 @@ func doTestCodecTableOne(t *testing.T, testNil bool, h Handle,
 			logT(t, "++++++++ Before and After marshal matched\n")
 			logT(t, "++++++++ Before and After marshal matched\n")
 		} else {
 		} else {
 			logT(t, "-------- Before and After marshal do not match: Error: %v"+
 			logT(t, "-------- Before and After marshal do not match: Error: %v"+
-				" ====> AGAINST: (%T) %#v, DECODED: (%T) %#v\n", err, v0check, v0check, v1, v1)
+				" ====> GOLDEN: (%T) %#v, DECODED: (%T) %#v\n", err, v0check, v0check, v1, v1)
 			failT(t)
 			failT(t)
 		}
 		}
 	}
 	}

+ 91 - 84
codec/decode.go

@@ -78,11 +78,9 @@ type decDriver interface {
 // decFnInfo has methods for registering handling decoding of a specific type
 // decFnInfo has methods for registering handling decoding of a specific type
 // based on some characteristics (builtin, extension, reflect Kind, etc)
 // based on some characteristics (builtin, extension, reflect Kind, etc)
 type decFnInfo struct {
 type decFnInfo struct {
-	sis *typeInfo
+	ti *typeInfo
 	d   *Decoder
 	d   *Decoder
 	dd  decDriver
 	dd  decDriver
-	rt    reflect.Type
-	rtid  uintptr
 	xfFn  func(reflect.Value, []byte) error
 	xfFn  func(reflect.Value, []byte) error
 	xfTag byte 
 	xfTag byte 
 }
 }
@@ -103,26 +101,17 @@ type Decoder struct {
 }
 }
 
 
 func (f *decFnInfo) builtin(rv reflect.Value) {
 func (f *decFnInfo) builtin(rv reflect.Value) {
-	for j, k := int8(0), f.sis.baseIndir; j < k; j++ {
-		rv = rv.Elem()
-	}
-	f.dd.decodeBuiltinType(f.sis.baseId, rv)
+	f.dd.decodeBuiltinType(f.ti.rtid, rv)
 }
 }
 
 
 func (f *decFnInfo) rawExt(rv reflect.Value) {
 func (f *decFnInfo) rawExt(rv reflect.Value) {
 	xtag, xbs := f.dd.decodeExt(false, 0)
 	xtag, xbs := f.dd.decodeExt(false, 0)
-	for j, k := int8(0), f.sis.baseIndir; j < k; j++ {
-		rv = rv.Elem()
-	}
 	rv.Field(0).SetUint(uint64(xtag))
 	rv.Field(0).SetUint(uint64(xtag))
 	rv.Field(1).SetBytes(xbs)
 	rv.Field(1).SetBytes(xbs)
 }
 }
 
 
 func (f *decFnInfo) ext(rv reflect.Value) {
 func (f *decFnInfo) ext(rv reflect.Value) {
 	_, xbs := f.dd.decodeExt(true, f.xfTag)
 	_, xbs := f.dd.decodeExt(true, f.xfTag)
-	for j, k := int8(0), f.sis.baseIndir; j < k; j++ {
-		rv = rv.Elem()
-	}
 	if fnerr := f.xfFn(rv, xbs); fnerr != nil {
 	if fnerr := f.xfFn(rv, xbs); fnerr != nil {
 		panic(fnerr)
 		panic(fnerr)
 	}
 	}
@@ -130,12 +119,15 @@ func (f *decFnInfo) ext(rv reflect.Value) {
 
 
 func (f *decFnInfo) binaryMarshal(rv reflect.Value) {
 func (f *decFnInfo) binaryMarshal(rv reflect.Value) {
 	var bm binaryUnmarshaler
 	var bm binaryUnmarshaler
-	if f.sis.unmIndir == -1 {
+	if f.ti.unmIndir == -1 {
 		bm = rv.Addr().Interface().(binaryUnmarshaler)
 		bm = rv.Addr().Interface().(binaryUnmarshaler)
-	} else if f.sis.unmIndir == 0 {
+	} else if f.ti.unmIndir == 0 {
 		bm = rv.Interface().(binaryUnmarshaler)
 		bm = rv.Interface().(binaryUnmarshaler)
 	} else {
 	} else {
-		for j, k := int8(0), f.sis.unmIndir; j < k; j++ {
+		for j, k := int8(0), f.ti.unmIndir; j < k; j++ {
+			if rv.IsNil() {
+				rv.Set(reflect.New(rv.Type().Elem()))
+			}
 			rv = rv.Elem()
 			rv = rv.Elem()
 		}
 		}
 		bm = rv.Interface().(binaryUnmarshaler)
 		bm = rv.Interface().(binaryUnmarshaler)
@@ -206,38 +198,40 @@ func (f *decFnInfo) kUint16(rv reflect.Value) {
 	rv.SetUint(f.dd.decodeUint(16))
 	rv.SetUint(f.dd.decodeUint(16))
 }
 }
 
 
-func (f *decFnInfo) kPtr(rv reflect.Value) {
-	if rv.IsNil() {
-		rv.Set(reflect.New(f.rt.Elem()))
-	}
-	f.d.decodeValue(rv.Elem())
-}
+// func (f *decFnInfo) kPtr(rv reflect.Value) {
+// 	debugf(">>>>>>> ??? decode kPtr called - shouldn't get called")
+// 	if rv.IsNil() {
+// 		rv.Set(reflect.New(rv.Type().Elem()))
+// 	}
+// 	f.d.decodeValue(rv.Elem())
+// }
 
 
 func (f *decFnInfo) kInterface(rv reflect.Value) {
 func (f *decFnInfo) kInterface(rv reflect.Value) {
 	f.d.decodeValue(rv.Elem())
 	f.d.decodeValue(rv.Elem())
 }
 }
 
 
 func (f *decFnInfo) kStruct(rv reflect.Value) {
 func (f *decFnInfo) kStruct(rv reflect.Value) {
+	fti := f.ti
 	if currEncodedType := f.dd.currentEncodedType(); currEncodedType == detMap {
 	if currEncodedType := f.dd.currentEncodedType(); currEncodedType == detMap {
 		containerLen := f.dd.readMapLen()
 		containerLen := f.dd.readMapLen()
 		if containerLen == 0 {
 		if containerLen == 0 {
 			return
 			return
 		}
 		}
-		sissis := f.sis.sis 
+		tisfi := fti.sfi 
 		for j := 0; j < containerLen; j++ {
 		for j := 0; j < containerLen; j++ {
 			// var rvkencname string
 			// var rvkencname string
 			// ddecode(&rvkencname)
 			// ddecode(&rvkencname)
 			f.dd.initReadNext()
 			f.dd.initReadNext()
 			rvkencname := f.dd.decodeString()
 			rvkencname := f.dd.decodeString()
-			// rvksi := sis.getForEncName(rvkencname)
-			if k := f.sis.indexForEncName(rvkencname); k > -1 {
-				sfik := sissis[k]
+			// rvksi := ti.getForEncName(rvkencname)
+			if k := fti.indexForEncName(rvkencname); k > -1 {
+				sfik := tisfi[k]
 				if sfik.i != -1 {
 				if sfik.i != -1 {
 					f.d.decodeValue(rv.Field(int(sfik.i)))
 					f.d.decodeValue(rv.Field(int(sfik.i)))
 				} else {
 				} else {
 					f.d.decodeValue(rv.FieldByIndex(sfik.is))
 					f.d.decodeValue(rv.FieldByIndex(sfik.is))
 				}
 				}
-				// f.d.decodeValue(sis.field(k, rv))
+				// f.d.decodeValue(ti.field(k, rv))
 			} else {
 			} else {
 				if f.d.h.errorIfNoField() {
 				if f.d.h.errorIfNoField() {
 					decErr("No matching struct field found when decoding stream map with key: %v", 
 					decErr("No matching struct field found when decoding stream map with key: %v", 
@@ -253,7 +247,7 @@ func (f *decFnInfo) kStruct(rv reflect.Value) {
 		if containerLen == 0 {
 		if containerLen == 0 {
 			return
 			return
 		}
 		}
-		for j, si := range f.sis.sisp {
+		for j, si := range fti.sfip {
 			if j == containerLen {
 			if j == containerLen {
 				break
 				break
 			}
 			}
@@ -263,9 +257,9 @@ func (f *decFnInfo) kStruct(rv reflect.Value) {
 				f.d.decodeValue(rv.FieldByIndex(si.is))
 				f.d.decodeValue(rv.FieldByIndex(si.is))
 			}
 			}
 		}
 		}
-		if containerLen > len(f.sis.sisp) {
+		if containerLen > len(fti.sfip) {
 			// read remaining values and throw away
 			// read remaining values and throw away
-			for j := len(f.sis.sisp); j < containerLen; j++ {
+			for j := len(fti.sfip); j < containerLen; j++ {
 				var nilintf0 interface{}
 				var nilintf0 interface{}
 				f.d.decodeValue(reflect.ValueOf(&nilintf0).Elem())
 				f.d.decodeValue(reflect.ValueOf(&nilintf0).Elem())
 			}
 			}
@@ -281,7 +275,7 @@ func (f *decFnInfo) kSlice(rv reflect.Value) {
 	// may have come in here (which may not be settable).
 	// may have come in here (which may not be settable).
 	// In places where the slice got from an array could be, we should guard with CanSet() calls.
 	// In places where the slice got from an array could be, we should guard with CanSet() calls.
 
 
-	if f.rtid == byteSliceTypId { // rawbytes
+	if f.ti.rtid == byteSliceTypId { // rawbytes
 		if bs2, changed2 := f.dd.decodeBytes(rv.Bytes()); changed2 {
 		if bs2, changed2 := f.dd.decodeBytes(rv.Bytes()); changed2 {
 			rv.SetBytes(bs2)
 			rv.SetBytes(bs2)
 		}
 		}
@@ -291,7 +285,7 @@ func (f *decFnInfo) kSlice(rv reflect.Value) {
 	containerLen := f.dd.readArrayLen()
 	containerLen := f.dd.readArrayLen()
 
 
 	if rv.IsNil() {
 	if rv.IsNil() {
-		rv.Set(reflect.MakeSlice(f.rt, containerLen, containerLen))
+		rv.Set(reflect.MakeSlice(f.ti.rt, containerLen, containerLen))
 	} 
 	} 
 	if containerLen == 0 {
 	if containerLen == 0 {
 		return
 		return
@@ -301,7 +295,7 @@ func (f *decFnInfo) kSlice(rv reflect.Value) {
 	// for example, if slice is got from unaddressable array, CanSet = false
 	// for example, if slice is got from unaddressable array, CanSet = false
 	if rvcap, rvlen := rv.Len(), rv.Cap(); containerLen > rvcap {
 	if rvcap, rvlen := rv.Len(), rv.Cap(); containerLen > rvcap {
 		if rv.CanSet() {
 		if rv.CanSet() {
-			rvn := reflect.MakeSlice(f.rt, containerLen, containerLen)
+			rvn := reflect.MakeSlice(f.ti.rt, containerLen, containerLen)
 			if rvlen > 0 {
 			if rvlen > 0 {
 				reflect.Copy(rvn, rv)
 				reflect.Copy(rvn, rv)
 			}
 			}
@@ -326,14 +320,14 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 	containerLen := f.dd.readMapLen()
 	containerLen := f.dd.readMapLen()
 
 
 	if rv.IsNil() {
 	if rv.IsNil() {
-		rv.Set(reflect.MakeMap(f.rt))
+		rv.Set(reflect.MakeMap(f.ti.rt))
 	}
 	}
 	
 	
 	if containerLen == 0 {
 	if containerLen == 0 {
 		return
 		return
 	}
 	}
 
 
-	ktype, vtype := f.rt.Key(), f.rt.Elem()
+	ktype, vtype := f.ti.rt.Key(), f.ti.rt.Elem()
 	for j := 0; j < containerLen; j++ {
 	for j := 0; j < containerLen; j++ {
 		rvk := reflect.New(ktype).Elem()
 		rvk := reflect.New(ktype).Elem()
 		f.d.decodeValue(rvk)
 		f.d.decodeValue(rvk)
@@ -510,8 +504,29 @@ func (d *Decoder) decode(iv interface{}) {
 func (d *Decoder) decodeValue(rv reflect.Value) {
 func (d *Decoder) decodeValue(rv reflect.Value) {
 	d.d.initReadNext()
 	d.d.initReadNext()
 
 
-	rt := rv.Type()
+	if d.d.tryDecodeAsNil() {
+		// If value in stream is nil, set the dereferenced value to its "zero" value (if settable).
+		for rv.Kind() == reflect.Ptr {
+			rv = rv.Elem()
+		}
+		if rv.CanSet() {
+			rv.Set(reflect.Zero(rv.Type()))
+		}
+		return
+	}
+	
+	// If stream is not containing a nil value, then we can deref to the base
+	// non-pointer value, and decode into that.
+	for rv.Kind() == reflect.Ptr {
+		if rv.IsNil() {
+			rv.Set(reflect.New(rv.Type().Elem()))
+		}
+		rv = rv.Elem()
+	}
+	
 	rvOrig := rv
 	rvOrig := rv
+
+	rt := rv.Type()
 	wasNilIntf := rt.Kind() == reflect.Interface && rv.IsNil()
 	wasNilIntf := rt.Kind() == reflect.Interface && rv.IsNil()
 
 
 	var ndesc decodeNakedContext
 	var ndesc decodeNakedContext
@@ -523,8 +538,8 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 		if ndesc == dncNil {
 		if ndesc == dncNil {
 			return
 			return
 		}
 		}
-		//Prevent from decoding into nil error, io.Reader, etc if non-nil value in stream.
-		//We can only decode into interface{} (0 methods). Else reflect.Set fails later.
+		// Cannot decode into nil interface with methods (e.g. error, io.Reader, etc) 
+		// if non-nil value in stream.
 		if num := rt.NumMethod(); num > 0 {
 		if num := rt.NumMethod(); num > 0 {
 			decErr("decodeValue: Cannot decode non-nil codec value into nil %v (%v methods)", rt, num)
 			decErr("decodeValue: Cannot decode non-nil codec value into nil %v (%v methods)", rt, num)
 		} 
 		} 
@@ -533,17 +548,7 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 			return
 			return
 		}
 		}
 		rt = rv.Type()
 		rt = rv.Type()
-	} else if d.d.tryDecodeAsNil() {
-		// Note: if value in stream is nil, 
-		// then we set the dereferenced value to its "zero" value (if settable).
-		for rv.Kind() == reflect.Ptr {
-			rv = rv.Elem()
-		}
-		if rv.CanSet() {
-			rv.Set(reflect.Zero(rv.Type()))
-		}
-		return
-	}
+	} 
 
 
 	rtid := reflect.ValueOf(rt).Pointer()
 	rtid := reflect.ValueOf(rt).Pointer()
 	
 	
@@ -567,7 +572,8 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 	}
 	}
 	if !ok {
 	if !ok {
 		// debugf("\tCreating new dec fn for type: %v\n", rt)
 		// debugf("\tCreating new dec fn for type: %v\n", rt)
-		fi := decFnInfo { sis:getTypeInfo(rtid, rt), d:d, dd:d.d, rt:rt, rtid:rtid }
+		fi := decFnInfo { ti:getTypeInfo(rtid, rt), d:d, dd:d.d }
+		fn.i = &fi 
 		// An extension can be registered for any type, regardless of the Kind
 		// An extension can be registered for any type, regardless of the Kind
 		// (e.g. type BitSet int64, type MyStruct { / * unexported fields * / }, type X []int, etc.
 		// (e.g. type BitSet int64, type MyStruct { / * unexported fields * / }, type X []int, etc.
 		//
 		//
@@ -577,61 +583,62 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 		//
 		//
 		// If we are checking for builtin or ext type here, it means we didn't go through decodeNaked,
 		// If we are checking for builtin or ext type here, it means we didn't go through decodeNaked,
 		// Because decodeNaked would have handled it. It also means wasNilIntf = false.
 		// Because decodeNaked would have handled it. It also means wasNilIntf = false.
-		if fi.sis.baseId == rawExtTypId {
-			fn = decFn { &fi, (*decFnInfo).rawExt }
-		} else if d.d.isBuiltinType(fi.sis.baseId) {
-			fn = decFn { &fi, (*decFnInfo).builtin }
-		} else if xfTag, xfFn := d.h.getDecodeExt(fi.sis.baseId); xfFn != nil {
+		// 
+		// NOTE: if decoding into a nil interface{}, we return a non-nil
+		// value except even if the container registers a length of 0.
+		if rtid == rawExtTypId {
+			fn.f = (*decFnInfo).rawExt 
+		} else if d.d.isBuiltinType(rtid) {
+			fn.f = (*decFnInfo).builtin 
+		} else if xfTag, xfFn := d.h.getDecodeExt(rtid); xfFn != nil {
 			fi.xfTag, fi.xfFn = xfTag, xfFn
 			fi.xfTag, fi.xfFn = xfTag, xfFn
-			fn = decFn { &fi, (*decFnInfo).ext }
-		} else if supportBinaryMarshal && fi.sis.unm {
-			fn = decFn { &fi, (*decFnInfo).binaryMarshal }
+			fn.f = (*decFnInfo).ext 
+		} else if supportBinaryMarshal && fi.ti.unm {
+			fn.f = (*decFnInfo).binaryMarshal 
 		} else {
 		} else {
-			// NOTE: if decoding into a nil interface{}, we return a non-nil
-			// value except even if the container registers a length of 0.
 			switch rk := rt.Kind(); rk {
 			switch rk := rt.Kind(); rk {
 			case reflect.String:
 			case reflect.String:
-				fn = decFn { &fi, (*decFnInfo).kString }
+				fn.f = (*decFnInfo).kString 
 			case reflect.Bool:
 			case reflect.Bool:
-				fn = decFn { &fi, (*decFnInfo).kBool }
+				fn.f = (*decFnInfo).kBool 
 			case reflect.Int:
 			case reflect.Int:
-				fn = decFn { &fi, (*decFnInfo).kInt }
+				fn.f = (*decFnInfo).kInt 
 			case reflect.Int64:
 			case reflect.Int64:
-				fn = decFn { &fi, (*decFnInfo).kInt64 }
+				fn.f = (*decFnInfo).kInt64 
 			case reflect.Int32:
 			case reflect.Int32:
-				fn = decFn { &fi, (*decFnInfo).kInt32 }
+				fn.f = (*decFnInfo).kInt32 
 			case reflect.Int8:
 			case reflect.Int8:
-				fn = decFn { &fi, (*decFnInfo).kInt8 }
+				fn.f = (*decFnInfo).kInt8 
 			case reflect.Int16:
 			case reflect.Int16:
-				fn = decFn { &fi, (*decFnInfo).kInt16 }
+				fn.f = (*decFnInfo).kInt16 
 			case reflect.Float32:
 			case reflect.Float32:
-				fn = decFn { &fi, (*decFnInfo).kFloat32 }
+				fn.f = (*decFnInfo).kFloat32 
 			case reflect.Float64:
 			case reflect.Float64:
-				fn = decFn { &fi, (*decFnInfo).kFloat64 }
+				fn.f = (*decFnInfo).kFloat64 
 			case reflect.Uint8:
 			case reflect.Uint8:
-				fn = decFn { &fi, (*decFnInfo).kUint8 }
+				fn.f = (*decFnInfo).kUint8 
 			case reflect.Uint64:
 			case reflect.Uint64:
-				fn = decFn { &fi, (*decFnInfo).kUint64 }
+				fn.f = (*decFnInfo).kUint64 
 			case reflect.Uint:
 			case reflect.Uint:
-				fn = decFn { &fi, (*decFnInfo).kUint }
+				fn.f = (*decFnInfo).kUint 
 			case reflect.Uint32:
 			case reflect.Uint32:
-				fn = decFn { &fi, (*decFnInfo).kUint32 }
+				fn.f = (*decFnInfo).kUint32 
 			case reflect.Uint16:
 			case reflect.Uint16:
-				fn = decFn { &fi, (*decFnInfo).kUint16 }
-			case reflect.Ptr:
-				fn = decFn { &fi, (*decFnInfo).kPtr }
+				fn.f = (*decFnInfo).kUint16 
+			// case reflect.Ptr:
+			// 	fn.f = (*decFnInfo).kPtr 
 			case reflect.Interface:
 			case reflect.Interface:
-				fn = decFn { &fi, (*decFnInfo).kInterface }
+				fn.f = (*decFnInfo).kInterface 
 			case reflect.Struct:
 			case reflect.Struct:
-				fn = decFn { &fi, (*decFnInfo).kStruct }
+				fn.f = (*decFnInfo).kStruct 
 			case reflect.Slice:
 			case reflect.Slice:
-				fn = decFn { &fi, (*decFnInfo).kSlice }
+				fn.f = (*decFnInfo).kSlice 
 			case reflect.Array:
 			case reflect.Array:
-				fn = decFn { &fi, (*decFnInfo).kArray }
+				fn.f = (*decFnInfo).kArray 
 			case reflect.Map:
 			case reflect.Map:
-				fn = decFn { &fi, (*decFnInfo).kMap }
+				fn.f = (*decFnInfo).kMap 
 			default:
 			default:
-				fn = decFn { &fi, (*decFnInfo).kErr }
+				fn.f = (*decFnInfo).kErr 
 			}
 			}
 		}
 		}
 		if useMapForCodecCache {
 		if useMapForCodecCache {
@@ -640,17 +647,17 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 			}
 			}
 			d.f[rtid] = fn
 			d.f[rtid] = fn
 		} else {
 		} else {
-			d.s = append(d.s, fn )
+			d.s = append(d.s, fn)
 			d.x = append(d.x, rtid)
 			d.x = append(d.x, rtid)
 		}
 		}
 	}
 	}
 	
 	
 	fn.f(fn.i, rv)
 	fn.f(fn.i, rv)
 	
 	
-
 	if wasNilIntf {
 	if wasNilIntf {
 		rvOrig.Set(rv)
 		rvOrig.Set(rv)
 	}
 	}
+	
 	return
 	return
 }
 }
 
 

+ 61 - 57
codec/encode.go

@@ -59,11 +59,9 @@ type encodeHandleI interface {
 }
 }
 
 
 type encFnInfo struct {
 type encFnInfo struct {
-	sis   *typeInfo
+	ti   *typeInfo
 	e     *Encoder
 	e     *Encoder
 	ee    encDriver
 	ee    encDriver
-	rt    reflect.Type
-	rtid  uintptr
 	xfFn  func(reflect.Value) ([]byte, error)
 	xfFn  func(reflect.Value) ([]byte, error)
 	xfTag byte 
 	xfTag byte 
 }
 }
@@ -147,16 +145,10 @@ func (o *EncodeOptions) structToArray() bool {
 }
 }
 
 
 func (f *encFnInfo) builtin(rv reflect.Value) {
 func (f *encFnInfo) builtin(rv reflect.Value) {
-	for j, k := int8(0), f.sis.baseIndir; j < k; j++ {
-		rv = rv.Elem()
-	}
-	f.ee.encodeBuiltinType(f.sis.baseId, rv)
+	f.ee.encodeBuiltinType(f.ti.rtid, rv)
 }
 }
 
 
 func (f *encFnInfo) rawExt(rv reflect.Value) {
 func (f *encFnInfo) rawExt(rv reflect.Value) {
-	for j, k := int8(0), f.sis.baseIndir; j < k; j++ {
-		rv = rv.Elem()
-	}
 	re := rv.Interface().(RawExt)
 	re := rv.Interface().(RawExt)
 	if re.Data == nil {
 	if re.Data == nil {
 		f.ee.encodeNil()
 		f.ee.encodeNil()
@@ -171,9 +163,6 @@ func (f *encFnInfo) rawExt(rv reflect.Value) {
 }
 }
 
 
 func (f *encFnInfo) ext(rv reflect.Value) {
 func (f *encFnInfo) ext(rv reflect.Value) {
-	for j, k := int8(0), f.sis.baseIndir; j < k; j++ {
-		rv = rv.Elem()
-	}
 	bs, fnerr := f.xfFn(rv)
 	bs, fnerr := f.xfFn(rv)
 	if fnerr != nil {
 	if fnerr != nil {
 		panic(fnerr)
 		panic(fnerr)
@@ -193,12 +182,16 @@ func (f *encFnInfo) ext(rv reflect.Value) {
 
 
 func (f *encFnInfo) binaryMarshal(rv reflect.Value) {
 func (f *encFnInfo) binaryMarshal(rv reflect.Value) {
 	var bm binaryMarshaler
 	var bm binaryMarshaler
-	if f.sis.mIndir == 0 {
+	if f.ti.mIndir == 0 {
 		bm = rv.Interface().(binaryMarshaler)
 		bm = rv.Interface().(binaryMarshaler)
-	} else if f.sis.mIndir == -1 {
+	} else if f.ti.mIndir == -1 {
 		bm = rv.Addr().Interface().(binaryMarshaler)
 		bm = rv.Addr().Interface().(binaryMarshaler)
 	} else {
 	} else {
-		for j, k := int8(0), f.sis.mIndir; j < k; j++ {
+		for j, k := int8(0), f.ti.mIndir; j < k; j++ {
+			if rv.IsNil() {
+				f.ee.encodeNil()
+				return
+			}
 			rv = rv.Elem()
 			rv = rv.Elem()
 		}
 		}
 		bm = rv.Interface().(binaryMarshaler)
 		bm = rv.Interface().(binaryMarshaler)
@@ -252,7 +245,7 @@ func (f *encFnInfo) kSlice(rv reflect.Value) {
 		f.ee.encodeNil()
 		f.ee.encodeNil()
 		return
 		return
 	}
 	}
-	if f.rtid == byteSliceTypId {
+	if f.ti.rtid == byteSliceTypId {
 		f.ee.encodeStringBytes(c_RAW, rv.Bytes())
 		f.ee.encodeStringBytes(c_RAW, rv.Bytes())
 		return
 		return
 	}
 	}
@@ -271,19 +264,21 @@ func (f *encFnInfo) kArray(rv reflect.Value) {
 }
 }
 
 
 func (f *encFnInfo) kStruct(rv reflect.Value) {
 func (f *encFnInfo) kStruct(rv reflect.Value) {
-	newlen := len(f.sis.sis)
+	// debugf(">>>> CALLING kStruct: %T, %v", rv.Interface(), rv.Interface())
+	fti := f.ti
+	newlen := len(fti.sfi)
 	rvals := make([]reflect.Value, newlen)
 	rvals := make([]reflect.Value, newlen)
 	var encnames []string
 	var encnames []string
 	e := f.e
 	e := f.e
-	sissis := f.sis.sisp
-	toMap := !(f.sis.toArray || e.h.structToArray())
+	tisfi := fti.sfip
+	toMap := !(fti.toArray || e.h.structToArray())
 	// if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct)
 	// if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct)
 	if toMap {
 	if toMap {
-		sissis = f.sis.sis
+		tisfi = fti.sfi
 		encnames = make([]string, newlen)
 		encnames = make([]string, newlen)
 	}
 	}
 	newlen = 0
 	newlen = 0
-	for _, si := range sissis {
+	for _, si := range tisfi {
 		if si.i != -1 {
 		if si.i != -1 {
 			rvals[newlen] = rv.Field(int(si.i))
 			rvals[newlen] = rv.Field(int(si.i))
 		} else {
 		} else {
@@ -302,6 +297,7 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
 		newlen++
 		newlen++
 	}
 	}
 
 
+	// debugf(">>>> kStruct: newlen: %v", newlen)
 	if toMap {
 	if toMap {
 		ee := f.ee //don't dereference everytime
 		ee := f.ee //don't dereference everytime
 		ee.encodeMapPreamble(newlen)
 		ee.encodeMapPreamble(newlen)
@@ -317,13 +313,14 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
 	}
 	}
 }
 }
 
 
-func (f *encFnInfo) kPtr(rv reflect.Value) {
-	if rv.IsNil() {
-		f.ee.encodeNil()
-		return
-	}
-	f.e.encodeValue(rv.Elem())
-}
+// func (f *encFnInfo) kPtr(rv reflect.Value) {
+// 	debugf(">>>>>>> ??? encode kPtr called - shouldn't get called")
+// 	if rv.IsNil() {
+// 		f.ee.encodeNil()
+// 		return
+// 	}
+// 	f.e.encodeValue(rv.Elem())
+// }
 
 
 func (f *encFnInfo) kInterface(rv reflect.Value) {
 func (f *encFnInfo) kInterface(rv reflect.Value) {
 	if rv.IsNil() {
 	if rv.IsNil() {
@@ -343,7 +340,7 @@ func (f *encFnInfo) kMap(rv reflect.Value) {
 	if l == 0 {
 	if l == 0 {
 		return
 		return
 	}
 	}
-	keyTypeIsString := f.rt.Key().Kind() == reflect.String
+	keyTypeIsString := f.ti.rt.Key().Kind() == reflect.String
 	mks := rv.MapKeys()
 	mks := rv.MapKeys()
 	// for j, lmks := 0, len(mks); j < lmks; j++ {
 	// for j, lmks := 0, len(mks); j < lmks; j++ {
 	for j := range mks {
 	for j := range mks {
@@ -526,12 +523,18 @@ func (e *Encoder) encode(iv interface{}) {
 }
 }
 
 
 func (e *Encoder) encodeValue(rv reflect.Value) {
 func (e *Encoder) encodeValue(rv reflect.Value) {
+	for rv.Kind() == reflect.Ptr {
+		if rv.IsNil() {
+			e.e.encodeNil()
+			return
+		}
+		rv = rv.Elem()
+	}
+	
 	rt := rv.Type()
 	rt := rv.Type()
 	rtid := reflect.ValueOf(rt).Pointer()
 	rtid := reflect.ValueOf(rt).Pointer()
 		
 		
-	// if e.f == nil && e.s == nil {
-	// 	debugf("---->Creating new enc f map for type: %v\n", rt)
-	// }
+	// if e.f == nil && e.s == nil { debugf("---->Creating new enc f map for type: %v\n", rt) }
 	var fn encFn 
 	var fn encFn 
 	var ok bool
 	var ok bool
 	if useMapForCodecCache {
 	if useMapForCodecCache {
@@ -546,46 +549,47 @@ func (e *Encoder) encodeValue(rv reflect.Value) {
 	}
 	}
 	if !ok {
 	if !ok {
 		// debugf("\tCreating new enc fn for type: %v\n", rt)
 		// debugf("\tCreating new enc fn for type: %v\n", rt)
-		fi := encFnInfo { sis:getTypeInfo(rtid, rt), e:e, ee:e.e, rt:rt, rtid:rtid }
-		if fi.sis.baseId == rawExtTypId {
-			fn = encFn{ &fi, (*encFnInfo).rawExt }
-		} else if e.e.isBuiltinType(fi.sis.baseId) {
-			fn = encFn{ &fi, (*encFnInfo).builtin }
-		} else if xfTag, xfFn := e.h.getEncodeExt(fi.sis.baseId); xfFn != nil {
+		fi := encFnInfo { ti:getTypeInfo(rtid, rt), e:e, ee:e.e }
+		fn.i = &fi 
+		if rtid == rawExtTypId {
+			fn.f = (*encFnInfo).rawExt 
+		} else if e.e.isBuiltinType(rtid) {
+			fn.f = (*encFnInfo).builtin 
+		} else if xfTag, xfFn := e.h.getEncodeExt(rtid); xfFn != nil {
 			fi.xfTag, fi.xfFn = xfTag, xfFn
 			fi.xfTag, fi.xfFn = xfTag, xfFn
-			fn = encFn{ &fi, (*encFnInfo).ext }
-		} else if supportBinaryMarshal && fi.sis.m {
-			fn = encFn{ &fi, (*encFnInfo).binaryMarshal }
+			fn.f = (*encFnInfo).ext 
+		} else if supportBinaryMarshal && fi.ti.m {
+			fn.f = (*encFnInfo).binaryMarshal 
 		} else {
 		} else {
 			switch rk := rt.Kind(); rk {
 			switch rk := rt.Kind(); rk {
 			case reflect.Bool:
 			case reflect.Bool:
-				fn = encFn{ &fi, (*encFnInfo).kBool }
+				fn.f = (*encFnInfo).kBool 
 			case reflect.String:
 			case reflect.String:
-				fn = encFn{ &fi, (*encFnInfo).kString }
+				fn.f = (*encFnInfo).kString 
 			case reflect.Float64:
 			case reflect.Float64:
-				fn = encFn{ &fi, (*encFnInfo).kFloat64 }
+				fn.f = (*encFnInfo).kFloat64 
 			case reflect.Float32:
 			case reflect.Float32:
-				fn = encFn{ &fi, (*encFnInfo).kFloat32 }
+				fn.f = (*encFnInfo).kFloat32 
 			case reflect.Int, reflect.Int8, reflect.Int64, reflect.Int32, reflect.Int16:
 			case reflect.Int, reflect.Int8, reflect.Int64, reflect.Int32, reflect.Int16:
-				fn = encFn{ &fi, (*encFnInfo).kInt }
+				fn.f = (*encFnInfo).kInt 
 			case reflect.Uint8, reflect.Uint64, reflect.Uint, reflect.Uint32, reflect.Uint16:
 			case reflect.Uint8, reflect.Uint64, reflect.Uint, reflect.Uint32, reflect.Uint16:
-				fn = encFn{ &fi, (*encFnInfo).kUint }
+				fn.f = (*encFnInfo).kUint 
 			case reflect.Invalid:
 			case reflect.Invalid:
-				fn = encFn{ &fi, (*encFnInfo).kInvalid }
+				fn.f = (*encFnInfo).kInvalid 
 			case reflect.Slice:
 			case reflect.Slice:
-				fn = encFn{ &fi, (*encFnInfo).kSlice }
+				fn.f = (*encFnInfo).kSlice 
 			case reflect.Array:
 			case reflect.Array:
-				fn = encFn{ &fi, (*encFnInfo).kArray }
+				fn.f = (*encFnInfo).kArray 
 			case reflect.Struct:
 			case reflect.Struct:
-				fn = encFn{ &fi, (*encFnInfo).kStruct }
-			case reflect.Ptr:
-				fn = encFn{ &fi, (*encFnInfo).kPtr }
+				fn.f = (*encFnInfo).kStruct 
+			// case reflect.Ptr:
+			// 	fn.f = (*encFnInfo).kPtr 
 			case reflect.Interface:
 			case reflect.Interface:
-				fn = encFn{ &fi, (*encFnInfo).kInterface }
+				fn.f = (*encFnInfo).kInterface 
 			case reflect.Map:
 			case reflect.Map:
-				fn = encFn{ &fi, (*encFnInfo).kMap }
+				fn.f = (*encFnInfo).kMap 
 			default:
 			default:
-				fn = encFn{ &fi, (*encFnInfo).kErr }
+				fn.f = (*encFnInfo).kErr 
 			}
 			}
 		}
 		}
 		if useMapForCodecCache {
 		if useMapForCodecCache {

+ 79 - 61
codec/helper.go

@@ -7,7 +7,6 @@ package codec
 
 
 import (
 import (
 	"encoding/binary"
 	"encoding/binary"
-	"errors"
 	"fmt"
 	"fmt"
 	"reflect"
 	"reflect"
 	"sort"
 	"sort"
@@ -131,11 +130,13 @@ type extTypeTagFn struct {
 	decFn func(reflect.Value, []byte) error
 	decFn func(reflect.Value, []byte) error
 }
 }
 
 
-type extHandle map[uintptr]*extTypeTagFn
+type extHandle []*extTypeTagFn
 
 
 // AddExt registers an encode and decode function for a reflect.Type.
 // AddExt registers an encode and decode function for a reflect.Type.
 // Note that the type must be a named type, and specifically not 
 // 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.
 // a pointer or Interface. An error is returned if that is not honored.
+// 
+// To Deregister an ext, call AddExt with 0 tag, nil encfn and nil decfn.
 func (o *extHandle) AddExt(
 func (o *extHandle) AddExt(
 	rt reflect.Type,
 	rt reflect.Type,
 	tag byte,
 	tag byte,
@@ -144,29 +145,37 @@ func (o *extHandle) AddExt(
 ) (err error) {
 ) (err error) {
 	// o is a pointer, because we may need to initialize it
 	// o is a pointer, because we may need to initialize it
 	if rt.PkgPath() == "" || rt.Kind() == reflect.Interface {
 	if rt.PkgPath() == "" || rt.Kind() == reflect.Interface {
-		err = fmt.Errorf("codec.Handle.AddExt: Takes a named type, especially not a pointer or interface: %T", 
+		err = fmt.Errorf("codec.Handle.AddExt: Takes named type, especially not a pointer or interface: %T", 
 			reflect.Zero(rt).Interface())
 			reflect.Zero(rt).Interface())
 		return
 		return
 	}
 	}
-	if o == nil {
-		err = errors.New("codec.Handle.AddExt: Nil (should never happen)")
-		return
-	}
+	
+	// o cannot be nil, since it is always embedded in a Handle. 
+	// if nil, let it panic.
+	// if o == nil {
+	// 	err = errors.New("codec.Handle.AddExt: extHandle cannot be a nil pointer.")
+	// 	return
+	// }
+	
 	rtid := reflect.ValueOf(rt).Pointer()
 	rtid := reflect.ValueOf(rt).Pointer()
-	if *o == nil {
-		*o = make(map[uintptr]*extTypeTagFn, 4)
-	}
-	m := *o
-	if encfn == nil || decfn == nil {
-		delete(m, rtid)
-	} else {
-		m[rtid] = &extTypeTagFn { rtid, rt, tag, encfn, decfn }
+	for _, v := range *o {
+		if v.rtid == rtid {
+			v.tag, v.encFn, v.decFn = tag, encfn, decfn
+			return
+		}
 	}
 	}
+	
+	*o = append(*o, &extTypeTagFn { rtid, rt, tag, encfn, decfn })
 	return
 	return
 }
 }
 
 
 func (o extHandle) getExt(rtid uintptr) *extTypeTagFn {
 func (o extHandle) getExt(rtid uintptr) *extTypeTagFn {
-	return o[rtid]
+	for _, v := range o {
+		if v.rtid == rtid {
+			return v
+		}
+	}
+	return nil
 }
 }
 
 
 func (o extHandle) getExtForTag(tag byte) *extTypeTagFn {
 func (o extHandle) getExtForTag(tag byte) *extTypeTagFn {
@@ -189,7 +198,7 @@ func (o extHandle) getDecodeExtForTag(tag byte) (
 }
 }
 
 
 func (o extHandle) getDecodeExt(rtid uintptr) (tag byte, fn func(reflect.Value, []byte) error) {
 func (o extHandle) getDecodeExt(rtid uintptr) (tag byte, fn func(reflect.Value, []byte) error) {
-	if x, ok := o[rtid]; ok {
+	if x := o.getExt(rtid); x != nil {
 		tag = x.tag
 		tag = x.tag
 		fn = x.decFn
 		fn = x.decFn
 	}
 	}
@@ -197,7 +206,7 @@ func (o extHandle) getDecodeExt(rtid uintptr) (tag byte, fn func(reflect.Value,
 }
 }
 
 
 func (o extHandle) getEncodeExt(rtid uintptr) (tag byte, fn func(reflect.Value) ([]byte, error)) {
 func (o extHandle) getEncodeExt(rtid uintptr) (tag byte, fn func(reflect.Value) ([]byte, error)) {
-	if x, ok := o[rtid]; ok {
+	if x := o.getExt(rtid); x != nil {
 		tag = x.tag
 		tag = x.tag
 		fn = x.encFn
 		fn = x.encFn
 	}
 	}
@@ -212,14 +221,18 @@ func (o extHandle) getEncodeExt(rtid uintptr) (tag byte, fn func(reflect.Value)
 //   - If type is binary(M/Unm)arshaler, call Binary(M/Unm)arshal method
 //   - If type is binary(M/Unm)arshaler, call Binary(M/Unm)arshal method
 //   - Else decode appropriately based on the reflect.Kind
 //   - Else decode appropriately based on the reflect.Kind
 type typeInfo struct {
 type typeInfo struct {
-	sis       []*structFieldInfo // sorted. Used when enc/dec struct to map.
-	sisp      []*structFieldInfo // unsorted. Used when enc/dec struct to array.
-	// base      reflect.Type
+	sfi       []*structFieldInfo // sorted. Used when enc/dec struct to map.
+	sfip      []*structFieldInfo // unsorted. Used when enc/dec struct to array.
+	
+	rt        reflect.Type
+	rtid      uintptr
 	
 	
-	// baseId is the pointer to the base reflect.Type, after deferencing
+	// baseId gives pointer to the base reflect.Type, after deferencing
 	// the pointers. E.g. base type of ***time.Time is time.Time.
 	// the pointers. E.g. base type of ***time.Time is time.Time.
+	base      reflect.Type
 	baseId    uintptr
 	baseId    uintptr
 	baseIndir int8 // number of indirections to get to base
 	baseIndir int8 // number of indirections to get to base
+	
 	m         bool // base type (T or *T) is a binaryMarshaler
 	m         bool // base type (T or *T) is a binaryMarshaler
 	unm       bool // base type (T or *T) is a binaryUnmarshaler
 	unm       bool // base type (T or *T) is a binaryUnmarshaler
 	mIndir    int8 // number of indirections to get to binaryMarshaler type
 	mIndir    int8 // number of indirections to get to binaryMarshaler type
@@ -249,43 +262,43 @@ func (p sfiSortedByEncName) Len() int           { return len(p) }
 func (p sfiSortedByEncName) Less(i, j int) bool { return p[i].encName < p[j].encName }
 func (p sfiSortedByEncName) Less(i, j int) bool { return p[i].encName < p[j].encName }
 func (p sfiSortedByEncName) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 func (p sfiSortedByEncName) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
 
-func (sis *typeInfo) indexForEncName(name string) int {
-	//sissis := sis.sis 
-	if sislen := len(sis.sis); sislen < binarySearchThreshold {
+func (ti *typeInfo) indexForEncName(name string) int {
+	//tisfi := ti.sfi 
+	if sfilen := len(ti.sfi); sfilen < binarySearchThreshold {
 		// linear search. faster than binary search in my testing up to 16-field structs.
 		// linear search. faster than binary search in my testing up to 16-field structs.
-		// for i := 0; i < sislen; i++ {
-		// 	if sis.sis[i].encName == name {
+		// for i := 0; i < sfilen; i++ {
+		// 	if ti.sfi[i].encName == name {
 		// 		return i
 		// 		return i
 		// 	}
 		// 	}
 		// }
 		// }
-		for i, si := range sis.sis {
+		for i, si := range ti.sfi {
 			if si.encName == name {
 			if si.encName == name {
 				return i
 				return i
 			}
 			}
 		}
 		}
 	} else {
 	} else {
 		// binary search. adapted from sort/search.go.
 		// binary search. adapted from sort/search.go.
-		h, i, j := 0, 0, sislen
+		h, i, j := 0, 0, sfilen
 		for i < j {
 		for i < j {
 			h = i + (j-i)/2
 			h = i + (j-i)/2
 			// i ≤ h < j
 			// i ≤ h < j
-			if sis.sis[h].encName < name {
+			if ti.sfi[h].encName < name {
 				i = h + 1 // preserves f(i-1) == false
 				i = h + 1 // preserves f(i-1) == false
 			} else {
 			} else {
 				j = h // preserves f(j) == true
 				j = h // preserves f(j) == true
 			}
 			}
 		}
 		}
-		if i < sislen && sis.sis[i].encName == name {
+		if i < sfilen && ti.sfi[i].encName == name {
 			return i
 			return i
 		}
 		}
 	}
 	}
 	return -1
 	return -1
 }
 }
 
 
-func getTypeInfo(rtid uintptr, rt reflect.Type) (sis *typeInfo) {
+func getTypeInfo(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 	var ok bool
 	var ok bool
 	cachedTypeInfoMutex.RLock()
 	cachedTypeInfoMutex.RLock()
-	sis, ok = cachedTypeInfo[rtid]
+	pti, ok = cachedTypeInfo[rtid]
 	cachedTypeInfoMutex.RUnlock()
 	cachedTypeInfoMutex.RUnlock()
 	if ok {
 	if ok {
 		return
 		return
@@ -293,66 +306,71 @@ func getTypeInfo(rtid uintptr, rt reflect.Type) (sis *typeInfo) {
 
 
 	cachedTypeInfoMutex.Lock()
 	cachedTypeInfoMutex.Lock()
 	defer cachedTypeInfoMutex.Unlock()
 	defer cachedTypeInfoMutex.Unlock()
-	if sis, ok = cachedTypeInfo[rtid]; ok {
+	if pti, ok = cachedTypeInfo[rtid]; ok {
 		return
 		return
 	}
 	}
 
 
-	sis = new(typeInfo)
+	ti := typeInfo { rt: rt, rtid: rtid }
+	pti = &ti
 	
 	
 	var indir int8
 	var indir int8
 	if ok, indir = implementsIntf(rt, binaryMarshalerTyp); ok {
 	if ok, indir = implementsIntf(rt, binaryMarshalerTyp); ok {
-		sis.m, sis.mIndir = true, indir
+		ti.m, ti.mIndir = true, indir
 	}
 	}
 	if ok, indir = implementsIntf(rt, binaryUnmarshalerTyp); ok {
 	if ok, indir = implementsIntf(rt, binaryUnmarshalerTyp); ok {
-		sis.unm, sis.unmIndir = true, indir
+		ti.unm, ti.unmIndir = true, indir
 	}
 	}
 	
 	
 	pt := rt
 	pt := rt
 	var ptIndir int8 
 	var ptIndir int8 
-	for ; pt.Kind() == reflect.Ptr; pt, ptIndir = pt.Elem(), ptIndir+1 { }
+	// for ; pt.Kind() == reflect.Ptr; pt, ptIndir = pt.Elem(), ptIndir+1 { }
+	for pt.Kind() == reflect.Ptr {
+		pt = pt.Elem()
+		ptIndir++
+	}
 	if ptIndir == 0 {
 	if ptIndir == 0 {
-		//sis.base = rt 
-		sis.baseId = rtid
+		ti.base = rt 
+		ti.baseId = rtid
 	} else {
 	} else {
-		//sis.base = pt 
-		sis.baseId = reflect.ValueOf(pt).Pointer()
-		sis.baseIndir = ptIndir
+		ti.base = pt 
+		ti.baseId = reflect.ValueOf(pt).Pointer()
+		ti.baseIndir = ptIndir
 	}
 	}
 	
 	
 	if rt.Kind() == reflect.Struct {
 	if rt.Kind() == reflect.Struct {
 		var siInfo *structFieldInfo
 		var siInfo *structFieldInfo
 		if f, ok := rt.FieldByName(structInfoFieldName); ok {
 		if f, ok := rt.FieldByName(structInfoFieldName); ok {
 			siInfo = parseStructFieldInfo(structInfoFieldName, f.Tag.Get(structTagName))
 			siInfo = parseStructFieldInfo(structInfoFieldName, f.Tag.Get(structTagName))
-			sis.toArray = siInfo.toArray
+			ti.toArray = siInfo.toArray
 		}
 		}
-		sisp := make([]*structFieldInfo, 0, rt.NumField())
-		rgetTypeInfo(rt, nil, make(map[string]bool), &sisp, siInfo)
+		sfip := make([]*structFieldInfo, 0, rt.NumField())
+		rgetTypeInfo(rt, nil, make(map[string]bool), &sfip, siInfo)
 
 
 		// // try to put all si close together
 		// // try to put all si close together
 		// const tryToPutAllStructFieldInfoTogether = true
 		// const tryToPutAllStructFieldInfoTogether = true
 		// if tryToPutAllStructFieldInfoTogether {
 		// if tryToPutAllStructFieldInfoTogether {
-		// 	sisp2 := make([]structFieldInfo, len(sisp))
-		// 	for i, si := range sisp {
-		// 		sisp2[i] = *si
+		// 	sfip2 := make([]structFieldInfo, len(sfip))
+		// 	for i, si := range sfip {
+		// 		sfip2[i] = *si
 		// 	}
 		// 	}
-		// 	for i := range sisp {
-		// 		sisp[i] = &sisp2[i]
+		// 	for i := range sfip {
+		// 		sfip[i] = &sfip2[i]
 		// 	}
 		// 	}
 		// }
 		// }
 		
 		
-		sis.sisp = make([]*structFieldInfo, len(sisp))
-		sis.sis = make([]*structFieldInfo, len(sisp))
-		copy(sis.sisp, sisp)
-		sort.Sort(sfiSortedByEncName(sisp))
-		copy(sis.sis, sisp)
+		ti.sfip = make([]*structFieldInfo, len(sfip))
+		ti.sfi = make([]*structFieldInfo, len(sfip))
+		copy(ti.sfip, sfip)
+		sort.Sort(sfiSortedByEncName(sfip))
+		copy(ti.sfi, sfip)
 	}
 	}
-	// sis = sisp
-	cachedTypeInfo[rtid] = sis
+	// sfi = sfip
+	cachedTypeInfo[rtid] = pti
 	return
 	return
 }
 }
 
 
 func rgetTypeInfo(rt reflect.Type, indexstack []int, fnameToHastag map[string]bool,
 func rgetTypeInfo(rt reflect.Type, indexstack []int, fnameToHastag map[string]bool,
-	sis *[]*structFieldInfo, siInfo *structFieldInfo,
+	sfi *[]*structFieldInfo, siInfo *structFieldInfo,
 ) {
 ) {
 	for j := 0; j < rt.NumField(); j++ {
 	for j := 0; j < rt.NumField(); j++ {
 		f := rt.Field(j)
 		f := rt.Field(j)
@@ -367,7 +385,7 @@ func rgetTypeInfo(rt reflect.Type, indexstack []int, fnameToHastag map[string]bo
 			//if anonymous, inline it if there is no struct tag, else treat as regular field
 			//if anonymous, inline it if there is no struct tag, else treat as regular field
 			if stag == "" {
 			if stag == "" {
 				indexstack2 := append(append([]int(nil), indexstack...), j)
 				indexstack2 := append(append([]int(nil), indexstack...), j)
-				rgetTypeInfo(f.Type, indexstack2, fnameToHastag, sis, siInfo)
+				rgetTypeInfo(f.Type, indexstack2, fnameToHastag, sfi, siInfo)
 				continue
 				continue
 			}
 			}
 		}
 		}
@@ -391,7 +409,7 @@ func rgetTypeInfo(rt reflect.Type, indexstack []int, fnameToHastag map[string]bo
 				si.omitEmpty = true
 				si.omitEmpty = true
 			}
 			}
 		}
 		}
-		*sis = append(*sis, si)
+		*sfi = append(*sfi, si)
 		fnameToHastag[f.Name] = stag != ""
 		fnameToHastag[f.Name] = stag != ""
 	}
 	}
 }
 }