Browse Source

codecgen: support extensions.

Previously, extensions were not supported in codecgen.
However, for completeness and to position codecgen as full replacement option,
we must support it, and try to make it as performant as possible.

This rounds up making sure that codecgen supports the same sequences that
the reflection-based encoding/decoding takes. The only missing feature is
support for Canonical=true option.

Updates #95
Ugorji Nwoke 10 years ago
parent
commit
e2517eda87
3 changed files with 96 additions and 10 deletions
  1. 39 1
      codec/gen-helper.generated.go
  2. 35 1
      codec/gen-helper.go.tmpl
  3. 22 8
      codec/gen.go

+ 39 - 1
codec/gen-helper.generated.go

@@ -10,7 +10,10 @@
 
 package codec
 
-import "encoding"
+import (
+	"encoding"
+	"reflect"
+)
 
 // This file is used to generate helper code for codecgen.
 // The values here i.e. genHelper(En|De)coder are not to be used directly by
@@ -88,6 +91,25 @@ func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
 	return 0
 }
 
+// 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 := reflect.ValueOf(rt).Pointer()
+	if xfFn := f.e.h.getExt(rtid); xfFn != nil {
+		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
+		return true
+	}
+	return false
+}
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
 	return f.d.h
@@ -160,3 +182,19 @@ func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
 	}
 	return 0
 }
+
+// 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 := reflect.ValueOf(rt).Pointer()
+	if xfFn := f.d.h.getExt(rtid); xfFn != nil {
+		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
+		return true
+	}
+	return false
+}

+ 35 - 1
codec/gen-helper.go.tmpl

@@ -10,7 +10,10 @@
 
 package codec
 
-import "encoding"
+import (
+	"encoding"
+	"reflect"
+)
 
 // This file is used to generate helper code for codecgen. 
 // The values here i.e. genHelper(En|De)coder are not to be used directly by 
@@ -82,6 +85,23 @@ func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
 	}
 	return 0
 }
+// 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 := reflect.ValueOf(rt).Pointer()
+	if xfFn := f.e.h.getExt(rtid); xfFn != nil {
+		f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
+		return true
+	}
+	return false 
+}
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
@@ -144,6 +164,20 @@ func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
 	}
 	return 0
 }
+// 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 := reflect.ValueOf(rt).Pointer()
+	if xfFn := f.d.h.getExt(rtid); xfFn != nil {
+		f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
+		return true
+	}
+	return false 
+}
 
 {{/*
 

+ 22 - 8
codec/gen.go

@@ -22,20 +22,25 @@ import (
 )
 
 // ---------------------------------------------------
-// codecgen MOSTLY supports things that are static.
-// This means that, for dynamic things, we MUST use reflection to at least get the reflect.Type.
+// codecgen supports the full cycle of reflection-based codec:
+//    - RawExt
+//    - Builtins
+//    - Extensions
+//    - (Binary|Text|JSON)(Unm|M)arshal
+//    - generic by-kind
 //
-// Currently, codecgen doesn't support the following:
-//   - extensions
+// This means that, for dynamic things, we MUST use reflection to at least get the reflect.Type.
+// In those areas, we try to only do reflection or interface-conversion when NECESSARY:
+//    - Extensions, only if Extensions are configured.
 //
-// In addition, codecgen doesn't support the following:
+// However, codecgen doesn't support the following:
 //   - Canonical option. (codecgen IGNORES it currently)
 //     This is just because it has not been implemented.
 //
 // During encode/decode, Selfer takes precedence.
 // A type implementing Selfer will know how to encode/decode itself statically.
 //
-//  The following field types are supported:
+// The following field types are supported:
 //     array: [n]T
 //     slice: []T
 //     map: map[K]V
@@ -607,7 +612,11 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
 		x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
 		x.linef("r.EncodeBuiltin(%s, %s)", vrtid, varname)
 	}
-
+	// only check for extensions if the type is named, and has a packagePath.
+	if t.PkgPath() != "" && t.Name() != "" {
+		// first check if extensions are configued, before doing the interface conversion
+		x.linef("} else if z.HasExtensions() && z.EncExt(%s) {", varname)
+	}
 	if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) {
 		x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname)
 	}
@@ -1010,7 +1019,7 @@ func (x *genRunner) decVar(varname string, t reflect.Type, canBeNil bool) {
 func (x *genRunner) dec(varname string, t reflect.Type) {
 	// assumptions:
 	//   - the varname is to a pointer already. No need to take address of it
-
+	//   - t is always a baseType T (not a *T, etc).
 	rtid := reflect.ValueOf(t).Pointer()
 	tptr := reflect.PtrTo(t)
 	if t.Implements(selferTyp) || (t.Kind() == reflect.Struct &&
@@ -1062,6 +1071,11 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 		x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
 		x.linef("r.DecodeBuiltin(%s, %s)", vrtid, varname)
 	}
+	// only check for extensions if the type is named, and has a packagePath.
+	if t.PkgPath() != "" && t.Name() != "" {
+		// first check if extensions are configued, before doing the interface conversion
+		x.linef("} else if z.HasExtensions() && z.DecExt(%s) {", varname)
+	}
 
 	if t.Implements(binaryUnmarshalerTyp) || tptr.Implements(binaryUnmarshalerTyp) {
 		x.linef("} else if %sm%s { z.DecBinaryUnmarshal(%v) ", genTempVarPfx, mi, varname)