Browse Source

codec: clean ups

move timeExt used only by tests from time.go into codec_test.go

ensure deprecated/legacy AddExt functionality is tested

re-style how we test extensions, and gen-helper...go

ensure RecursiveEmptyCheck is tested

clarify that some functions in gen-helper...go (e.g. TimeRtidIfBinc, StringView) are no
longer used and only remain there for compatility (so files need not be re-generated).
Ugorji Nwoke 8 years ago
parent
commit
3ae9bb479b
8 changed files with 199 additions and 162 deletions
  1. 85 42
      codec/codec_test.go
  2. 41 32
      codec/gen-helper.generated.go
  3. 41 32
      codec/gen-helper.go.tmpl
  4. 9 15
      codec/helper.go
  5. 1 4
      codec/helper_internal.go
  6. 16 4
      codec/helper_unsafe.go
  7. 2 33
      codec/time.go
  8. 4 0
      codec/z_all_test.go

+ 85 - 42
codec/codec_test.go

@@ -94,6 +94,9 @@ var (
 	testRpcInt = new(TestRpcInt)
 	testRpcInt = new(TestRpcInt)
 )
 )
 
 
+var wrapInt64Typ = reflect.TypeOf(wrapInt64(0))
+var wrapBytesTyp = reflect.TypeOf(wrapBytes(nil))
+
 func testByteBuf(in []byte) *bytes.Buffer {
 func testByteBuf(in []byte) *bytes.Buffer {
 	return bytes.NewBuffer(in)
 	return bytes.NewBuffer(in)
 }
 }
@@ -171,6 +174,8 @@ type TestRawValue struct {
 	I int
 	I int
 }
 }
 
 
+// ----
+
 type testUnixNanoTimeExt struct {
 type testUnixNanoTimeExt struct {
 	// keep timestamp here, so that do not incur interface-conversion costs
 	// keep timestamp here, so that do not incur interface-conversion costs
 	// ts int64
 	// ts int64
@@ -216,7 +221,7 @@ func (x *testUnixNanoTimeExt) UpdateExt(dest interface{}, v interface{}) {
 	}
 	}
 }
 }
 
 
-var wrapInt64Typ = reflect.TypeOf(wrapInt64(0))
+// ----
 
 
 type wrapInt64Ext int64
 type wrapInt64Ext int64
 
 
@@ -239,7 +244,7 @@ func (x *wrapInt64Ext) UpdateExt(dest interface{}, v interface{}) {
 	*v2 = wrapInt64(v.(int64))
 	*v2 = wrapInt64(v.(int64))
 }
 }
 
 
-var wrapBytesTyp = reflect.TypeOf(wrapBytes(nil))
+// ----
 
 
 type wrapBytesExt struct{}
 type wrapBytesExt struct{}
 
 
@@ -267,6 +272,38 @@ func (x *wrapBytesExt) UpdateExt(dest interface{}, v interface{}) {
 	// *v2 = wrapBytes(v.([]byte))
 	// *v2 = wrapBytes(v.([]byte))
 }
 }
 
 
+// ----
+
+type timeExt struct{}
+
+func (x timeExt) WriteExt(v interface{}) (bs []byte) {
+	switch v2 := v.(type) {
+	case time.Time:
+		bs = encodeTime(v2)
+	case *time.Time:
+		bs = encodeTime(*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)
+	if err != nil {
+		panic(err)
+	}
+	*(v.(*time.Time)) = tt
+}
+
+func (x timeExt) ConvertExt(v interface{}) interface{} {
+	return x.WriteExt(v)
+}
+func (x timeExt) UpdateExt(v interface{}, src interface{}) {
+	x.ReadExt(v, src.([]byte))
+}
+
+// ----
+
 func testCodecEncode(ts interface{}, bsIn []byte,
 func testCodecEncode(ts interface{}, bsIn []byte,
 	fn func([]byte) *bytes.Buffer, h Handle) (bs []byte, err error) {
 	fn func([]byte) *bytes.Buffer, h Handle) (bs []byte, err error) {
 	return sTestCodecEncode(ts, bsIn, fn, h, h.getBasicHandle())
 	return sTestCodecEncode(ts, bsIn, fn, h, h.getBasicHandle())
@@ -321,51 +358,58 @@ func testInit() {
 
 
 	testMsgpackH.RawToString = true
 	testMsgpackH.RawToString = true
 
 
-	// testMsgpackH.AddExt(byteSliceTyp, 0, testMsgpackH.BinaryEncodeExt, testMsgpackH.BinaryDecodeExt)
-	// testMsgpackH.AddExt(timeTyp, 1, testMsgpackH.TimeEncodeExt, testMsgpackH.TimeDecodeExt)
+	var tTimeExt timeExt
+	var tBytesExt wrapBytesExt
+	var tI64Ext wrapInt64Ext
 
 
-	// add extensions for msgpack, simple for time.Time, so we can encode/decode same way.
-	// use different flavors of XXXExt calls, including deprecated ones.
-	// NOTE:
-	// DO NOT set extensions for JsonH, so we can test json(M|Unm)arshal support.
+	// create legacy functions suitable for deprecated AddExt functionality,
+	// and use on some places for testSimpleH e.g. for time.Time and wrapInt64
 	var (
 	var (
-		timeExtEncFn = func(rv reflect.Value) (bs []byte, err error) {
+		myExtEncFn = func(x BytesExt, rv reflect.Value) (bs []byte, err error) {
 			defer panicToErr(&err)
 			defer panicToErr(&err)
-			bs = timeExt{}.WriteExt(rv.Interface())
+			bs = x.WriteExt(rv.Interface())
 			return
 			return
 		}
 		}
-		timeExtDecFn = func(rv reflect.Value, bs []byte) (err error) {
+		myExtDecFn = func(x BytesExt, rv reflect.Value, bs []byte) (err error) {
 			defer panicToErr(&err)
 			defer panicToErr(&err)
-			timeExt{}.ReadExt(rv.Interface(), bs)
+			x.ReadExt(rv.Interface(), bs)
 			return
 			return
 		}
 		}
+		timeExtEncFn      = func(rv reflect.Value) (bs []byte, err error) { return myExtEncFn(tTimeExt, rv) }
+		timeExtDecFn      = func(rv reflect.Value, bs []byte) (err error) { return myExtDecFn(tTimeExt, rv, bs) }
+		wrapInt64ExtEncFn = func(rv reflect.Value) (bs []byte, err error) { return myExtEncFn(&tI64Ext, rv) }
+		wrapInt64ExtDecFn = func(rv reflect.Value, bs []byte) (err error) { return myExtDecFn(&tI64Ext, rv, bs) }
 	)
 	)
 
 
-	// Although these extensions on time.Time will have no effect
-	// (as time.Time is a native type),
-	// we add these here to ensure nothing happens.
-	testSimpleH.AddExt(timeTyp, 1, timeExtEncFn, timeExtDecFn)
+	chkErr := func(err error) {
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	// time.Time is a native type, so extensions will have no effect.
+	// However, we add these here to ensure nothing happens.
+	chkErr(testSimpleH.AddExt(timeTyp, 1, timeExtEncFn, timeExtDecFn))
 	// testBincH.SetBytesExt(timeTyp, 1, timeExt{}) // time is builtin for binc
 	// testBincH.SetBytesExt(timeTyp, 1, timeExt{}) // time is builtin for binc
-	testMsgpackH.SetBytesExt(timeTyp, 1, timeExt{})
-	testCborH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{})
+	chkErr(testMsgpackH.SetBytesExt(timeTyp, 1, timeExt{}))
+	chkErr(testCborH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{}))
 	// testJsonH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{})
 	// testJsonH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{})
 
 
-	// Now, add extensions for the type wrapInt64,
+	// Now, add extensions for the type wrapInt64 and wrapBytes,
 	// so we can execute the Encode/Decode Ext paths.
 	// so we can execute the Encode/Decode Ext paths.
-	var tI64Ext wrapInt64Ext
-	testSimpleH.SetBytesExt(wrapInt64Typ, 16, &tI64Ext)
-	testMsgpackH.SetBytesExt(wrapInt64Typ, 16, &tI64Ext)
-	testBincH.SetBytesExt(wrapInt64Typ, 16, &tI64Ext)
-	testJsonH.SetInterfaceExt(wrapInt64Typ, 16, &tI64Ext)
-	testCborH.SetInterfaceExt(wrapInt64Typ, 16, &tI64Ext)
 
 
-	var tBytesExt wrapBytesExt
+	chkErr(testSimpleH.SetBytesExt(wrapBytesTyp, 32, &tBytesExt))
+	chkErr(testMsgpackH.SetBytesExt(wrapBytesTyp, 32, &tBytesExt))
+	chkErr(testBincH.SetBytesExt(wrapBytesTyp, 32, &tBytesExt))
+	chkErr(testJsonH.SetInterfaceExt(wrapBytesTyp, 32, &tBytesExt))
+	chkErr(testCborH.SetInterfaceExt(wrapBytesTyp, 32, &tBytesExt))
 
 
-	testSimpleH.SetBytesExt(wrapBytesTyp, 32, &tBytesExt)
-	testMsgpackH.SetBytesExt(wrapBytesTyp, 32, &tBytesExt)
-	testBincH.SetBytesExt(wrapBytesTyp, 32, &tBytesExt)
-	testJsonH.SetInterfaceExt(wrapBytesTyp, 32, &tBytesExt)
-	testCborH.SetInterfaceExt(wrapBytesTyp, 32, &tBytesExt)
+	chkErr(testSimpleH.AddExt(wrapInt64Typ, 16, wrapInt64ExtEncFn, wrapInt64ExtDecFn))
+	// chkErr(testSimpleH.SetBytesExt(wrapInt64Typ, 16, &tI64Ext))
+	chkErr(testMsgpackH.SetBytesExt(wrapInt64Typ, 16, &tI64Ext))
+	chkErr(testBincH.SetBytesExt(wrapInt64Typ, 16, &tI64Ext))
+	chkErr(testJsonH.SetInterfaceExt(wrapInt64Typ, 16, &tI64Ext))
+	chkErr(testCborH.SetInterfaceExt(wrapInt64Typ, 16, &tI64Ext))
 
 
 	// primitives MUST be an even number, so it can be used as a mapBySlice also.
 	// primitives MUST be an even number, so it can be used as a mapBySlice also.
 	primitives := []interface{}{
 	primitives := []interface{}{
@@ -2842,24 +2886,23 @@ func TestSimpleScalars(t *testing.T) {
 
 
 // TODO:
 // TODO:
 //
 //
-//   Add Tests for:
-//   - struct tags:
-//     on anonymous fields, _struct (all fields), etc
-//   - codecgen of struct containing channels.
+// Add Tests for:
+// - struct tags: on anonymous fields, _struct (all fields), etc
+// - chan to encode and decode (with support for codecgen also)
 //
 //
-//  Add negative tests for failure conditions:
-//   - bad input with large array length prefix
+// Add negative tests for failure conditions:
+// - bad input with large array length prefix
 //
 //
-// decode.go
+// decode.go (standalone)
 // - UnreadByte: only 2 states (z.ls = 2 and z.ls = 1) (0 --> 2 --> 1)
 // - UnreadByte: only 2 states (z.ls = 2 and z.ls = 1) (0 --> 2 --> 1)
 // - track: z.trb: track, stop track, check
 // - track: z.trb: track, stop track, check
-// - PreferArrayOverSlice??? (standalone test)
-// - InterfaceReset (standalone test)
+// - PreferArrayOverSlice???
+// - InterfaceReset
 // - (chan byte) to decode []byte (with mapbyslice track)
 // - (chan byte) to decode []byte (with mapbyslice track)
 // - decode slice of len 6, 16 into slice of (len 4, cap 8) and (len ) with maxinitlen=6, 8, 16
 // - decode slice of len 6, 16 into slice of (len 4, cap 8) and (len ) with maxinitlen=6, 8, 16
-// - DeleteOnNilMapValue (standalone test)
+// - DeleteOnNilMapValue
 // - decnaked: n.l == nil
 // - decnaked: n.l == nil
 // - ensureDecodeable (try to decode into a non-decodeable thing e.g. a nil interface{},
 // - ensureDecodeable (try to decode into a non-decodeable thing e.g. a nil interface{},
 //
 //
-// encode.go
+// encode.go (standalone)
 // - nil and 0-len slices and maps for non-fastpath things
 // - nil and 0-len slices and maps for non-fastpath things

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

@@ -28,15 +28,19 @@ const GenVersion = 8
 // GenHelperEncoder is exported so that it can be used externally by codecgen.
 // GenHelperEncoder is exported so that it can be used externally by codecgen.
 //
 //
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperEncoder(e *Encoder) (genHelperEncoder, encDriver) {
-	return genHelperEncoder{e: e}, e.e
+func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee encDriver) {
+	ge = genHelperEncoder{e: e}
+	ee = e.e
+	return
 }
 }
 
 
 // GenHelperDecoder is exported so that it can be used externally by codecgen.
 // GenHelperDecoder is exported so that it can be used externally by codecgen.
 //
 //
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperDecoder(d *Decoder) (genHelperDecoder, decDriver) {
-	return genHelperDecoder{d: d}, d.d
+func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd decDriver) {
+	gd = genHelperDecoder{d: d}
+	dd = d.d
+	return
 }
 }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
@@ -87,18 +91,19 @@ func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
 }
 }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperEncoder) EncRaw(iv Raw) {
-	f.e.rawBytes(iv)
-}
+func (f genHelperEncoder) EncRaw(iv Raw) { f.e.rawBytes(iv) }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
-	// // Note: builtin is no longer supported - so make this a no-op
-	// if _, ok := f.e.hh.(*BincHandle); ok {
-	// 	return timeTypId
-	// }
-	return 0
-}
+//
+// Deprecated: builtin no longer supported - so we make this method a no-op,
+// but leave in-place so that old generated files continue to work without regeneration.
+func (f genHelperEncoder) TimeRtidIfBinc() (v uintptr) { return }
+
+// func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
+// 	if _, ok := f.e.hh.(*BincHandle); ok {
+// 		return timeTypId
+// 	}
+// }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) IsJSONHandle() bool {
 func (f genHelperEncoder) IsJSONHandle() bool {
@@ -117,7 +122,8 @@ func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
 		rt = rt.Elem()
 		rt = rt.Elem()
 	}
 	}
 	rtid := rt2id(rt)
 	rtid := rt2id(rt)
-	if xfFn := f.e.h.getExt(rtid); xfFn != nil {
+	xfFn := f.e.h.getExt(rtid)
+	if xfFn != nil {
 		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 		return true
 		return true
 	}
 	}
@@ -137,9 +143,7 @@ func (f genHelperDecoder) DecBinary() bool {
 }
 }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) DecSwallow() {
-	f.d.swallow()
-}
+func (f genHelperDecoder) DecSwallow() { f.d.swallow() }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecScratchBuffer() []byte {
 func (f genHelperDecoder) DecScratchBuffer() []byte {
@@ -199,18 +203,21 @@ func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
 }
 }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) DecRaw() []byte {
-	return f.d.rawBytes()
-}
+func (f genHelperDecoder) DecRaw() []byte { return f.d.rawBytes() }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
-	// // Note: builtin is no longer supported - so make this a no-op
-	// if _, ok := f.d.hh.(*BincHandle); ok {
-	// 	return timeTypId
-	// }
-	return 0
-}
+//
+// Deprecated: builtin no longer supported - so we make this method a no-op,
+// but leave in-place so that old generated files continue to work without regeneration.
+func (f genHelperDecoder) TimeRtidIfBinc() (v uintptr) { return }
+
+// func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
+// 	// Note: builtin is no longer supported - so make this a no-op
+// 	if _, ok := f.d.hh.(*BincHandle); ok {
+// 		return timeTypId
+// 	}
+// 	return 0
+// }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) IsJSONHandle() bool {
 func (f genHelperDecoder) IsJSONHandle() bool {
@@ -226,7 +233,8 @@ func (f genHelperDecoder) HasExtensions() bool {
 func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
 func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
 	rt := reflect.TypeOf(v).Elem()
 	rt := reflect.TypeOf(v).Elem()
 	rtid := rt2id(rt)
 	rtid := rt2id(rt)
-	if xfFn := f.d.h.getExt(rtid); xfFn != nil {
+	xfFn := f.d.h.getExt(rtid)
+	if xfFn != nil {
 		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
 		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
 		return true
 		return true
 	}
 	}
@@ -239,6 +247,7 @@ func (f genHelperDecoder) DecInferLen(clen, maxlen, unit int) (rvlen int) {
 }
 }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) StringView(v []byte) string {
-	return stringView(v)
-}
+//
+// Deprecated: no longer used,
+// but leave in-place so that old generated files continue to work without regeneration.
+func (f genHelperDecoder) StringView(v []byte) string { return stringView(v) }

+ 41 - 32
codec/gen-helper.go.tmpl

@@ -28,15 +28,19 @@ const GenVersion = {{ .Version }}
 // GenHelperEncoder is exported so that it can be used externally by codecgen.
 // GenHelperEncoder is exported so that it can be used externally by codecgen.
 // 
 // 
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperEncoder(e *Encoder) (genHelperEncoder, encDriver) {
-	return genHelperEncoder{e:e}, e.e 
+func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee encDriver) {
+	ge = genHelperEncoder{e:e}
+	ee = e.e
+	return 
 }
 }
 
 
 // GenHelperDecoder is exported so that it can be used externally by codecgen.
 // GenHelperDecoder is exported so that it can be used externally by codecgen.
 // 
 // 
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperDecoder(d *Decoder) (genHelperDecoder, decDriver) {
-	return genHelperDecoder{d:d}, d.d 
+func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd decDriver) {
+	gd = genHelperDecoder{d:d}
+	dd = d.d
+	return
 }
 }
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
@@ -82,17 +86,18 @@ func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
 	f.e.marshal(bs, fnerr, false, cRAW)
 	f.e.marshal(bs, fnerr, false, cRAW)
 }
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperEncoder) EncRaw(iv Raw) {
-	f.e.rawBytes(iv)
-}
+func (f genHelperEncoder) EncRaw(iv Raw) { f.e.rawBytes(iv) }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
-	// // Note: builtin is no longer supported - so make this a no-op
-	// if _, ok := f.e.hh.(*BincHandle); ok {
-	// 	return timeTypId
-	// }
-	return 0
-}
+//
+// Deprecated: builtin no longer supported - so we make this method a no-op, 
+// but leave in-place so that old generated files continue to work without regeneration.
+func (f genHelperEncoder) TimeRtidIfBinc() (v uintptr) { return }
+// func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
+// 	if _, ok := f.e.hh.(*BincHandle); ok {
+// 		return timeTypId
+// 	}
+// }
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) IsJSONHandle() bool {
 func (f genHelperEncoder) IsJSONHandle() bool {
 	return f.e.cf.js
 	return f.e.cf.js
@@ -108,7 +113,8 @@ func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
 		rt = rt.Elem()
 		rt = rt.Elem()
 	}
 	}
 	rtid := rt2id(rt)
 	rtid := rt2id(rt)
-	if xfFn := f.e.h.getExt(rtid); xfFn != nil {
+	xfFn := f.e.h.getExt(rtid)
+	if xfFn != nil {
 		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 		return true
 		return true
 	}
 	}
@@ -126,9 +132,7 @@ func (f genHelperDecoder) DecBinary() bool {
      return f.d.be // f.d.hh.isBinaryEncoding()
      return f.d.be // f.d.hh.isBinaryEncoding()
 }
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) DecSwallow() {
-	f.d.swallow()
-}
+func (f genHelperDecoder) DecSwallow() { f.d.swallow() }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecScratchBuffer() []byte {
 func (f genHelperDecoder) DecScratchBuffer() []byte {
 	return f.d.b[:]
 	return f.d.b[:]
@@ -179,17 +183,20 @@ func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
 	}
 	}
 }
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) DecRaw() []byte {
-	return f.d.rawBytes()
-}
+func (f genHelperDecoder) DecRaw() []byte {	return f.d.rawBytes() }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
-	// // Note: builtin is no longer supported - so make this a no-op
-	// if _, ok := f.d.hh.(*BincHandle); ok {
-	// 	return timeTypId
-	// }
-	return 0
-}
+//
+// Deprecated: builtin no longer supported - so we make this method a no-op, 
+// but leave in-place so that old generated files continue to work without regeneration.
+func (f genHelperDecoder) TimeRtidIfBinc() (v uintptr) { return }
+// func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
+// 	// Note: builtin is no longer supported - so make this a no-op
+// 	if _, ok := f.d.hh.(*BincHandle); ok {
+// 		return timeTypId
+// 	}
+// 	return 0
+// }
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) IsJSONHandle() bool {
 func (f genHelperDecoder) IsJSONHandle() bool {
 	return f.d.js 
 	return f.d.js 
@@ -202,7 +209,8 @@ func (f genHelperDecoder) HasExtensions() bool {
 func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
 func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
 	rt := reflect.TypeOf(v).Elem()
 	rt := reflect.TypeOf(v).Elem()
 	rtid := rt2id(rt)
 	rtid := rt2id(rt)
-	if xfFn := f.d.h.getExt(rtid); xfFn != nil {
+	xfFn := f.d.h.getExt(rtid)
+	if xfFn != nil {
 		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
 		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
 		return true
 		return true
 	}
 	}
@@ -213,7 +221,8 @@ func (f genHelperDecoder) DecInferLen(clen, maxlen, unit int) (rvlen int) {
 	return decInferLen(clen, maxlen, unit)
 	return decInferLen(clen, maxlen, unit)
 }
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
-func (f genHelperDecoder) StringView(v []byte) string {
-	return stringView(v)
-}
+//
+// Deprecated: no longer used, 
+// but leave in-place so that old generated files continue to work without regeneration.
+func (f genHelperDecoder) StringView(v []byte) string { return stringView(v) }
 
 

+ 9 - 15
codec/helper.go

@@ -514,34 +514,28 @@ type setExtWrapper struct {
 	i InterfaceExt
 	i InterfaceExt
 }
 }
 
 
-func (x *setExtWrapper) WriteExt(v interface{}) []byte {
-	if x.b == nil {
-		panic("BytesExt.WriteExt is not supported")
+func (x *setExtWrapper) check(v bool, s string) {
+	if v {
+		panic(fmt.Errorf("%s is not supported", s))
 	}
 	}
+}
+func (x *setExtWrapper) WriteExt(v interface{}) []byte {
+	x.check(x.b == nil, "BytesExt.WriteExt")
 	return x.b.WriteExt(v)
 	return x.b.WriteExt(v)
 }
 }
 
 
 func (x *setExtWrapper) ReadExt(v interface{}, bs []byte) {
 func (x *setExtWrapper) ReadExt(v interface{}, bs []byte) {
-	if x.b == nil {
-		panic("BytesExt.WriteExt is not supported")
-
-	}
+	x.check(x.b == nil, "BytesExt.ReadExt")
 	x.b.ReadExt(v, bs)
 	x.b.ReadExt(v, bs)
 }
 }
 
 
 func (x *setExtWrapper) ConvertExt(v interface{}) interface{} {
 func (x *setExtWrapper) ConvertExt(v interface{}) interface{} {
-	if x.i == nil {
-		panic("InterfaceExt.ConvertExt is not supported")
-
-	}
+	x.check(x.i == nil, "InterfaceExt.ConvertExt")
 	return x.i.ConvertExt(v)
 	return x.i.ConvertExt(v)
 }
 }
 
 
 func (x *setExtWrapper) UpdateExt(dest interface{}, v interface{}) {
 func (x *setExtWrapper) UpdateExt(dest interface{}, v interface{}) {
-	if x.i == nil {
-		panic("InterfaceExxt.UpdateExt is not supported")
-
-	}
+	x.check(x.i == nil, "InterfaceExt.UpdateExt")
 	x.i.UpdateExt(dest, v)
 	x.i.UpdateExt(dest, v)
 }
 }
 
 

+ 1 - 4
codec/helper_internal.go

@@ -14,11 +14,8 @@ import (
 )
 )
 
 
 func panicValToErr(panicVal interface{}, err *error) {
 func panicValToErr(panicVal interface{}, err *error) {
-	if panicVal == nil {
-		return
-	}
-	// case nil
 	switch xerr := panicVal.(type) {
 	switch xerr := panicVal.(type) {
+	case nil:
 	case error:
 	case error:
 		*err = xerr
 		*err = xerr
 	case string:
 	case string:

+ 16 - 4
codec/helper_unsafe.go

@@ -69,12 +69,24 @@ func definitelyNil(v interface{}) bool {
 	// For true references (map, ptr, func, chan), you can just look
 	// For true references (map, ptr, func, chan), you can just look
 	// at the word of the interface. However, for slices, you have to dereference
 	// at the word of the interface. However, for slices, you have to dereference
 	// the word, and get a pointer to the 3-word interface value.
 	// the word, and get a pointer to the 3-word interface value.
+	//
+	// However, the following are cheap calls
+	// - TypeOf(interface): cheap 2-line call.
+	// - ValueOf(interface{}): expensive
+	// - type.Kind: cheap call through an interface
+	// - Value.Type(): cheap call
+	//                 except it's a method value (e.g. r.Read, which implies that it is a Func)
 
 
-	// var ui *unsafeIntf = (*unsafeIntf)(unsafe.Pointer(&v))
-	// var word unsafe.Pointer = ui.word
-	// // fmt.Printf(">>>> definitely nil: isnil: %v, TYPE: \t%T, word: %v, *word: %v, type: %v, nil: %v\n", v == nil, v, word, *((*unsafe.Pointer)(word)), ui.typ, nil)
-	// return word == nil // || *((*unsafe.Pointer)(word)) == nil
 	return ((*unsafeIntf)(unsafe.Pointer(&v))).word == nil
 	return ((*unsafeIntf)(unsafe.Pointer(&v))).word == nil
+
+	// var ui *unsafeIntf = (*unsafeIntf)(unsafe.Pointer(&v))
+	// if ui.word == nil {
+	// 	return true
+	// }
+	// var tk = reflect.TypeOf(v).Kind()
+	// return (tk == reflect.Interface || tk == reflect.Slice) && *(*unsafe.Pointer)(ui.word) == nil
+	// fmt.Printf(">>>> definitely nil: isnil: %v, TYPE: \t%T, word: %v, *word: %v, type: %v, nil: %v\n",
+	// v == nil, v, word, *((*unsafe.Pointer)(word)), ui.typ, nil)
 }
 }
 
 
 // func keepAlive4BytesView(v string) {
 // func keepAlive4BytesView(v string) {

+ 2 - 33
codec/time.go

@@ -3,40 +3,9 @@
 
 
 package codec
 package codec
 
 
-import (
-	"fmt"
-	"time"
-)
+import "time"
 
 
-var timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
-
-type timeExt struct{}
-
-func (x timeExt) WriteExt(v interface{}) (bs []byte) {
-	switch v2 := v.(type) {
-	case time.Time:
-		bs = encodeTime(v2)
-	case *time.Time:
-		bs = encodeTime(*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)
-	if err != nil {
-		panic(err)
-	}
-	*(v.(*time.Time)) = tt
-}
-
-func (x timeExt) ConvertExt(v interface{}) interface{} {
-	return x.WriteExt(v)
-}
-func (x timeExt) UpdateExt(v interface{}, src interface{}) {
-	x.ReadExt(v, src.([]byte))
-}
+// var timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
 
 
 // EncodeTime encodes a time.Time as a []byte, including
 // EncodeTime encodes a time.Time as a []byte, including
 // information on the instant in time and UTC offset.
 // information on the instant in time and UTC offset.

+ 4 - 0
codec/z_all_test.go

@@ -41,6 +41,9 @@ func testGroupResetFlags() {
 	testMaxInitLen = 0
 	testMaxInitLen = 0
 	testUseIoWrapper = false
 	testUseIoWrapper = false
 	testNumRepeatString = 8
 	testNumRepeatString = 8
+	testEncodeOptions.RecursiveEmptyCheck = false
+	testDecodeOptions.MapValueReset = false
+	testUseIoEncDec = -1
 }
 }
 
 
 func testSuite(t *testing.T, f func(t *testing.T)) {
 func testSuite(t *testing.T, f func(t *testing.T)) {
@@ -65,6 +68,7 @@ func testSuite(t *testing.T, f func(t *testing.T)) {
 	testCheckCircRef = true
 	testCheckCircRef = true
 	testUseReset = true
 	testUseReset = true
 	testDecodeOptions.MapValueReset = true
 	testDecodeOptions.MapValueReset = true
+	testEncodeOptions.RecursiveEmptyCheck = true
 	testReinit()
 	testReinit()
 	t.Run("optionsTrue", f)
 	t.Run("optionsTrue", f)