Browse Source

codec: support for extension types which fully delegate encoding/decoding to library

Previously, users had to create extensions and fully own how they
are encoded or decoded.

However, there's a convenience to having the type be naturally encoded in the format,
and just registering a tag id with it.

We now support this via
- func NewSelfBytesExt(...)
- var GlobalSelfInterfaceExt
- SelfExt interface

Fixes #274
Ugorji Nwoke 6 years ago
parent
commit
449ecf999a
9 changed files with 618 additions and 0 deletions
  1. 6 0
      README.md
  2. 3 0
      codec/cbor.go
  3. 44 0
      codec/codec_test.go
  4. 3 0
      codec/doc.go
  5. 1 0
      codec/encode.go
  6. 3 0
      codec/json.go
  7. 131 0
      codec/selfext.go
  8. 412 0
      codec/values_codecgen_generated_test.go
  9. 15 0
      codec/values_flex_test.go

+ 6 - 0
README.md

@@ -116,6 +116,9 @@ as an empty map because it has no exported fields, while UUID would be
 encoded as a string. However, with extension support, you can encode any of
 encoded as a string. However, with extension support, you can encode any of
 these however you like.
 these however you like.
 
 
+There is also seamless support provided for registering an extension (with a
+tag) but letting the encoding mechanism default to the standard way.
+
 
 
 ## Custom Encoding and Decoding
 ## Custom Encoding and Decoding
 
 
@@ -267,6 +270,7 @@ var GoRpc goRpc
 var MsgpackSpecRpc msgpackSpecRpc
 var MsgpackSpecRpc msgpackSpecRpc
 func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd genHelperDecDriver)
 func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd genHelperDecDriver)
 func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee genHelperEncDriver)
 func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee genHelperEncDriver)
+func NewSelfBytesExt(h Handle, bufcap int) *selfBytesExt
 type BasicHandle struct{ ... }
 type BasicHandle struct{ ... }
 type BincHandle struct{ ... }
 type BincHandle struct{ ... }
 type BytesExt interface{ ... }
 type BytesExt interface{ ... }
@@ -282,6 +286,7 @@ type Encoder struct{ ... }
 type Ext interface{ ... }
 type Ext interface{ ... }
 type Handle interface{ ... }
 type Handle interface{ ... }
 type InterfaceExt interface{ ... }
 type InterfaceExt interface{ ... }
+    var GlobalSelfInterfaceExt InterfaceExt = selfInterfaceExt{}
 type JsonHandle struct{ ... }
 type JsonHandle struct{ ... }
 type MapBySlice interface{ ... }
 type MapBySlice interface{ ... }
 type MissingFielder interface{ ... }
 type MissingFielder interface{ ... }
@@ -291,6 +296,7 @@ type RPCOptions struct{ ... }
 type Raw []byte
 type Raw []byte
 type RawExt struct{ ... }
 type RawExt struct{ ... }
 type Rpc interface{ ... }
 type Rpc interface{ ... }
+type SelfExt interface{ ... }
 type Selfer interface{ ... }
 type Selfer interface{ ... }
 type SimpleHandle struct{ ... }
 type SimpleHandle struct{ ... }
 type TypeInfos struct{ ... }
 type TypeInfos struct{ ... }

+ 3 - 0
codec/cbor.go

@@ -607,6 +607,9 @@ func (d *cborDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxta
 		return
 		return
 	} else {
 	} else {
 		var v interface{}
 		var v interface{}
+		if v2, ok := rv.(SelfExt); ok {
+			v = v2.CodecConvertExt()
+		}
 		d.d.decode(&v)
 		d.d.decode(&v)
 		ext.UpdateExt(rv, v)
 		ext.UpdateExt(rv, v)
 	}
 	}

+ 44 - 0
codec/codec_test.go

@@ -132,6 +132,7 @@ var (
 
 
 var wrapInt64Typ = reflect.TypeOf(wrapInt64(0))
 var wrapInt64Typ = reflect.TypeOf(wrapInt64(0))
 var wrapBytesTyp = reflect.TypeOf(wrapBytes(nil))
 var wrapBytesTyp = reflect.TypeOf(wrapBytes(nil))
+var testSelfExtTyp = reflect.TypeOf((*TestSelfExtImpl)(nil)).Elem()
 
 
 func testByteBuf(in []byte) *bytes.Buffer {
 func testByteBuf(in []byte) *bytes.Buffer {
 	return bytes.NewBuffer(in)
 	return bytes.NewBuffer(in)
@@ -423,6 +424,13 @@ func testInit() {
 	chkErr(testCborH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{}))
 	chkErr(testCborH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{}))
 	// testJsonH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{})
 	// testJsonH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{})
 
 
+	// Add extensions for the testSelfExt
+	chkErr(testSimpleH.SetBytesExt(testSelfExtTyp, 78, NewSelfBytesExt(testSimpleH, 128)))
+	chkErr(testMsgpackH.SetBytesExt(testSelfExtTyp, 78, NewSelfBytesExt(testMsgpackH, 128)))
+	chkErr(testBincH.SetBytesExt(testSelfExtTyp, 78, NewSelfBytesExt(testBincH, 128)))
+	chkErr(testJsonH.SetInterfaceExt(testSelfExtTyp, 78, GlobalSelfInterfaceExt))
+	chkErr(testCborH.SetInterfaceExt(testSelfExtTyp, 78, GlobalSelfInterfaceExt))
+
 	// Now, add extensions for the type wrapInt64 and wrapBytes,
 	// 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.
 
 
@@ -2472,6 +2480,22 @@ func doTestMultipleEncDec(t *testing.T, name string, h Handle) {
 	testDeepEqualErr(s2, s21, t, name+"-multiple-encode")
 	testDeepEqualErr(s2, s21, t, name+"-multiple-encode")
 }
 }
 
 
+func doTestSelfExt(t *testing.T, name string, h Handle) {
+	testOnce.Do(testInitAll)
+	// encode a string multiple times.
+	// decode it multiple times.
+	// ensure we get the value each time
+	var ts TestSelfExtImpl
+	ts.S = "ugorji"
+	ts.I = 5678
+	ts.B = true
+	var ts2 TestSelfExtImpl
+
+	bs := testMarshalErr(&ts, h, t, name)
+	testUnmarshalErr(&ts2, bs, h, t, name)
+	testDeepEqualErr(&ts, &ts2, t, name)
+}
+
 // -----------------
 // -----------------
 
 
 func TestJsonDecodeNonStringScalarInStringContext(t *testing.T) {
 func TestJsonDecodeNonStringScalarInStringContext(t *testing.T) {
@@ -3265,6 +3289,26 @@ func TestSimpleMaxDepth(t *testing.T) {
 	doTestMaxDepth(t, "simple", testSimpleH)
 	doTestMaxDepth(t, "simple", testSimpleH)
 }
 }
 
 
+func TestJsonSelfExt(t *testing.T) {
+	doTestSelfExt(t, "json", testJsonH)
+}
+
+func TestCborSelfExt(t *testing.T) {
+	doTestSelfExt(t, "cbor", testCborH)
+}
+
+func TestMsgpackSelfExt(t *testing.T) {
+	doTestSelfExt(t, "msgpack", testMsgpackH)
+}
+
+func TestBincSelfExt(t *testing.T) {
+	doTestSelfExt(t, "binc", testBincH)
+}
+
+func TestSimpleSelfExt(t *testing.T) {
+	doTestSelfExt(t, "simple", testSimpleH)
+}
+
 func TestMultipleEncDec(t *testing.T) {
 func TestMultipleEncDec(t *testing.T) {
 	doTestMultipleEncDec(t, "json", testJsonH)
 	doTestMultipleEncDec(t, "json", testJsonH)
 }
 }

+ 3 - 0
codec/doc.go

@@ -96,6 +96,9 @@ encoded as an empty map because it has no exported fields, while UUID
 would be encoded as a string. However, with extension support, you can
 would be encoded as a string. However, with extension support, you can
 encode any of these however you like.
 encode any of these however you like.
 
 
+There is also seamless support provided for registering an extension (with a tag)
+but letting the encoding mechanism default to the standard way.
+
 Custom Encoding and Decoding
 Custom Encoding and Decoding
 
 
 This package maintains symmetry in the encoding and decoding halfs.
 This package maintains symmetry in the encoding and decoding halfs.

+ 1 - 0
codec/encode.go

@@ -479,6 +479,7 @@ func (e *Encoder) rawExt(f *codecFnInfo, rv reflect.Value) {
 }
 }
 
 
 func (e *Encoder) ext(f *codecFnInfo, rv reflect.Value) {
 func (e *Encoder) ext(f *codecFnInfo, rv reflect.Value) {
+	// xdebugf("*Encoder.ext: rv: %v, rv2i: %T, %v", rv, rv2i(rv), rv2i(rv))
 	e.e.EncodeExt(rv2i(rv), f.xfTag, f.xfFn, e)
 	e.e.EncodeExt(rv2i(rv), f.xfTag, f.xfFn, e)
 }
 }
 
 

+ 3 - 0
codec/json.go

@@ -946,6 +946,9 @@ func (d *jsonDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxta
 		d.d.decode(&re.Value)
 		d.d.decode(&re.Value)
 	} else {
 	} else {
 		var v interface{}
 		var v interface{}
+		if v2, ok := rv.(SelfExt); ok {
+			v = v2.CodecConvertExt()
+		}
 		d.d.decode(&v)
 		d.d.decode(&v)
 		ext.UpdateExt(rv, v)
 		ext.UpdateExt(rv, v)
 	}
 	}

+ 131 - 0
codec/selfext.go

@@ -0,0 +1,131 @@
+// 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 "sync"
+
+// We now support the ability for an extension to define a tag,
+// but allow itself to be encoded in a default way.
+//
+// Because we use the type to determine how to encode or decode a value,
+// we cannot tell a value to just encode itself, as that will lead to an
+// infinite recursion.
+//
+// Instead, a value can define how it is to be encoded using a delegate value.
+//
+// If your types implement SelfExt, you can use this to define an extension.
+//
+// At encode time, the interface will call CodecConvertExt, and encode that value.
+// At decode time, the library will call CodecConvertExt, decode into that value, and
+// call the primary object's CodecUpdateExt with that value.
+//
+// The easiest way to do this is via struct embedding:
+//
+//    type T struct {
+//        tHelper
+//    }
+//    func (t *T) CodecConvertExt() { return &t.tHelper }
+//    func (t *T) CodecUpdateExt(interface{}) {  } // no-op (as our delegate is interior pointer)
+//    type tHelper struct {
+//        // ... all t fields
+//    }
+//
+// To use:
+//
+//    cborHandle.SetInterfaceExt(reflect.TypeOf(T), 122, codec.GlobalSelfInterfaceExt)
+//
+//    msgpackHandle.SetBytesExt(reflect.TypeOf(T), 122, codec.NewSelfBytesExt(msgpackHandle, 1024))
+//
+// This extension expects that types registered with it implement SelfExt interface.
+var GlobalSelfInterfaceExt InterfaceExt = selfInterfaceExt{}
+
+// var selfExtEncPool = sync.Pool{
+// 	New: func() interface{} { return new(Encoder) },
+// }
+// var selfExtDecPool = sync.Pool{
+// 	New: func() interface{} { return new(Decoder) },
+// }
+
+// SelfExt is the interface that users implement so that their types
+// can, with minimal effort, be able to be an extension while allowing the
+// library handling the encoding/decoding needs easily.
+type SelfExt interface {
+	CodecConvertExt() interface{}
+	CodecUpdateExt(interface{})
+}
+
+type selfBytesExt struct {
+	p sync.Pool // pool of byte buffers
+	e sync.Pool
+	d sync.Pool
+	h Handle
+	// bufcap int     // cap for each byte buffer created
+	// inited uint32
+	// mu sync.Mutex
+}
+
+type selfInterfaceExt struct{}
+
+// NewSelfBytesExt will return a BytesExt implementation,
+// that will call an encoder to encode the value to a stream
+// so it can be placed into the encoder stream.
+//
+// Users can specify a buffer size, and we will initialize that
+// buffer for encoding the type. This allows users manage
+// how big the buffer is based on their knowledge of the type being
+// registered.
+//
+// This extension expects that types registered with it implement
+// SelfExt interface.
+//
+// This is used by libraries that support BytesExt e.g. msgpack, binc.
+func NewSelfBytesExt(h Handle, bufcap int) *selfBytesExt {
+	var v = selfBytesExt{h: h}
+	v.p.New = func() interface{} {
+		return make([]byte, 0, bufcap)
+	}
+	v.e.New = func() interface{} { return NewEncoderBytes(nil, v.h) }
+	v.d.New = func() interface{} { return NewDecoderBytes(nil, v.h) }
+	return &v
+}
+
+func (x *selfBytesExt) WriteExt(v interface{}) (s []byte) {
+	ei := x.e.Get()
+	bi := x.p.Get()
+	defer func() {
+		x.e.Put(ei)
+		x.p.Put(bi)
+	}()
+	b := (bi.([]byte))[:0]
+	e := ei.(*Encoder)
+	e.ResetBytes(&b)
+	e.MustEncode(v.(SelfExt).CodecConvertExt())
+	if len(b) > 0 {
+		s = make([]byte, len(b))
+		copy(s, b)
+	}
+	return
+}
+
+func (x *selfBytesExt) ReadExt(dst interface{}, src []byte) {
+	di := x.d.Get()
+	d := di.(*Decoder)
+	defer func() {
+		d.Release()
+		x.d.Put(di)
+	}()
+	d.ResetBytes(src)
+	v := dst.(SelfExt).CodecConvertExt()
+	d.MustDecode(v)
+	dst.(SelfExt).CodecUpdateExt(v)
+	return
+}
+
+func (x selfInterfaceExt) ConvertExt(v interface{}) interface{} {
+	return v.(SelfExt).CodecConvertExt()
+}
+
+func (x selfInterfaceExt) UpdateExt(dst interface{}, src interface{}) {
+	dst.(SelfExt).CodecUpdateExt(src)
+}

+ 412 - 0
codec/values_codecgen_generated_test.go

@@ -8552,6 +8552,418 @@ func (x *missingFielderT2) codecDecodeSelfFromArray(l int, d *Decoder) {
 	z.DecReadArrayEnd()
 	z.DecReadArrayEnd()
 }
 }
 
 
+func (x *testSelfExtHelper) CodecEncodeSelf(e *Encoder) {
+	var h codecSelfer19780
+	z, r := GenHelperEncoder(e)
+	_, _, _ = h, z, r
+	if x == nil {
+		r.EncodeNil()
+	} else {
+		if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+			z.EncExtension(x, yyxt1)
+		} else {
+			yysep2 := !z.EncBinary()
+			yy2arr2 := z.EncBasicHandle().StructToArray
+			_, _ = yysep2, yy2arr2
+			const yyr2 bool = false // struct tag has 'toArray'
+			if yyr2 || yy2arr2 {
+				z.EncWriteArrayStart(3)
+				z.EncWriteArrayElem()
+				if z.EncBasicHandle().StringToRaw {
+					r.EncodeStringBytesRaw(z.BytesView(string(x.S)))
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, string(x.S))
+				}
+				z.EncWriteArrayElem()
+				r.EncodeInt(int64(x.I))
+				z.EncWriteArrayElem()
+				r.EncodeBool(bool(x.B))
+				z.EncWriteArrayEnd()
+			} else {
+				z.EncWriteMapStart(3)
+				z.EncWriteMapElemKey()
+				if z.IsJSONHandle() {
+					z.WriteStr("\"S\"")
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, `S`)
+				}
+				z.EncWriteMapElemValue()
+				if z.EncBasicHandle().StringToRaw {
+					r.EncodeStringBytesRaw(z.BytesView(string(x.S)))
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, string(x.S))
+				}
+				z.EncWriteMapElemKey()
+				if z.IsJSONHandle() {
+					z.WriteStr("\"I\"")
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, `I`)
+				}
+				z.EncWriteMapElemValue()
+				r.EncodeInt(int64(x.I))
+				z.EncWriteMapElemKey()
+				if z.IsJSONHandle() {
+					z.WriteStr("\"B\"")
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, `B`)
+				}
+				z.EncWriteMapElemValue()
+				r.EncodeBool(bool(x.B))
+				z.EncWriteMapEnd()
+			}
+		}
+	}
+}
+
+func (x *testSelfExtHelper) CodecDecodeSelf(d *Decoder) {
+	var h codecSelfer19780
+	z, r := GenHelperDecoder(d)
+	_, _, _ = h, z, r
+	if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
+	} else {
+		yyct2 := r.ContainerType()
+		if yyct2 == codecSelferValueTypeMap19780 {
+			yyl2 := z.DecReadMapStart()
+			if yyl2 == 0 {
+				z.DecReadMapEnd()
+			} else {
+				x.codecDecodeSelfFromMap(yyl2, d)
+			}
+		} else if yyct2 == codecSelferValueTypeArray19780 {
+			yyl2 := z.DecReadArrayStart()
+			if yyl2 == 0 {
+				z.DecReadArrayEnd()
+			} else {
+				x.codecDecodeSelfFromArray(yyl2, d)
+			}
+		} else {
+			panic(errCodecSelferOnlyMapOrArrayEncodeToStruct19780)
+		}
+	}
+}
+
+func (x *testSelfExtHelper) codecDecodeSelfFromMap(l int, d *Decoder) {
+	var h codecSelfer19780
+	z, r := GenHelperDecoder(d)
+	_, _, _ = h, z, r
+	var yyhl3 bool = l >= 0
+	for yyj3 := 0; ; yyj3++ {
+		if yyhl3 {
+			if yyj3 >= l {
+				break
+			}
+		} else {
+			if r.CheckBreak() {
+				break
+			}
+		}
+		z.DecReadMapElemKey()
+		yys3 := z.StringView(r.DecodeStringAsBytes())
+		z.DecReadMapElemValue()
+		switch yys3 {
+		case "S":
+			if r.TryDecodeAsNil() {
+				x.S = ""
+			} else {
+				x.S = (string)(r.DecodeString())
+			}
+		case "I":
+			if r.TryDecodeAsNil() {
+				x.I = 0
+			} else {
+				x.I = (int64)(r.DecodeInt64())
+			}
+		case "B":
+			if r.TryDecodeAsNil() {
+				x.B = false
+			} else {
+				x.B = (bool)(r.DecodeBool())
+			}
+		default:
+			z.DecStructFieldNotFound(-1, yys3)
+		} // end switch yys3
+	} // end for yyj3
+	z.DecReadMapEnd()
+}
+
+func (x *testSelfExtHelper) codecDecodeSelfFromArray(l int, d *Decoder) {
+	var h codecSelfer19780
+	z, r := GenHelperDecoder(d)
+	_, _, _ = h, z, r
+	var yyj7 int
+	var yyb7 bool
+	var yyhl7 bool = l >= 0
+	yyj7++
+	if yyhl7 {
+		yyb7 = yyj7 > l
+	} else {
+		yyb7 = r.CheckBreak()
+	}
+	if yyb7 {
+		z.DecReadArrayEnd()
+		return
+	}
+	z.DecReadArrayElem()
+	if r.TryDecodeAsNil() {
+		x.S = ""
+	} else {
+		x.S = (string)(r.DecodeString())
+	}
+	yyj7++
+	if yyhl7 {
+		yyb7 = yyj7 > l
+	} else {
+		yyb7 = r.CheckBreak()
+	}
+	if yyb7 {
+		z.DecReadArrayEnd()
+		return
+	}
+	z.DecReadArrayElem()
+	if r.TryDecodeAsNil() {
+		x.I = 0
+	} else {
+		x.I = (int64)(r.DecodeInt64())
+	}
+	yyj7++
+	if yyhl7 {
+		yyb7 = yyj7 > l
+	} else {
+		yyb7 = r.CheckBreak()
+	}
+	if yyb7 {
+		z.DecReadArrayEnd()
+		return
+	}
+	z.DecReadArrayElem()
+	if r.TryDecodeAsNil() {
+		x.B = false
+	} else {
+		x.B = (bool)(r.DecodeBool())
+	}
+	for {
+		yyj7++
+		if yyhl7 {
+			yyb7 = yyj7 > l
+		} else {
+			yyb7 = r.CheckBreak()
+		}
+		if yyb7 {
+			break
+		}
+		z.DecReadArrayElem()
+		z.DecStructFieldNotFound(yyj7-1, "")
+	}
+	z.DecReadArrayEnd()
+}
+
+func (x *TestSelfExtImpl) CodecEncodeSelf(e *Encoder) {
+	var h codecSelfer19780
+	z, r := GenHelperEncoder(e)
+	_, _, _ = h, z, r
+	if x == nil {
+		r.EncodeNil()
+	} else {
+		if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+			z.EncExtension(x, yyxt1)
+		} else {
+			yysep2 := !z.EncBinary()
+			yy2arr2 := z.EncBasicHandle().StructToArray
+			_, _ = yysep2, yy2arr2
+			const yyr2 bool = false // struct tag has 'toArray'
+			if yyr2 || yy2arr2 {
+				z.EncWriteArrayStart(3)
+				z.EncWriteArrayElem()
+				if z.EncBasicHandle().StringToRaw {
+					r.EncodeStringBytesRaw(z.BytesView(string(x.testSelfExtHelper.S)))
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, string(x.testSelfExtHelper.S))
+				}
+				z.EncWriteArrayElem()
+				r.EncodeInt(int64(x.testSelfExtHelper.I))
+				z.EncWriteArrayElem()
+				r.EncodeBool(bool(x.testSelfExtHelper.B))
+				z.EncWriteArrayEnd()
+			} else {
+				z.EncWriteMapStart(3)
+				z.EncWriteMapElemKey()
+				if z.IsJSONHandle() {
+					z.WriteStr("\"S\"")
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, `S`)
+				}
+				z.EncWriteMapElemValue()
+				if z.EncBasicHandle().StringToRaw {
+					r.EncodeStringBytesRaw(z.BytesView(string(x.testSelfExtHelper.S)))
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, string(x.testSelfExtHelper.S))
+				}
+				z.EncWriteMapElemKey()
+				if z.IsJSONHandle() {
+					z.WriteStr("\"I\"")
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, `I`)
+				}
+				z.EncWriteMapElemValue()
+				r.EncodeInt(int64(x.testSelfExtHelper.I))
+				z.EncWriteMapElemKey()
+				if z.IsJSONHandle() {
+					z.WriteStr("\"B\"")
+				} else {
+					r.EncodeStringEnc(codecSelferCcUTF819780, `B`)
+				}
+				z.EncWriteMapElemValue()
+				r.EncodeBool(bool(x.testSelfExtHelper.B))
+				z.EncWriteMapEnd()
+			}
+		}
+	}
+}
+
+func (x *TestSelfExtImpl) CodecDecodeSelf(d *Decoder) {
+	var h codecSelfer19780
+	z, r := GenHelperDecoder(d)
+	_, _, _ = h, z, r
+	if yyxt1 := z.Extension(z.I2Rtid(x)); yyxt1 != nil {
+		z.DecExtension(x, yyxt1)
+	} else {
+		yyct2 := r.ContainerType()
+		if yyct2 == codecSelferValueTypeMap19780 {
+			yyl2 := z.DecReadMapStart()
+			if yyl2 == 0 {
+				z.DecReadMapEnd()
+			} else {
+				x.codecDecodeSelfFromMap(yyl2, d)
+			}
+		} else if yyct2 == codecSelferValueTypeArray19780 {
+			yyl2 := z.DecReadArrayStart()
+			if yyl2 == 0 {
+				z.DecReadArrayEnd()
+			} else {
+				x.codecDecodeSelfFromArray(yyl2, d)
+			}
+		} else {
+			panic(errCodecSelferOnlyMapOrArrayEncodeToStruct19780)
+		}
+	}
+}
+
+func (x *TestSelfExtImpl) codecDecodeSelfFromMap(l int, d *Decoder) {
+	var h codecSelfer19780
+	z, r := GenHelperDecoder(d)
+	_, _, _ = h, z, r
+	var yyhl3 bool = l >= 0
+	for yyj3 := 0; ; yyj3++ {
+		if yyhl3 {
+			if yyj3 >= l {
+				break
+			}
+		} else {
+			if r.CheckBreak() {
+				break
+			}
+		}
+		z.DecReadMapElemKey()
+		yys3 := z.StringView(r.DecodeStringAsBytes())
+		z.DecReadMapElemValue()
+		switch yys3 {
+		case "S":
+			if r.TryDecodeAsNil() {
+				x.testSelfExtHelper.S = ""
+			} else {
+				x.testSelfExtHelper.S = (string)(r.DecodeString())
+			}
+		case "I":
+			if r.TryDecodeAsNil() {
+				x.testSelfExtHelper.I = 0
+			} else {
+				x.testSelfExtHelper.I = (int64)(r.DecodeInt64())
+			}
+		case "B":
+			if r.TryDecodeAsNil() {
+				x.testSelfExtHelper.B = false
+			} else {
+				x.testSelfExtHelper.B = (bool)(r.DecodeBool())
+			}
+		default:
+			z.DecStructFieldNotFound(-1, yys3)
+		} // end switch yys3
+	} // end for yyj3
+	z.DecReadMapEnd()
+}
+
+func (x *TestSelfExtImpl) codecDecodeSelfFromArray(l int, d *Decoder) {
+	var h codecSelfer19780
+	z, r := GenHelperDecoder(d)
+	_, _, _ = h, z, r
+	var yyj7 int
+	var yyb7 bool
+	var yyhl7 bool = l >= 0
+	yyj7++
+	if yyhl7 {
+		yyb7 = yyj7 > l
+	} else {
+		yyb7 = r.CheckBreak()
+	}
+	if yyb7 {
+		z.DecReadArrayEnd()
+		return
+	}
+	z.DecReadArrayElem()
+	if r.TryDecodeAsNil() {
+		x.testSelfExtHelper.S = ""
+	} else {
+		x.testSelfExtHelper.S = (string)(r.DecodeString())
+	}
+	yyj7++
+	if yyhl7 {
+		yyb7 = yyj7 > l
+	} else {
+		yyb7 = r.CheckBreak()
+	}
+	if yyb7 {
+		z.DecReadArrayEnd()
+		return
+	}
+	z.DecReadArrayElem()
+	if r.TryDecodeAsNil() {
+		x.testSelfExtHelper.I = 0
+	} else {
+		x.testSelfExtHelper.I = (int64)(r.DecodeInt64())
+	}
+	yyj7++
+	if yyhl7 {
+		yyb7 = yyj7 > l
+	} else {
+		yyb7 = r.CheckBreak()
+	}
+	if yyb7 {
+		z.DecReadArrayEnd()
+		return
+	}
+	z.DecReadArrayElem()
+	if r.TryDecodeAsNil() {
+		x.testSelfExtHelper.B = false
+	} else {
+		x.testSelfExtHelper.B = (bool)(r.DecodeBool())
+	}
+	for {
+		yyj7++
+		if yyhl7 {
+			yyb7 = yyj7 > l
+		} else {
+			yyb7 = r.CheckBreak()
+		}
+		if yyb7 {
+			break
+		}
+		z.DecReadArrayElem()
+		z.DecStructFieldNotFound(yyj7-1, "")
+	}
+	z.DecReadArrayEnd()
+}
+
 func (x *TestStrucFlex) CodecEncodeSelf(e *Encoder) {
 func (x *TestStrucFlex) CodecEncodeSelf(e *Encoder) {
 	var h codecSelfer19780
 	var h codecSelfer19780
 	z, r := GenHelperEncoder(e)
 	z, r := GenHelperEncoder(e)

+ 15 - 0
codec/values_flex_test.go

@@ -135,6 +135,21 @@ type missingFielderT2 struct {
 	I int64
 	I int64
 }
 }
 
 
+type testSelfExtHelper struct {
+	S string
+	I int64
+	B bool
+}
+
+type TestSelfExtImpl struct {
+	testSelfExtHelper
+}
+
+func (t *TestSelfExtImpl) CodecConvertExt() interface{} {
+	return &t.testSelfExtHelper
+}
+func (t *TestSelfExtImpl) CodecUpdateExt(v interface{}) {}
+
 var testWRepeated512 wrapBytes
 var testWRepeated512 wrapBytes
 var testStrucTime = time.Date(2012, 2, 2, 2, 2, 2, 2000, time.UTC).UTC()
 var testStrucTime = time.Date(2012, 2, 2, 2, 2, 2, 2000, time.UTC).UTC()