浏览代码

codec: streamline and optimize fast-path functions

    Since we know statically whether the array or slice or map can be changed,
    we can dispatch to an appropriate function directly.

    This reduces function overhead and extra work done in each loop.
Ugorji Nwoke 6 年之前
父节点
当前提交
79e5e7c841
共有 2 个文件被更改,包括 475 次插入659 次删除
  1. 361 521
      codec/fast-path.generated.go
  2. 114 138
      codec/fast-path.go.tmpl

文件差异内容过多而无法显示
+ 361 - 521
codec/fast-path.generated.go


+ 114 - 138
codec/fast-path.go.tmpl

@@ -95,11 +95,13 @@ func init() {
 		i++
 	}
 	{{/* do not register []uint8 in fast-path */}}
-	{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
-	fn([]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}{{end}}
+	{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8" -}}
+	fn([]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R)
+	{{end}}{{end}}{{end}}{{end}}
 	
-	{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
-	fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
+	{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
+	fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R)
+	{{end}}{{end}}{{end}}
 	
 	sort.Sort(fastpathAslice(fastpathAV[:]))
 }
@@ -109,60 +111,20 @@ func init() {
 // -- -- fast path type switch
 func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
 	switch v := iv.(type) {
-
-{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
-	case []{{ .Elem }}:
-		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
-	case *[]{{ .Elem }}:
-		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e){{/*
-*/}}{{end}}{{end}}{{end}}{{end}}
-
-{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
-	case map[{{ .MapKey }}]{{ .Elem }}:
-		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
-	case *map[{{ .MapKey }}]{{ .Elem }}:
-		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e){{/*
-*/}}{{end}}{{end}}{{end}}
-
-	default:
-		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
-		return false
-	}
-	return true
-}
-
-{{/*
-**** removing this block, as they are never called directly ****
-
-
-
-**** removing this block, as they are never called directly ****
-
-
-
-func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
-	switch v := iv.(type) {
-{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8" -}}
 	case []{{ .Elem }}:
 		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
 	case *[]{{ .Elem }}:
 		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e)
-{{end}}{{end}}{{end}}
-	default:
-		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
-		return false
-	}
-	return true
-}
+{{end}}{{end}}{{end}}{{end -}}
 
-func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
-	switch v := iv.(type) {
-{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
 	case map[{{ .MapKey }}]{{ .Elem }}:
 		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
 	case *map[{{ .MapKey }}]{{ .Elem }}:
 		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e)
-{{end}}{{end}}{{end}}
+{{end}}{{end}}{{end -}}
+
 	default:
 		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
@@ -170,17 +132,8 @@ func fastpathEncodeTypeSwitchMap(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 ****
-*/}}
-
 // -- -- fast path functions
-{{range .Values}}{{if not .Primitive}}{{if not .MapKey }} 
+{{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)
@@ -218,9 +171,9 @@ func (fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Enco
 		e.mapEnd()
 	}
 }
-{{end}}{{end}}{{end}}
+{{end}}{{end}}{{end -}}
 
-{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+{{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)
 }
@@ -271,40 +224,36 @@ func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem
 	}
 	e.mapEnd()
 }
-{{end}}{{end}}{{end}}
+{{end}}{{end}}{{end -}}
 
 // -- decode
 
 // -- -- fast path type switch
 func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
-	 var changed bool
+	var changed bool
+	var containerLen int
 	switch v := iv.(type) {
-{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8" -}}
 	case []{{ .Elem }}:
-		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)
-		}
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}N(v, d)
 	case *[]{{ .Elem }}:
 		var v2 []{{ .Elem }}
-		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
-		if changed {
+		if v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*v, d); changed {
 			*v = v2 
-		}{{/*
-*/}}{{end}}{{end}}{{end}}{{end}}
+		}
+{{end}}{{end}}{{end}}{{end -}}
 {{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)
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}L(v, d.mapStart(), d)
 	case *map[{{ .MapKey }}]{{ .Elem }}:
-		 var v2 map[{{ .MapKey }}]{{ .Elem }}
-		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
-		if changed {
-			*v = v2 
-		}{{/*
-*/}}{{end}}{{end}}{{end}}
+		containerLen = d.mapStart()
+		if *v == nil {
+			*v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+		}
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*v, containerLen, d)
+{{end}}{{end}}{{end -}}
 	default:
 		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
@@ -314,14 +263,14 @@ func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
 
 func fastpathDecodeSetZeroTypeSwitch(iv interface{}) bool {
 	switch v := iv.(type) {
-{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey -}}
 	case *[]{{ .Elem }}: 
-		*v = nil {{/*
-*/}}{{end}}{{end}}{{end}}
-{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+		*v = nil
+{{end}}{{end}}{{end}}
+{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
 	case *map[{{ .MapKey }}]{{ .Elem }}: 
-		*v = nil {{/*
-*/}}{{end}}{{end}}{{end}}
+		*v = nil 
+{{end}}{{end}}{{end}}
 	default:
 		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
@@ -330,44 +279,37 @@ func fastpathDecodeSetZeroTypeSwitch(iv interface{}) bool {
 }
 
 // -- -- fast path functions
-{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey -}}
 {{/*
-Slices can change if they 
+Slices can change if they
 - did not come from an array
 - 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) {
-	if array := f.seq == seqTypeArray; !array && rv.Kind() == reflect.Ptr {
+	if f.seq != seqTypeArray && rv.Kind() == reflect.Ptr {
 		vp := rv2i(rv).(*[]{{ .Elem }})
-		if v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, !array, d); changed { *vp = v }
+		if v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*vp, d); changed { *vp = v }
 	} else {
-		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)
-		}
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}N(rv2i(rv).([]{{ .Elem }}), d)
 	}
 }
 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 }
+	if v, changed := f.{{ .MethodNamePfx "Dec" false }}Y(*vp, d); changed { *vp = v }
 }
-func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
+func (fastpathT) {{ .MethodNamePfx "Dec" false }}Y(v []{{ .Elem }}, d *Decoder) (_ []{{ .Elem }}, changed bool) {
 	{{/* dd := d.d
 		// if d.d.isContainerType(valueTypeNil) { d.d.TryDecodeAsNil() }
 	 */ -}}
 	slh, containerLenS := d.decSliceHelperStart()
 	if containerLenS == 0 {
-		if canChange {
-			if v == nil { v = []{{ .Elem }}{} } else if len(v) != 0 { v = v[:0] }
-			changed = true
-		}
+		if v == nil { v = []{{ .Elem }}{} } else if len(v) != 0 { v = v[:0] }
 		slh.End()
-		return v, changed
+		return v, true
 	}
 	hasLen := containerLenS > 0
 	var xlen int 
-	if hasLen && canChange {
+	if hasLen {
 		if containerLenS > cap(v) {
 			xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
 			if xlen <= cap(v) {
@@ -383,7 +325,7 @@ func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bo
 	}
 	var j int
 	for j = 0; (hasLen && j < containerLenS) || !(hasLen || d.d.CheckBreak()); j++ {
-		if j == 0 && len(v) == 0 && canChange {
+		if j == 0 && len(v) == 0 {
 			if hasLen {
 				xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
 			} else {
@@ -393,69 +335,104 @@ func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bo
 			changed = true 
 		}
 		{{/* // if indefinite, etc, then expand the slice if necessary */ -}}
-		var decodeIntoBlank bool
 		if j >= len(v) {
-			if canChange {
-				v = append(v, {{ zerocmd .Elem }})
-				changed = true
-			} else {
-				d.arrayCannotExpand(len(v), j+1)
-				decodeIntoBlank = true
-			}
+			v = append(v, {{ zerocmd .Elem }})
+			changed = true
 		} 
 		slh.ElemContainerState(j)
-		if decodeIntoBlank {
-			d.swallow()
-		} else if d.d.TryDecodeAsNil() {
+		if d.d.TryDecodeAsNil() {
 			v[uint(j)] = {{ zerocmd .Elem }}
 		} else {
 			{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem }}{{ end }}
 		}
 	}
-	if canChange {
-		if j < len(v) {
-			v = v[:uint(j)]
-			changed = true
-		} else if j == 0 && v == nil {
-			v = make([]{{ .Elem }}, 0)
-			changed = true
-		}
+	if j < len(v) {
+		v = v[:uint(j)]
+		changed = true
+	} else if j == 0 && v == nil {
+		v = []{{ .Elem }}{}
+		changed = true
 	}
 	slh.End()
 	return v, changed 
 }
-{{end}}{{end}}{{end}}
+func (fastpathT) {{ .MethodNamePfx "Dec" false }}N(v []{{ .Elem }}, d *Decoder) {
+	{{/* dd := d.d
+		// if d.d.isContainerType(valueTypeNil) { d.d.TryDecodeAsNil() }
+	 */ -}}
+	slh, containerLenS := d.decSliceHelperStart()
+	if containerLenS == 0 {
+		slh.End()
+		return
+	}
+	hasLen := containerLenS > 0
+	for j := 0; (hasLen && j < containerLenS) || !(hasLen || d.d.CheckBreak()); j++ {
+		{{/* // if indefinite, etc, then expand the slice if necessary */ -}}
+		if j >= len(v) {
+			d.arrayCannotExpand(len(v), j+1)
+			slh.ElemContainerState(j)
+			d.swallow()
+			for ; (hasLen && j < containerLenS) || !(hasLen || d.d.CheckBreak()); j++ {
+				slh.ElemContainerState(j)
+				d.swallow()
+			}
+			break
+		} 
+		slh.ElemContainerState(j)
+		if d.d.TryDecodeAsNil() {
+			v[uint(j)] = {{ zerocmd .Elem }}
+		} else {
+			{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem }}{{ end }}
+		}
+	}
+	slh.End()
+}
+{{end}}{{end}}{{end -}}
 
-{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
 {{/*
 Maps can change if they are
 - addressable (from a ptr)
 - settable (e.g. contained in an interface{})
-*/}}
+*/ -}}
 func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) {
+    {{/* // if d.d.isContainerType(valueTypeNil) {d.d.TryDecodeAsNil()	*/ -}}
+	containerLen := d.mapStart()
 	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 }
+		if *vp == nil {
+			*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+		}
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
 	} else {
-		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), false, d)
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}L(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), containerLen, 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 }
+	containerLen := d.mapStart()
+	if *vp == nil {
+		*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+	}
+	f.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
 }
-func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, canChange bool, 
-	d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
-    {{/*
-		// if d.d.isContainerType(valueTypeNil) {d.d.TryDecodeAsNil()
-	*/ -}}
+{{/*
+func (f fastpathT) {{ .MethodNamePfx "Dec" false }}Y(v map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
 	containerLen := d.mapStart()
-	if canChange && v == nil {
+	if v == nil {
 		v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
 		changed = true
 	}
+	f.{{ .MethodNamePfx "Dec" false }}L(v, containerLen, d)
+	return v, changed
+}
+func (f fastpathT) {{ .MethodNamePfx "Dec" false }}N(v map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) {
+	f.{{ .MethodNamePfx "Dec" false }}L(v, d.mapStart(), d)
+}
+*/ -}}
+func (fastpathT) {{ .MethodNamePfx "Dec" false }}L(v map[{{ .MapKey }}]{{ .Elem }}, containerLen int, d *Decoder) {
 	if containerLen == 0 {
 		d.mapEnd()
-		return v, changed
+		return
 	}
 	{{if eq .Elem "interface{}" }}mapGet := v != nil && !d.h.MapValueReset && !d.h.InterfaceReset
     {{else if eq .Elem "bytes" "[]byte" }}mapGet := v != nil && !d.h.MapValueReset
@@ -488,6 +465,5 @@ func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem
 		if v != nil { v[mk] = mv }
 	}
 	d.mapEnd()
-	return v, changed
 }
 {{end}}{{end}}{{end}}

部分文件因为文件数量过多而无法显示