Browse Source

codec: fast-path: ensure we decode fully and copy slice contents if possible, but never modify slice if nil and canChange=false

Also, if a pointer to the slice is not given, copy the decoded bytes as much as possible into slice passed.
Ugorji Nwoke 8 years ago
parent
commit
ef9d3323cc
3 changed files with 953 additions and 389 deletions
  1. 1 1
      codec/decode.go
  2. 896 309
      codec/fast-path.generated.go
  3. 56 79
      codec/fast-path.go.tmpl

+ 1 - 1
codec/decode.go

@@ -1224,7 +1224,7 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 			if !(len(bs2) > 0 && len(bs2) == len(rvbs) && &bs2[0] == &rvbs[0]) {
 				if rv.CanSet() {
 					rv.SetBytes(bs2)
-				} else {
+				} else if len(rvbs) > 0 && len(bs2) > 0 {
 					copy(rvbs, bs2)
 				}
 			}

File diff suppressed because it is too large
+ 896 - 309
codec/fast-path.generated.go


+ 56 - 79
codec/fast-path.go.tmpl

@@ -15,19 +15,19 @@ package codec
 // This file can be omitted without causing a build failure.
 //
 // The advantage of fast paths is:
-//    - Many calls bypass reflection altogether
+//	  - Many calls bypass reflection altogether
 // 
 // Currently support
-//    - slice of all builtin types,
-//    - map of all builtin types to string or interface value
-//    - symmetrical maps of all builtin types (e.g. str-str, uint8-uint8)
+//	  - slice of all builtin types,
+//	  - map of all builtin types to string or interface value
+//	  - symmetrical maps of all builtin types (e.g. str-str, uint8-uint8)
 // This should provide adequate "typical" implementations.
 // 
 // Note that fast track decode functions must handle values for which an address cannot be obtained.
 // For example: 
-//   m2 := map[string]int{}
-//   p2 := []interface{}{m2}
-//   // decoding into p2 will bomb if fast track functions do not treat like unaddressable.
+//	 m2 := map[string]int{}
+//	 p2 := []interface{}{m2}
+//	 // decoding into p2 will bomb if fast track functions do not treat like unaddressable.
 // 
 
 import (
@@ -124,25 +124,12 @@ func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
 	return true
 }
 
-{{/* **** removing this block, as they are never called directly ****
-
-
-
-
-
-
-
-
-
-
-
-
+{{/*
 **** removing this block, as they are never called directly ****
 
 
 
-
-
+**** removing this block, as they are never called directly ****
 
 
 
@@ -178,24 +165,15 @@ func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
 
 
 
-
-
-
-
 **** removing this block, as they are never called directly ****
 
 
 
-
-
-
-
-
+**** removing this block, as they are never called directly ****
 */}}
 
 // -- -- fast path functions
 {{range .Values}}{{if not .Primitive}}{{if not .MapKey }} 
-
 func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) {
 	if f.ti.mbs {
 		fastpathTV.{{ .MethodNamePfx "EncAsMap" false }}V(rv2i(rv).([]{{ .Elem }}), e)
@@ -213,7 +191,6 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *Encoder
 	}
 	ee.WriteArrayEnd()
 }
-
 func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) {
 	ee, esep := e.e, e.hh.hasElemSeparators()
 	if len(v)%2 == 1 {
@@ -233,11 +210,9 @@ func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *En
 	}
 	ee.WriteMapEnd()
 }
-
 {{end}}{{end}}{{end}}
 
 {{range .Values}}{{if not .Primitive}}{{if .MapKey }}
-
 func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) {
 	fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), e)
 }
@@ -298,27 +273,37 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
 	}
 	ee.WriteMapEnd()
 }
-
 {{end}}{{end}}{{end}}
 
 // -- decode
 
 // -- -- fast path type switch
 func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
+     var changed bool
 	switch v := iv.(type) {
 {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
 	case []{{ .Elem }}:
-		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, false, d)
+        var v2 []{{ .Elem }}
+		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, false, d)
+        if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
+			copy(v, v2)
+		}
 	case *[]{{ .Elem }}:
-		if v2, changed2 := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d); changed2 {
+        var v2 []{{ .Elem }}
+		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
+        if changed {
 			*v = v2 
 		}{{/*
 */}}{{end}}{{end}}{{end}}{{end}}
-{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+{{range .Values}}{{if not .Primitive}}{{if .MapKey }}{{/*
+// maps only change if nil, and in that case, there's no point copying
+*/}}
 	case map[{{ .MapKey }}]{{ .Elem }}:
 		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, false, d)
 	case *map[{{ .MapKey }}]{{ .Elem }}:
-		if v2, changed2 := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d); changed2 {
+         var v2 map[{{ .MapKey }}]{{ .Elem }}
+		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
+        if changed {
 			*v = v2 
 		}{{/*
 */}}{{end}}{{end}}{{end}}
@@ -354,38 +339,36 @@ Slices can change if they
 - are addressable (from a ptr)
 - are settable (e.g. contained in an interface{})
 */}}
-func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) { 
+func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) {
 	if array := f.seq == seqTypeArray; !array && rv.Kind() == reflect.Ptr {
-		var vp = rv2i(rv).(*[]{{ .Elem }})
-		if v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, !array, d); changed {
-				*vp = v
-		}
+		vp := rv2i(rv).(*[]{{ .Elem }})
+		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, !array, d)
+        if changed { *vp = v }
 	} else {
-		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(rv2i(rv).([]{{ .Elem }}), !array, d)
+		v := rv2i(rv).([]{{ .Elem }})
+        v2, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, !array, d)
+        if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
+           copy(v, v2)
+        }
 	}
 }
 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, d *Decoder) {
-	if v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d); changed {
-		*vp = v 
-	}
+	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
+    if changed { *vp = v }
 }
 func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
-	dd := d.d
-	{{/* // if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil() */}}
+	dd := d.d{{/*
+	    // if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil()
+    */}}
 	slh, containerLenS := d.decSliceHelperStart()
 	if containerLenS == 0 {
 		if canChange {
-			if v == nil { 
-				v = []{{ .Elem }}{}
-			} else if len(v) != 0 {
-				v = v[:0]
-			}
+			if v == nil { v = []{{ .Elem }}{} } else if len(v) != 0 { v = v[:0] }
 			changed = true
 		}
 		slh.End()
 		return v, changed
 	}
-
 	hasLen := containerLenS > 0
 	var xlen int 
 	if hasLen && canChange {
@@ -404,7 +387,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 	}
 	j := 0
 	for ; (hasLen && j < containerLenS) || !(hasLen || dd.CheckBreak()); j++ {
-		if j == 0 && len(v) == 0 {
+		if j == 0 && len(v) == 0 && canChange {
 			if hasLen {
 				xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
 			} else {
@@ -423,7 +406,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 				d.arrayCannotExpand(len(v), j+1)
 				decodeIntoBlank = true
 			}
-		}
+		} 
 		slh.ElemContainerState(j)
 		if decodeIntoBlank {
 			d.swallow()
@@ -445,10 +428,8 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 	slh.End()
 	return v, changed 
 }
-
 {{end}}{{end}}{{end}}
 
-
 {{range .Values}}{{if not .Primitive}}{{if .MapKey }}
 {{/*
 Maps can change if they are
@@ -458,22 +439,21 @@ Maps can change if they are
 func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) {
 	if rv.Kind() == reflect.Ptr {
 		vp := rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})
-		if v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d); changed {
-			*vp = v
-		}
-		return
-	}
-	fastpathTV.{{ .MethodNamePfx "Dec" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), false, d)
+		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d);
+		if changed { *vp = v }
+	} else {
+	    fastpathTV.{{ .MethodNamePfx "Dec" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), false, d)
+    }
 }
 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) {
-	if v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d); changed {
-		*vp = v 
-	}
+	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
+	if changed { *vp = v }
 }
 func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, canChange bool, 
 	d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
-	dd, esep := d.d, d.hh.hasElemSeparators()
-	{{/* // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil() */}}
+	dd, esep := d.d, d.hh.hasElemSeparators(){{/*
+	    // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil()
+    */}}
 	containerLen := dd.ReadMapStart()
 	if canChange && v == nil {
 		xlen := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
@@ -484,8 +464,8 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		dd.ReadMapEnd()
 		return v, changed
 	}
-	{{ if eq .Elem "interface{}" }}mapGet := !d.h.MapValueReset && !d.h.InterfaceReset{{end}}
-	var mk {{ .MapKey }}
+	{{ if eq .Elem "interface{}" }}mapGet := v != nil && !d.h.MapValueReset && !d.h.InterfaceReset
+    {{end}}var mk {{ .MapKey }}
 	var mv {{ .Elem }}
 	hasLen := containerLen > 0
 	for j := 0; (hasLen && j < containerLen) || !(hasLen || dd.CheckBreak()); j++ {
@@ -497,17 +477,14 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
 		if esep { dd.ReadMapElemValue() }
 		if dd.TryDecodeAsNil() {
-			if d.h.DeleteOnNilMapValue { delete(v, mk) } else { v[mk] = {{ zerocmd .Elem }} }
+			if v == nil {} else if d.h.DeleteOnNilMapValue { delete(v, mk) } else { v[mk] = {{ zerocmd .Elem }} }
 			continue 
 		}
 		{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
 		d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
-		if v != nil {
-			v[mk] = mv
-		}
+		if v != nil { v[mk] = mv }
 	}
 	dd.ReadMapEnd()
 	return v, changed
 }
-
 {{end}}{{end}}{{end}}

Some files were not shown because too many files changed in this diff