Pārlūkot izejas kodu

codec: support mapping non-interface type to an interface

This allows us decode appropriately into any interface type
with a correctly configured non-interface value.

Also did misc clean ups in preparation for v1.1 release
Ugorji Nwoke 8 gadi atpakaļ
vecāks
revīzija
d2b2650062
11 mainītis faili ar 245 papildinājumiem un 102 dzēšanām
  1. 11 5
      codec/0doc.go
  2. 8 5
      codec/README.md
  3. 1 1
      codec/binc.go
  4. 62 0
      codec/codec_test.go
  5. 15 15
      codec/decode.go
  6. 6 10
      codec/encode.go
  7. 54 5
      codec/helper.go
  8. 23 20
      codec/helper_not_unsafe.go
  9. 58 41
      codec/helper_unsafe.go
  10. 1 0
      codec/rpc.go
  11. 6 0
      codec/z_all_test.go

+ 11 - 5
codec/0doc.go

@@ -39,9 +39,8 @@ Rich Feature Set includes:
   - Careful selected use of 'unsafe' for targeted performance gains.
     100% mode exists where 'unsafe' is not used at all.
   - Lock-free (sans mutex) concurrency for scaling to 100's of cores
-  - Multiple conversions:
-    Package coerces types where appropriate
-    e.g. decode an int in the stream into a float, etc.
+  - Coerce types where appropriate
+    e.g. decode an int in the stream into a float, decode numbers from formatted strings, etc
   - Corner Cases:
     Overflows, nil maps/slices, nil values in streams are handled correctly
   - Standard field renaming via tags
@@ -50,9 +49,13 @@ Rich Feature Set includes:
     (struct, slice, map, primitives, pointers, interface{}, etc)
   - Extensions to support efficient encoding/decoding of any named types
   - Support encoding.(Binary|Text)(M|Unm)arshaler interfaces
+  - Support IsZero() bool to determine if a value is a zero value.
+    Analogous to time.Time.IsZero() bool.
   - Decoding without a schema (into a interface{}).
     Includes Options to configure what specific map or slice type to use
     when decoding an encoded list or map into a nil interface{}
+  - Mapping a non-interface type to an interface, so we can decode appropriately
+    into any interface type with a correctly configured non-interface value.
   - Encode a struct as an array, and decode struct from an array in the data stream
   - Option to encode struct keys as numbers (instead of strings)
     (to support structured streams with fields encoded as numeric codes)
@@ -210,8 +213,8 @@ Struct fields matching the following are ignored during encoding and decoding
     - struct tag value set to -
     - func, complex numbers, unsafe pointers
     - unexported and not embedded
-    - unexported embedded non-struct
-    - unexported embedded pointers (from go1.10)
+    - unexported and embedded and not struct kind
+    - unexported and embedded pointers (from go1.10)
 
 Every other field in a struct will be encoded/decoded.
 
@@ -240,6 +243,8 @@ package codec
 //     and use overlay methods on *BasicHandle to call through to extHandle after initializing
 //     the "xh *extHandle" to point to a real slice.
 //
+//   - Allow mapping a concrete type to an interface, for use during decoding.
+//
 // BEFORE EACH RELEASE:
 //   - Look through and fix padding for each type, to eliminate false sharing
 //     - critical shared objects that are read many times
@@ -261,3 +266,4 @@ package codec
 //     Check this out by running: ./run.sh -z
 //     - look at those tagged ****, meaning they are not occupying full cache lines
 //     - look at those tagged <<<<, meaning they are larger than 32 words (something to watch)
+//   - Run "golint -min_confidence 0.81"

+ 8 - 5
codec/README.md

@@ -37,9 +37,8 @@ Rich Feature Set includes:
   - Careful selected use of 'unsafe' for targeted performance gains.
     100% mode exists where 'unsafe' is not used at all.
   - Lock-free (sans mutex) concurrency for scaling to 100's of cores
-  - Multiple conversions:
-    Package coerces types where appropriate 
-    e.g. decode an int in the stream into a float, etc.
+  - Coerce types where appropriate
+    e.g. decode an int in the stream into a float, decode numbers from formatted strings, etc
   - Corner Cases: 
     Overflows, nil maps/slices, nil values in streams are handled correctly
   - Standard field renaming via tags
@@ -48,9 +47,13 @@ Rich Feature Set includes:
     (struct, slice, map, primitives, pointers, interface{}, etc)
   - Extensions to support efficient encoding/decoding of any named types
   - Support encoding.(Binary|Text)(M|Unm)arshaler interfaces
+  - Support IsZero() bool to determine if a value is a zero value.
+    Analogous to time.Time.IsZero() bool.
   - Decoding without a schema (into a interface{}).
     Includes Options to configure what specific map or slice type to use
     when decoding an encoded list or map into a nil interface{}
+  - Mapping a non-interface type to an interface, so we can decode appropriately
+    into any interface type with a correctly configured non-interface value.
   - Encode a struct as an array, and decode struct from an array in the data stream
   - Option to encode struct keys as numbers (instead of strings)
     (to support structured streams with fields encoded as numeric codes)
@@ -194,8 +197,8 @@ Struct fields matching the following are ignored during encoding and decoding
   - struct tag value set to -
   - func, complex numbers, unsafe pointers
   - unexported and not embedded
-  - unexported embedded non-struct
-  - unexported embedded pointers (from go1.10)
+  - unexported and embedded and not struct kind
+  - unexported and embedded pointers (from go1.10)
 
 Every other field in a struct will be encoded/decoded.
 

+ 1 - 1
codec/binc.go

@@ -918,7 +918,7 @@ type BincHandle struct {
 	// - 0: default: library uses best judgement
 	// - 1: use symbols
 	// - 2: do not use symbols
-	AsSymbols byte
+	AsSymbols uint8
 
 	// AsSymbols: may later on introduce more options ...
 	// - m: map keys

+ 62 - 0
codec/codec_test.go

@@ -46,6 +46,24 @@ type testMbsCustStrT []testCustomStringT
 
 func (testMbsCustStrT) MapBySlice() {}
 
+type testIntfMapI interface {
+	GetIntfMapV() string
+}
+
+type testIntfMapT1 struct {
+	IntfMapV string
+}
+
+func (x *testIntfMapT1) GetIntfMapV() string { return x.IntfMapV }
+
+type testIntfMapT2 struct {
+	IntfMapV string
+}
+
+func (x testIntfMapT2) GetIntfMapV() string { return x.IntfMapV }
+
+// ----
+
 type testVerifyFlag uint8
 
 const (
@@ -2251,6 +2269,30 @@ func doTestScalars(t *testing.T, name string, h Handle) {
 	}
 }
 
+func doTestIntfMapping(t *testing.T, name string, h Handle) {
+	testOnce.Do(testInitAll)
+	rti := reflect.TypeOf((*testIntfMapI)(nil)).Elem()
+	defer func() { h.getBasicHandle().Intf2Impl(rti, nil) }()
+
+	type T9 struct {
+		I testIntfMapI
+	}
+
+	for i, v := range []testIntfMapI{
+		&testIntfMapT1{"ABC"},
+		testIntfMapT2{"DEF"},
+	} {
+		if err := h.getBasicHandle().Intf2Impl(rti, reflect.TypeOf(v)); err != nil {
+			failT(t, "Error mapping %v to %T", rti, v)
+		}
+		var v1, v2 T9
+		v1 = T9{v}
+		b := testMarshalErr(v1, h, t, name+"-enc-"+strconv.Itoa(i))
+		testUnmarshalErr(&v2, b, h, t, name+"-dec-"+strconv.Itoa(i))
+		testDeepEqualErr(v1, v2, t, name+"-dec-eq-"+strconv.Itoa(i))
+	}
+}
+
 // -----------------
 
 func TestJsonDecodeNonStringScalarInStringContext(t *testing.T) {
@@ -2888,6 +2930,26 @@ func TestSimpleScalars(t *testing.T) {
 	doTestScalars(t, "simple", testSimpleH)
 }
 
+func TestJsonIntfMapping(t *testing.T) {
+	doTestIntfMapping(t, "json", testJsonH)
+}
+
+func TestCborIntfMapping(t *testing.T) {
+	doTestIntfMapping(t, "cbor", testCborH)
+}
+
+func TestMsgpackIntfMapping(t *testing.T) {
+	doTestIntfMapping(t, "msgpack", testMsgpackH)
+}
+
+func TestBincIntfMapping(t *testing.T) {
+	doTestIntfMapping(t, "binc", testBincH)
+}
+
+func TestSimpleIntfMapping(t *testing.T) {
+	doTestIntfMapping(t, "simple", testSimpleH)
+}
+
 // TODO:
 //
 // Add Tests for:

+ 15 - 15
codec/decode.go

@@ -952,7 +952,6 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 		return
 	}
 	// We cannot decode non-nil stream value into nil interface with methods (e.g. io.Reader).
-	// if num := f.ti.rt.NumMethod(); num > 0 {
 	if f.ti.numMeth > 0 {
 		d.errorf("cannot decode non-nil codec value into nil %v (%v methods)", f.ti.rt, f.ti.numMeth)
 		return
@@ -1098,24 +1097,25 @@ func (d *Decoder) kInterface(f *codecFnInfo, rv reflect.Value) {
 
 	// every interface passed here MUST be settable.
 	var rvn reflect.Value
-	if rv.IsNil() {
-		if rvn = d.kInterfaceNaked(f); rvn.IsValid() {
-			rv.Set(rvn)
-		}
-		return
-	}
-	if d.h.InterfaceReset {
-		if rvn = d.kInterfaceNaked(f); rvn.IsValid() {
+	if rv.IsNil() || d.h.InterfaceReset {
+		// check if mapping to a type: if so, initialize it and move on
+		rvn = d.h.intf2impl(f.ti.rtid)
+		if rvn.IsValid() {
 			rv.Set(rvn)
 		} else {
-			// reset to zero value based on current type in there.
-			rv.Set(reflect.Zero(rv.Elem().Type()))
+			rvn = d.kInterfaceNaked(f)
+			if rvn.IsValid() {
+				rv.Set(rvn)
+			} else if d.h.InterfaceReset {
+				// reset to zero value based on current type in there.
+				rv.Set(reflect.Zero(rv.Elem().Type()))
+			}
+			return
 		}
-		return
+	} else {
+		// now we have a non-nil interface value, meaning it contains a type
+		rvn = rv.Elem()
 	}
-
-	// now we have a non-nil interface value, meaning it contains a type
-	rvn = rv.Elem()
 	if d.d.TryDecodeAsNil() {
 		rv.Set(reflect.Zero(rvn.Type()))
 		return

+ 6 - 10
codec/encode.go

@@ -162,10 +162,6 @@ type EncodeOptions struct {
 
 // ---------------------------------------------
 
-type simpleIoEncWriter struct {
-	io.Writer
-}
-
 // ioEncWriter implements encWriter and can write to an io.Writer implementation
 type ioEncWriter struct {
 	w  io.Writer
@@ -916,8 +912,9 @@ type Encoder struct {
 	// ---- writable fields during execution --- *try* to keep in sep cache line
 
 	// ---- cpu cache line boundary?
-	b [scratchByteArrayLen]byte                 // used for encoding a chan or (non-addressable) array of bytes
-	_ [cacheLineSize - scratchByteArrayLen]byte // padding
+	// b [scratchByteArrayLen]byte
+	// _ [cacheLineSize - scratchByteArrayLen]byte // padding
+	b [cacheLineSize - 0]byte // used for encoding a chan or (non-addressable) array of bytes
 }
 
 // NewEncoder returns an Encoder for encoding into an io.Writer.
@@ -1125,11 +1122,10 @@ func (e *Encoder) encode(iv interface{}) {
 		return
 	}
 
+	// a switch with only concrete types can be optimized.
+	// consequently, we deal with nil and interfaces outside.
+
 	switch v := iv.(type) {
-	// case nil:
-	// 	e.e.EncodeNil()
-	// case Selfer:
-	// 	v.CodecEncodeSelf(e)
 	case Raw:
 		e.rawBytes(v)
 	case reflect.Value:

+ 54 - 5
codec/helper.go

@@ -136,7 +136,9 @@ const (
 	cacheLineSize = 64
 
 	wordSizeBits = strconv.IntSize
-	wordSize     = strconv.IntSize / 8
+	wordSize     = wordSizeBits / 8
+
+	maxLevelsEmbedding = 15 // use this, so structFieldInfo fits into 8 bytes
 )
 
 var (
@@ -420,6 +422,9 @@ type BasicHandle struct {
 	TypeInfos *TypeInfos
 
 	extHandle
+
+	intf2impls
+
 	// xh *extHandle // consider making this a pointer, if needed for all handles to be comparable
 	// xha [2]extTypeTagFn // init of xh. DONT DO THIS - makes this struct bigger by len*7 words
 
@@ -658,9 +663,9 @@ func (o *extHandle) SetExt(rt reflect.Type, tag uint64, ext Ext) (err error) {
 		// all natively supported type, so cannot have an extension
 		return // TODO: should we silently ignore, or return an error???
 	}
-	if o == nil {
-		return errors.New("codec.Handle.SetExt: extHandle not initialized")
-	}
+	// if o == nil {
+	// 	return errors.New("codec.Handle.SetExt: extHandle not initialized")
+	// }
 	o2 := *o
 	// if o2 == nil {
 	// 	return errors.New("codec.Handle.SetExt: extHandle not initialized")
@@ -699,7 +704,51 @@ func (o extHandle) getExtForTag(tag uint64) *extTypeTagFn {
 	return nil
 }
 
-const maxLevelsEmbedding = 15 // use this, so structFieldInfo fits into 8 bytes
+type intf2impl struct {
+	rtid uintptr // for intf
+	impl reflect.Type
+}
+
+type intf2impls []intf2impl
+
+// Intf2Impl maps an interface to an implementing type.
+// This allows us support infering the concrete type
+// and populating it when passed an interface.
+// e.g. var v io.Reader can be decoded as a bytes.Buffer, etc.
+//
+// Passing a nil impl will clear the mapping.
+func (o *intf2impls) Intf2Impl(intf, impl reflect.Type) (err error) {
+	if impl != nil && !impl.Implements(intf) {
+		return fmt.Errorf("Intf2Impl: %v does not implement %v", impl, intf)
+	}
+	rtid := rt2id(intf)
+	o2 := *o
+	for i := range o2 {
+		v := &o2[i]
+		if v.rtid == rtid {
+			v.impl = impl
+			return
+		}
+	}
+	*o = append(o2, intf2impl{rtid, impl})
+	return
+}
+
+func (o intf2impls) intf2impl(rtid uintptr) (rv reflect.Value) {
+	for i := range o {
+		v := &o[i]
+		if v.rtid == rtid {
+			if v.impl == nil {
+				return
+			}
+			if v.impl.Kind() == reflect.Ptr {
+				return reflect.New(v.impl.Elem())
+			}
+			return reflect.New(v.impl).Elem()
+		}
+	}
+	return
+}
 
 type structFieldInfo struct {
 	encName   string // encode name

+ 23 - 20
codec/helper_not_unsafe.go

@@ -35,29 +35,10 @@ func bytesView(v string) []byte {
 
 func definitelyNil(v interface{}) bool {
 	// this is a best-effort option.
-	// We just return false, so we don't unneessarily incur the cost of reflection this early.
+	// We just return false, so we don't unnecessarily incur the cost of reflection this early.
 	return false
-	// rv := reflect.ValueOf(v)
-	// switch rv.Kind() {
-	// case reflect.Invalid:
-	// 	return true
-	// case reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Slice, reflect.Map, reflect.Func:
-	// 	return rv.IsNil()
-	// default:
-	// 	return false
-	// }
 }
 
-// // keepAlive4BytesView maintains a reference to the input parameter for bytesView.
-// //
-// // Usage: call this at point where done with the bytes view.
-// func keepAlive4BytesView(v string) {}
-
-// // keepAlive4BytesView maintains a reference to the input parameter for stringView.
-// //
-// // Usage: call this at point where done with the string view.
-// func keepAlive4StringView(v []byte) {}
-
 func rv2i(rv reflect.Value) interface{} {
 	return rv.Interface()
 }
@@ -237,3 +218,25 @@ func (e *Encoder) kUint64(f *codecFnInfo, rv reflect.Value) {
 func (e *Encoder) kUintptr(f *codecFnInfo, rv reflect.Value) {
 	e.e.EncodeUint(rv.Uint())
 }
+
+// // keepAlive4BytesView maintains a reference to the input parameter for bytesView.
+// //
+// // Usage: call this at point where done with the bytes view.
+// func keepAlive4BytesView(v string) {}
+
+// // keepAlive4BytesView maintains a reference to the input parameter for stringView.
+// //
+// // Usage: call this at point where done with the string view.
+// func keepAlive4StringView(v []byte) {}
+
+// func definitelyNil(v interface{}) bool {
+// 	rv := reflect.ValueOf(v)
+// 	switch rv.Kind() {
+// 	case reflect.Invalid:
+// 		return true
+// 	case reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Slice, reflect.Map, reflect.Func:
+// 		return rv.IsNil()
+// 	default:
+// 		return false
+// 	}
+// }

+ 58 - 41
codec/helper_unsafe.go

@@ -48,20 +48,16 @@ func stringView(v []byte) string {
 	if len(v) == 0 {
 		return ""
 	}
-
 	bx := (*unsafeSlice)(unsafe.Pointer(&v))
-	sx := unsafeString{bx.Data, bx.Len}
-	return *(*string)(unsafe.Pointer(&sx))
+	return *(*string)(unsafe.Pointer(&unsafeString{bx.Data, bx.Len}))
 }
 
 func bytesView(v string) []byte {
 	if len(v) == 0 {
 		return zeroByteSlice
 	}
-
 	sx := (*unsafeString)(unsafe.Pointer(&v))
-	bx := unsafeSlice{sx.Data, sx.Len, sx.Len}
-	return *(*[]byte)(unsafe.Pointer(&bx))
+	return *(*[]byte)(unsafe.Pointer(&unsafeSlice{sx.Data, sx.Len, sx.Len}))
 }
 
 func definitelyNil(v interface{}) bool {
@@ -78,43 +74,23 @@ func definitelyNil(v interface{}) bool {
 	//                 except it's a method value (e.g. r.Read, which implies that it is a Func)
 
 	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) {
-// 	runtime.KeepAlive(v)
-// }
-
-// func keepAlive4StringView(v []byte) {
-// 	runtime.KeepAlive(v)
-// }
-
-// 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.
 func rv2i(rv reflect.Value) interface{} {
+	// 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.
+
 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
 	// true references (map, func, chan, ptr - NOT slice) may be double-referenced as flagIndir
 	var ptr unsafe.Pointer
-	// kk := reflect.Kind(urv.flag & (1<<5 - 1))
-	// if (kk == reflect.Map || kk == reflect.Ptr || kk == reflect.Chan || kk == reflect.Func) && urv.flag&unsafeFlagIndir != 0 {
 	if refBitset.isset(byte(urv.flag&(1<<5-1))) && urv.flag&unsafeFlagIndir != 0 {
 		ptr = *(*unsafe.Pointer)(urv.ptr)
 	} else {
 		ptr = urv.ptr
 	}
 	return *(*interface{})(unsafe.Pointer(&unsafeIntf{typ: urv.typ, word: ptr}))
-	// return *(*interface{})(unsafe.Pointer(&unsafeIntf{word: *(*unsafe.Pointer)(urv.ptr), typ: urv.typ}))
-	// return *(*interface{})(unsafe.Pointer(&unsafeIntf{word: urv.ptr, typ: urv.typ}))
 }
 
 func rt2id(rt reflect.Type) uintptr {
@@ -129,14 +105,8 @@ 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
-// 	uv := unsafeReflectValue{ut.word, &zeroRTv, flag(rt.Kind())}
-// 	return *(*reflect.Value)(unsafe.Pointer(&uv})
-// }
-
 // --------------------------
+
 type atomicTypeInfoSlice struct { // expected to be 2 words
 	v unsafe.Pointer
 	_ [8]byte // padding
@@ -153,9 +123,6 @@ func (x *atomicTypeInfoSlice) store(p *[]rtid2ti) {
 // --------------------------
 func (d *Decoder) raw(f *codecFnInfo, rv reflect.Value) {
 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
-	// if urv.flag&unsafeFlagIndir != 0 {
-	// 	urv.ptr = *(*unsafe.Pointer)(urv.ptr)
-	// }
 	*(*[]byte)(urv.ptr) = d.rawBytes()
 }
 
@@ -327,6 +294,56 @@ func (e *Encoder) kUintptr(f *codecFnInfo, rv reflect.Value) {
 
 // ------------
 
+// func (d *Decoder) raw(f *codecFnInfo, rv reflect.Value) {
+// 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
+// 	// if urv.flag&unsafeFlagIndir != 0 {
+// 	// 	urv.ptr = *(*unsafe.Pointer)(urv.ptr)
+// 	// }
+// 	*(*[]byte)(urv.ptr) = d.rawBytes()
+// }
+
+// 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
+// 	uv := unsafeReflectValue{ut.word, &zeroRTv, flag(rt.Kind())}
+// 	return *(*reflect.Value)(unsafe.Pointer(&uv})
+// }
+
+// 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
+// 	var ptr unsafe.Pointer
+// 	// kk := reflect.Kind(urv.flag & (1<<5 - 1))
+// 	// if (kk == reflect.Map || kk == reflect.Ptr || kk == reflect.Chan || kk == reflect.Func) && urv.flag&unsafeFlagIndir != 0 {
+// 	if refBitset.isset(byte(urv.flag&(1<<5-1))) && urv.flag&unsafeFlagIndir != 0 {
+// 		ptr = *(*unsafe.Pointer)(urv.ptr)
+// 	} else {
+// 		ptr = urv.ptr
+// 	}
+// 	return *(*interface{})(unsafe.Pointer(&unsafeIntf{typ: urv.typ, word: ptr}))
+// 	// return *(*interface{})(unsafe.Pointer(&unsafeIntf{word: *(*unsafe.Pointer)(urv.ptr), typ: urv.typ}))
+// 	// return *(*interface{})(unsafe.Pointer(&unsafeIntf{word: urv.ptr, typ: urv.typ}))
+// }
+
+// func definitelyNil(v interface{}) bool {
+// 	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) {
+// 	runtime.KeepAlive(v)
+// }
+
+// func keepAlive4StringView(v []byte) {
+// 	runtime.KeepAlive(v)
+// }
+
 // func rt2id(rt reflect.Type) uintptr {
 // 	return uintptr(((*unsafeIntf)(unsafe.Pointer(&rt))).word)
 // 	// var i interface{} = rt

+ 1 - 0
codec/rpc.go

@@ -17,6 +17,7 @@ type Rpc interface {
 	ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec
 }
 
+// RPCOptions holds options specific to rpc functionality
 type RPCOptions struct {
 	// RPCNoBuffer configures whether we attempt to buffer reads and writes during RPC calls.
 	//

+ 6 - 0
codec/z_all_test.go

@@ -229,6 +229,11 @@ func testCodecGroup(t *testing.T) {
 	t.Run("TestMsgpackScalars", TestMsgpackScalars)
 	t.Run("TestBincScalars", TestBincScalars)
 	t.Run("TestSimpleScalars", TestSimpleScalars)
+	t.Run("TestJsonIntfMapping", TestJsonIntfMapping)
+	t.Run("TestCborIntfMapping", TestCborIntfMapping)
+	t.Run("TestMsgpackIntfMapping", TestMsgpackIntfMapping)
+	t.Run("TestBincIntfMapping", TestBincIntfMapping)
+	t.Run("TestSimpleIntfMapping", TestSimpleIntfMapping)
 
 	t.Run("TestJsonInvalidUnicode", TestJsonInvalidUnicode)
 	t.Run("TestCborHalfFloat", TestCborHalfFloat)
@@ -260,6 +265,7 @@ func testJsonGroup(t *testing.T) {
 	t.Run("TestJsonUintToInt", TestJsonUintToInt)
 	t.Run("TestJsonDifferentMapOrSliceType", TestJsonDifferentMapOrSliceType)
 	t.Run("TestJsonScalars", TestJsonScalars)
+	t.Run("TestJsonIntfMapping", TestJsonIntfMapping)
 }
 
 func testBincGroup(t *testing.T) {