Jelajahi Sumber

codec: fix decoding into array with length smaller than stream

Also, streamline fastpath decoding into maps.
Ugorji Nwoke 6 tahun lalu
induk
melakukan
6c2e17b36f
3 mengubah file dengan 546 tambahan dan 791 penghapusan
  1. 31 36
      codec/decode.go
  2. 462 726
      codec/fast-path.generated.go
  3. 53 29
      codec/fast-path.go.tmpl

+ 31 - 36
codec/decode.go

@@ -770,52 +770,47 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 			}
 		}
 		slh.ElemContainerState(j)
-		// var decodeAsNil = d.d.TryDecodeAsNil()
 		// if indefinite, etc, then expand the slice if necessary
-		var decodeIntoBlank bool
 		if j >= rvlen {
 			if f.seq == seqTypeArray {
 				d.arrayCannotExpand(rvlen, j+1)
-				decodeIntoBlank = true
-			} else { // if f.seq == seqTypeSlice
-				// rv = reflect.Append(rv, reflect.Zero(rtelem0)) // append logic + varargs
-				var rvcap2 int
-				var rvErrmsg2 string
-				rv9, rvcap2, rvChanged, rvErrmsg2 =
-					expandSliceRV(rv, f.ti.rt, rvCanset, rtelem0Size, 1, rvlen, rvcap)
-				if rvErrmsg2 != "" {
-					d.errorf(rvErrmsg2)
-				}
-				rvlen++
-				if rvChanged {
-					rv = rv9
-					rvcap = rvcap2
+				// drain completely and return
+				d.swallow()
+				j++
+				for ; (hasLen && j < containerLenS) || !(hasLen || d.d.CheckBreak()); j++ {
+					slh.ElemContainerState(j)
+					d.swallow()
 				}
+				slh.End()
+				return
+			}
+			// rv = reflect.Append(rv, reflect.Zero(rtelem0)) // append logic + varargs
+			var rvcap2 int
+			var rvErrmsg2 string
+			rv9, rvcap2, rvChanged, rvErrmsg2 =
+				expandSliceRV(rv, f.ti.rt, rvCanset, rtelem0Size, 1, rvlen, rvcap)
+			if rvErrmsg2 != "" {
+				d.errorf(rvErrmsg2)
+			}
+			rvlen++
+			if rvChanged {
+				rv = rv9
+				rvcap = rvcap2
 			}
 		}
-		if decodeIntoBlank {
-			// if !decodeAsNil {
-			// 	d.swallow()
-			// }
-			d.swallow()
-		} else {
-			rv9 = rv.Index(j)
-			if d.h.SliceElementReset { // || decodeAsNil {
-				if !rtelem0ZeroValid {
-					rtelem0ZeroValid = true
-					rtelem0Zero = reflect.Zero(rtelem0)
-				}
-				rv9.Set(rtelem0Zero)
-				// if decodeAsNil {
-				// 	continue
-				// }
+		rv9 = rv.Index(j)
+		if d.h.SliceElementReset {
+			if !rtelem0ZeroValid {
+				rtelem0ZeroValid = true
+				rtelem0Zero = reflect.Zero(rtelem0)
 			}
+			rv9.Set(rtelem0Zero)
+		}
 
-			if fn == nil {
-				fn = d.h.fn(rtelem)
-			}
-			d.decodeValue(rv9, fn)
+		if fn == nil {
+			fn = d.h.fn(rtelem)
 		}
+		d.decodeValue(rv9, fn)
 	}
 	if j < rvlen {
 		if rv.CanSet() {

File diff ditekan karena terlalu besar
+ 462 - 726
codec/fast-path.generated.go


+ 53 - 29
codec/fast-path.go.tmpl

@@ -44,11 +44,9 @@ DecSliceIntfX  (called by codecgen) (calls Y below)
 DecSliceIntfY  (delegate when slice CAN be updated)
 DecSliceIntfN  (delegate when slice CANNOT be updated e.g. from array or non-addressable slice)
 
-fastpathDecMap...R (called by fastpath...switch) (calls L below)
+fastpathDecMap...R (called by fastpath...switch) (calls L or X? below)
 DecMap...X  (called by codecgen)
 DecMap...L  (delegated to by both above)
-    (update: let both handle when containerLen == 0 or decContainerLenNil)
-    (L doesn't do mapStart or mapEnd - just the meat)
 */ -}}
 
 import (
@@ -266,26 +264,36 @@ func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
 	case *[]{{ .Elem }}:
 		var v2 []{{ .Elem }}
 		if v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*v, d); changed {
-			*v = v2 
+			*v = v2
 		}
 {{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 }}:
-		if containerLen = d.mapStart(); containerLen != decContainerLenNil {
-			fastpathTV.{{ .MethodNamePfx "Dec" false }}L(v, containerLen, d)
+		containerLen = d.mapStart()
+		if containerLen != decContainerLenNil {
+			if containerLen != 0 {
+				fastpathTV.{{ .MethodNamePfx "Dec" false }}L(v, containerLen, d)
+			}
+			d.mapEnd()
 		}
 	case *map[{{ .MapKey }}]{{ .Elem }}:
+		{{/*
 		containerLen = d.mapStart()
-		if containerLen == decContainerLenNil {
+		if containerLen == 0 {
+			d.mapEnd()
+		} else if containerLen == decContainerLenNil {
 			*v = nil
-			break
-		}
-		if *v == nil {
-			*v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+		} else {
+			if *v == nil {
+				*v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+			}
+			fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*v, containerLen, d)
 		}
-		fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*v, containerLen, d)
+		// consider delegating fully to X - encoding *map is uncommon, so ok to pay small function call cost
+		*/ -}}
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}X(v, d)
 {{end}}{{end}}{{end -}}
 	default:
 		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
@@ -320,6 +328,7 @@ Slices can change if they
 - are settable (e.g. contained in an interface{})
 */}}
 func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) {
+	{{/* xdebugf("{{ .MethodNamePfx "fastpathDec" false }}R: f.seq: %v", f.seq) */ -}}
 	if f.seq != seqTypeArray && rv.Kind() == reflect.Ptr {
 		vp := rv2i(rv).(*[]{{ .Elem }})
 		if v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*vp, d); changed { *vp = v }
@@ -408,6 +417,7 @@ func (fastpathT) {{ .MethodNamePfx "Dec" false }}N(v []{{ .Elem }}, d *Decoder)
 			d.arrayCannotExpand(len(v), j+1)
 			slh.ElemContainerState(j)
 			d.swallow()
+			j++
 			for ; (hasLen && j < containerLenS) || !(hasLen || d.d.CheckBreak()); j++ {
 				slh.ElemContainerState(j)
 				d.swallow()
@@ -421,7 +431,11 @@ func (fastpathT) {{ .MethodNamePfx "Dec" false }}N(v []{{ .Elem }}, d *Decoder)
 		} else {
 			{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem }}{{ end }}
 		} */ -}}
-		{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem }}{{ end }}
+		{{ if eq .Elem "interface{}" -}}
+		d.decode(&v[uint(j)])
+		{{- else -}}
+		v[uint(j)] = {{ decmd .Elem }}
+		{{- end }}
 	}
 	slh.End()
 }
@@ -435,30 +449,38 @@ Maps can change if they are
 */ -}}
 func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) {
 	containerLen := d.mapStart()
-	if rv.Kind() == reflect.Ptr {
-		vp := rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})
-		if containerLen == decContainerLenNil {
-			*vp = nil
-			return
+	if containerLen == decContainerLenNil {
+		if rv.Kind() == reflect.Ptr {
+			*(rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})) = nil
 		}
-		if *vp == nil {
-			*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+	} else {
+		if rv.Kind() == reflect.Ptr {
+			vp, _ := rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})
+			if *vp == nil {
+				*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+			}
+			if containerLen != 0 {
+				fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
+			}
+		} else if containerLen != 0 {
+			fastpathTV.{{ .MethodNamePfx "Dec" false }}L(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), containerLen, d)
 		}
-		fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
-	} else if containerLen != decContainerLenNil {
-		fastpathTV.{{ .MethodNamePfx "Dec" false }}L(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), containerLen, d)
+		d.mapEnd()
 	}
 }
 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) {
 	containerLen := d.mapStart()
 	if containerLen == decContainerLenNil {
 		*vp = nil
-		return
-	}
-	if *vp == nil {
-		*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+	} else {
+		if *vp == nil {
+			*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }}))
+		}
+		if containerLen != 0 {
+			f.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
+		}
+		d.mapEnd()
 	}
-	f.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
 }
 {{/*
 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}Y(v map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
@@ -479,11 +501,11 @@ func (fastpathT) {{ .MethodNamePfx "Dec" false }}L(v map[{{ .MapKey }}]{{ .Elem
 	if containerLen == decContainerLenNil {
 		return
 	}
-	*/ -}}
 	if containerLen == 0 {
 		d.mapEnd()
 		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
     {{end -}}
@@ -516,6 +538,8 @@ func (fastpathT) {{ .MethodNamePfx "Dec" false }}L(v map[{{ .MapKey }}]{{ .Elem
 		{{ end -}}
 		if v != nil { v[mk] = mv }
 	}
+	{{- /*
 	d.mapEnd()
+	*/}}
 }
 {{end}}{{end}}{{end}}

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini