Browse Source

codec: multiple clean-ups especially on optimizing extension support and removing TODOs

First, add i2rtid(interface{}) functions for getting rtid given an interface{}

gen-helper: support extensions in a way to minimize unnecessary allocations:
    Provide methods to
    - grab the rtid from an interface{}
      ensure it is inlineable in unsafe mode
    - get Extension for a given rtid
    - encode a value given its extension

During codecgen, we will use these methods to smartly encode/decode an extension
while minimizing allocations:
    change from
        else if z.HasExtensions() && z.EncExt(v) {
    TO
        else if x := z.Extension(z.I2Rtid(v)); x != nil { z.EncExtension(x, v)

    This way, we only call EncExtension iff an extension exists, and we
    depend on the fact that I2Rtid is inlineable in unsafe mode, so
    v will typically not allocate.

    Do something similar for DecExt

Fix SetExt bugs:
- First, passing an ptr rtid to getExt should return same extension.
  To do this, we store both the base rtid and rtid to a ptr to the base.
- Also, fix SetExt for when we are replacing the ext.
- Finally, do not allow SetExt on natively supported types (Raw, RawExt, time.Time).

test: add slice and array of custom struct type to TestStruc
- This ensures that we test (during codecgen and non-codecgen mode) that we can handle
  a slice or array of custom type.

Finally, remove some unnecessary TODO's
Ugorji Nwoke 8 years ago
parent
commit
84cb69a8af

+ 0 - 1
codec/codec_test.go

@@ -1674,7 +1674,6 @@ func doTestSwallowAndZero(t *testing.T, h Handle) {
 
 func doTestRawExt(t *testing.T, h Handle) {
 	testOnce.Do(testInitAll)
-	// return // TODO: need to fix this ...
 	var b []byte
 	var v RawExt // interface{}
 	_, isJson := h.(*JsonHandle)

+ 1 - 1
codec/decode.go

@@ -2084,7 +2084,7 @@ func (d *Decoder) decode(iv interface{}) {
 		*v = d.rawBytes()
 
 	case *interface{}:
-		d.decodeValue(reflect.ValueOf(iv).Elem(), nil, true) // TODO: consider recognize here
+		d.decodeValue(reflect.ValueOf(iv).Elem(), nil, true)
 		// d.decodeValueNotNil(reflect.ValueOf(iv).Elem())
 
 	default:

+ 1 - 0
codec/encode.go

@@ -51,6 +51,7 @@ type encDriver interface {
 
 	// Deprecated: left here for now so that old codecgen'ed filed will work. TODO: remove.
 	EncodeBuiltin(rt uintptr, v interface{})
+
 	EncodeNil()
 	EncodeInt(i int64)
 	EncodeUint(i uint64)

+ 32 - 9
codec/gen-helper.generated.go

@@ -110,6 +110,21 @@ 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)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) Extension(rtid uintptr) (xfn *extTypeTagFn) {
+	return f.e.h.getExt(rtid)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncExtension(v interface{}, xfFn *extTypeTagFn) {
+	f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
+}
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) HasExtensions() bool {
 	return len(f.e.h.extHandle) != 0
@@ -117,12 +132,7 @@ func (f genHelperEncoder) HasExtensions() bool {
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
-	rt := reflect.TypeOf(v)
-	if rt.Kind() == reflect.Ptr {
-		rt = rt.Elem()
-	}
-	rtid := rt2id(rt)
-	xfFn := f.e.h.getExt(rtid)
+	xfFn := f.e.h.getExt(i2rtid(v))
 	if xfFn != nil {
 		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 		return true
@@ -224,6 +234,21 @@ func (f genHelperDecoder) IsJSONHandle() bool {
 	return f.d.js
 }
 
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) I2Rtid(v interface{}) uintptr {
+	return i2rtid(v)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) Extension(rtid uintptr) (xfn *extTypeTagFn) {
+	return f.d.h.getExt(rtid)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecExtension(v interface{}, xfFn *extTypeTagFn) {
+	f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
+}
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) HasExtensions() bool {
 	return len(f.d.h.extHandle) != 0
@@ -231,9 +256,7 @@ func (f genHelperDecoder) HasExtensions() bool {
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
-	rt := reflect.TypeOf(v).Elem()
-	rtid := rt2id(rt)
-	xfFn := f.d.h.getExt(rtid)
+	xfFn := f.d.h.getExt(i2rtid(v))
 	if xfFn != nil {
 		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
 		return true

+ 26 - 10
codec/gen-helper.go.tmpl

@@ -59,7 +59,6 @@ type genHelperDecoder struct {
 func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
 	return f.e.h
 }
-
 // 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()
@@ -103,17 +102,24 @@ 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)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) Extension(rtid uintptr) (xfn *extTypeTagFn) {
+	return f.e.h.getExt(rtid)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncExtension(v interface{}, xfFn *extTypeTagFn) {
+	f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) HasExtensions() bool {
 	return len(f.e.h.extHandle) != 0
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
-	rt := reflect.TypeOf(v)
-	if rt.Kind() == reflect.Ptr {
-		rt = rt.Elem()
-	}
-	rtid := rt2id(rt)
-	xfFn := f.e.h.getExt(rtid)
+	xfFn := f.e.h.getExt(i2rtid(v))
 	if xfFn != nil {
 		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 		return true
@@ -202,14 +208,24 @@ func (f genHelperDecoder) IsJSONHandle() bool {
 	return f.d.js 
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) I2Rtid(v interface{}) uintptr {
+	return i2rtid(v)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) Extension(rtid uintptr) (xfn *extTypeTagFn) {
+	return f.d.h.getExt(rtid)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecExtension(v interface{}, xfFn *extTypeTagFn) {
+	f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) HasExtensions() bool {
 	return len(f.d.h.extHandle) != 0
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
-	rt := reflect.TypeOf(v).Elem()
-	rtid := rt2id(rt)
-	xfFn := f.d.h.getExt(rtid)
+	xfFn := f.d.h.getExt(i2rtid(v))
 	if xfFn != nil {
 		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
 		return true

+ 25 - 9
codec/gen.go

@@ -254,12 +254,13 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, noExtensions bool,
 	x.linef("// ----- value types used ----")
 	x.linef("codecSelferValueTypeArray%s = %v", x.xs, int64(valueTypeArray))
 	x.linef("codecSelferValueTypeMap%s = %v", x.xs, int64(valueTypeMap))
-	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))
+	// 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.line(")")
 	x.line("var (")
 	x.line("codecSelferBitsize" + x.xs + " = uint8(reflect.TypeOf(uint(0)).Bits())")
@@ -715,11 +716,24 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
 	// 	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)
+		// 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)
 	}
-	if tk == reflect.Array || tk == reflect.Struct { // varname is of type *T
+	if arrayOrStruct { // varname is of type *T
 		if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) {
 			x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname)
 		}
@@ -1171,7 +1185,9 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 	// 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
-		x.linef("} else if z.HasExtensions() && z.DecExt(%s) {", varname)
+		// x.linef("} else if z.HasExtensions() && z.DecExt(%s) {", varname)
+		yy := fmt.Sprintf("%sxt%s", genTempVarPfx, mi)
+		x.linef("} else if %s := z.Extension(z.I2Rtid(%s)); %s != nil { z.DecExtension(%s, %s) ", yy, varname, yy, varname, yy)
 	}
 
 	if t.Implements(binaryUnmarshalerTyp) || tptr.Implements(binaryUnmarshalerTyp) {

+ 32 - 18
codec/helper.go

@@ -590,10 +590,11 @@ func (z bigenHelper) writeUint64(v uint64) {
 }
 
 type extTypeTagFn struct {
-	rtid uintptr
-	rt   reflect.Type
-	tag  uint64
-	ext  Ext
+	rtid    uintptr
+	rtidptr uintptr
+	rt      reflect.Type
+	tag     uint64
+	ext     Ext
 }
 
 type extHandle []extTypeTagFn
@@ -620,24 +621,37 @@ func (o *extHandle) AddExt(
 // Deprecated: Use SetBytesExt or SetInterfaceExt on the Handle instead.
 func (o *extHandle) SetExt(rt reflect.Type, tag uint64, ext Ext) (err error) {
 	// o is a pointer, because we may need to initialize it
-	if rt.PkgPath() == "" || rt.Kind() == reflect.Interface {
-		err = fmt.Errorf("codec.Handle.AddExt: Takes named type, not a pointer or interface: %T",
-			reflect.Zero(rt).Interface())
-		return
+	rk := rt.Kind()
+	for rk == reflect.Ptr {
+		rt = rt.Elem()
+		rk = rt.Kind()
 	}
 
-	rtid := rt2id(rt)
-	for _, v := range *o {
-		if v.rtid == rtid {
-			v.tag, v.ext = tag, ext
-			return
-		}
+	if rt.PkgPath() == "" || rk == reflect.Interface { // || rk == reflect.Ptr {
+		return fmt.Errorf("codec.Handle.SetExt: Takes named type, not a pointer or interface: %v", rt)
 	}
 
-	if *o == nil {
-		*o = make([]extTypeTagFn, 0, 4)
+	rtid := rt2id(rt)
+	switch rtid {
+	case timeTypId, rawTypId, rawExtTypId:
+		// all natively supported type, so cannot have an extension
+		return // TODO: should we silently ignore, or return an error???
+	}
+	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
+			}
+		}
 	}
-	*o = append(*o, extTypeTagFn{rtid, rt, tag, ext})
+	rtidptr := rt2id(reflect.PtrTo(rt))
+	*o = append(o2, extTypeTagFn{rtid, rtidptr, rt, tag, ext})
 	return
 }
 
@@ -645,7 +659,7 @@ func (o extHandle) getExt(rtid uintptr) *extTypeTagFn {
 	var v *extTypeTagFn
 	for i := range o {
 		v = &o[i]
-		if v.rtid == rtid {
+		if v.rtid == rtid || v.rtidptr == rtid {
 			return v
 		}
 	}

+ 4 - 0
codec/helper_not_unsafe.go

@@ -70,6 +70,10 @@ func rv2rtid(rv reflect.Value) uintptr {
 	return reflect.ValueOf(rv.Type()).Pointer()
 }
 
+func i2rtid(i interface{}) uintptr {
+	return reflect.ValueOf(reflect.TypeOf(i)).Pointer()
+}
+
 // --------------------------
 // type ptrToRvMap struct{}
 

+ 5 - 2
codec/helper_unsafe.go

@@ -100,8 +100,7 @@ func definitelyNil(v interface{}) bool {
 // TODO: consider a more generally-known optimization for reflect.Value ==> Interface
 //
 // Currently, we use this fragile method that taps into implememtation details from
-// the source go stdlib reflect/value.go,
-// and trims the implementation.
+// the source go stdlib reflect/value.go, and trims the implementation.
 func rv2i(rv reflect.Value) interface{} {
 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
 	// true references (map, func, chan, ptr - NOT slice) may be double-referenced as flagIndir
@@ -126,6 +125,10 @@ func rv2rtid(rv reflect.Value) uintptr {
 	return uintptr((*unsafeReflectValue)(unsafe.Pointer(&rv)).typ)
 }
 
+func i2rtid(i interface{}) uintptr {
+	return uintptr(((*unsafeIntf)(unsafe.Pointer(&i))).typ)
+}
+
 // func rv0t(rt reflect.Type) reflect.Value {
 // 	ut := (*unsafeIntf)(unsafe.Pointer(&rt))
 // 	// we need to determine whether ifaceIndir, and then whether to just pass 0 as the ptr

+ 24 - 18
codec/mammoth2_codecgen_generated_test.go

@@ -18,12 +18,6 @@ const (
 	// ----- value types used ----
 	codecSelferValueTypeArray19781 = 10
 	codecSelferValueTypeMap19781   = 9
-	// ----- containerStateValues ----
-	codecSelferKcontainerMapKey19781    = 2
-	codecSelferKcontainerMapValue19781  = 3
-	codecSelferKcontainerMapEnd19781    = 4
-	codecSelferKcontainerArrayElem19781 = 6
-	codecSelferKcontainerArrayEnd19781  = 7
 )
 
 var (
@@ -54,7 +48,8 @@ func (x *TestMammoth2) CodecEncodeSelf(e *Encoder) {
 		yym1 := z.EncBinary()
 		_ = yym1
 		if false {
-		} else if z.HasExtensions() && z.EncExt(x) {
+		} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+			z.EncExtension(x, yyxt1)
 		} else {
 			yysep2 := !z.EncBinary()
 			yy2arr2 := z.EncBasicHandle().StructToArray
@@ -20409,7 +20404,8 @@ func (x *TestMammoth2) CodecDecodeSelf(d *Decoder) {
 	yym1 := z.DecBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.DecExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
 	} else {
 		yyct2 := r.ContainerType()
 		if yyct2 == codecSelferValueTypeMap19781 {
@@ -44107,7 +44103,8 @@ func (x testMammoth2Binary) CodecEncodeSelf(e *Encoder) {
 	yym1 := z.EncBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.EncExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.EncExtension(x, yyxt1)
 	} else if yym1 {
 		z.EncBinaryMarshal(x)
 	} else {
@@ -44122,7 +44119,8 @@ func (x *testMammoth2Binary) CodecDecodeSelf(d *Decoder) {
 	yym1 := z.DecBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.DecExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
 	} else if yym1 {
 		z.DecBinaryUnmarshal(x)
 	} else {
@@ -44137,7 +44135,8 @@ func (x testMammoth2Text) CodecEncodeSelf(e *Encoder) {
 	yym1 := z.EncBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.EncExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.EncExtension(x, yyxt1)
 	} else if !yym1 {
 		z.EncTextMarshal(x)
 	} else {
@@ -44152,7 +44151,8 @@ func (x *testMammoth2Text) CodecDecodeSelf(d *Decoder) {
 	yym1 := z.DecBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.DecExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
 	} else if !yym1 {
 		z.DecTextUnmarshal(x)
 	} else {
@@ -44167,7 +44167,8 @@ func (x testMammoth2Json) CodecEncodeSelf(e *Encoder) {
 	yym1 := z.EncBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.EncExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.EncExtension(x, yyxt1)
 	} else if !yym1 && z.IsJSONHandle() {
 		z.EncJSONMarshal(x)
 	} else {
@@ -44182,7 +44183,8 @@ func (x *testMammoth2Json) CodecDecodeSelf(d *Decoder) {
 	yym1 := z.DecBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.DecExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
 	} else if !yym1 && z.IsJSONHandle() {
 		z.DecJSONUnmarshal(x)
 	} else {
@@ -44200,7 +44202,8 @@ func (x *testMammoth2Basic) CodecEncodeSelf(e *Encoder) {
 		yym1 := z.EncBinary()
 		_ = yym1
 		if false {
-		} else if z.HasExtensions() && z.EncExt(x) {
+		} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+			z.EncExtension(x, yyxt1)
 		} else {
 			h.enctestMammoth2Basic((*testMammoth2Basic)(x), e)
 		}
@@ -44214,7 +44217,8 @@ func (x *testMammoth2Basic) CodecDecodeSelf(d *Decoder) {
 	yym1 := z.DecBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.DecExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
 	} else {
 		h.dectestMammoth2Basic((*testMammoth2Basic)(x), d)
 	}
@@ -44230,7 +44234,8 @@ func (x *TestMammoth2Wrapper) CodecEncodeSelf(e *Encoder) {
 		yym1 := z.EncBinary()
 		_ = yym1
 		if false {
-		} else if z.HasExtensions() && z.EncExt(x) {
+		} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+			z.EncExtension(x, yyxt1)
 		} else {
 			yysep2 := !z.EncBinary()
 			yy2arr2 := z.EncBasicHandle().StructToArray
@@ -44381,7 +44386,8 @@ func (x *TestMammoth2Wrapper) CodecDecodeSelf(d *Decoder) {
 	yym1 := z.DecBinary()
 	_ = yym1
 	if false {
-	} else if z.HasExtensions() && z.DecExt(x) {
+	} else if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
 	} else {
 		yyct2 := r.ContainerType()
 		if yyct2 == codecSelferValueTypeMap19781 {

+ 0 - 1
codec/time.go

@@ -151,7 +151,6 @@ func decodeTime(bs []byte) (tt time.Time, err error) {
 		tz = tz & 0x3fff //clear 2 MSBs: dst bits
 	} else { // negative
 		tz = tz | 0xc000 //set 2 MSBs: dst bits
-		//tzname[3] = '-' (TODO: verify. this works here)
 	}
 	tzint := int16(tz)
 	if tzint == 0 {

+ 4 - 1
codec/values_flex_test.go

@@ -91,6 +91,8 @@ type TestStrucFlex struct {
 	Swrapbytes []wrapBytes
 	Swrapuint8 []wrapUint8
 
+	ArrStrUi64T [4]stringUint64T
+
 	Ui64array      [4]uint64
 	Ui64slicearray []*[4]uint64
 
@@ -150,7 +152,8 @@ func newTestStrucFlex(depth, n int, bench, useInterface, useStringKeyOnly bool)
 		Swrapuint8: []wrapUint8{
 			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
 		},
-		Ui64array: [4]uint64{4, 16, 64, 256},
+		Ui64array:   [4]uint64{4, 16, 64, 256},
+		ArrStrUi64T: [4]stringUint64T{{"4", 4}, {"3", 3}, {"2", 2}, {"1", 1}},
 	}
 
 	ts.Ui64slicearray = []*[4]uint64{&ts.Ui64array, &ts.Ui64array}

+ 3 - 6
codec/values_test.go

@@ -89,9 +89,6 @@ type testSimpleFields struct {
 
 	Iptrslice []*int64
 
-	// TODO: test these separately, specifically for reflection and codecgen.
-	// Unfortunately, ffjson doesn't support these. Its compilation even fails.
-
 	WrapSliceInt64  wrapSliceUint64
 	WrapSliceString wrapSliceString
 
@@ -132,9 +129,6 @@ type TestStrucCommon struct {
 
 	Iptrslice []*int64
 
-	// TODO: test these separately, specifically for reflection and codecgen.
-	// Unfortunately, ffjson doesn't support these. Its compilation even fails.
-
 	WrapSliceInt64  wrapSliceUint64
 	WrapSliceString wrapSliceString
 
@@ -142,6 +136,8 @@ type TestStrucCommon struct {
 
 	Simplef testSimpleFields
 
+	SstrUi64T []stringUint64T
+
 	AnonInTestStruc
 
 	NotAnon AnonInTestStruc
@@ -349,6 +345,7 @@ func populateTestStrucCommon(ts *TestStrucCommon, n int, bench, useInterface, us
 			WrapSliceString: []string{strRpt(n, "4"), strRpt(n, "16"), strRpt(n, "64"), strRpt(n, "256")},
 		},
 
+		SstrUi64T:       []stringUint64T{{"1", 1}, {"2", 2}, {"3", 3}, {"4", 4}},
 		AnonInTestStruc: a,
 		NotAnon:         a,
 	}