Ver código fonte

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 anos atrás
pai
commit
79e5e7c841
2 arquivos alterados com 475 adições e 659 exclusões
  1. 361 521
      codec/fast-path.generated.go
  2. 114 138
      codec/fast-path.go.tmpl

Diferenças do arquivo suprimidas por serem muito extensas
+ 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}}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff