// +build !notfastpath // +build !codec.notfastpath // Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved. // Use of this source code is governed by a MIT license found in the LICENSE file. // Code generated from fast-path.go.tmpl - DO NOT EDIT. package codec // Fast path functions try to create a fast path encode or decode implementation // for common maps and slices. // // We define the functions and register them in this single file // so as not to pollute the encode.go and decode.go, and create a dependency in there. // This file can be omitted without causing a build failure. // // The advantage of fast paths is: // - Many calls bypass reflection altogether // // Currently support // - slice of all builtin types (numeric, bool, string, []byte) // - maps of builtin types to builtin or interface{} type, EXCEPT FOR // keys of type uintptr, int8/16/32, uint16/32, float32/64, bool, interface{} // AND values of type type int8/16/32, uint16/32 // 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. // {{/* fastpathEncMapStringUint64R (called by fastpath...switch) EncMapStringUint64V (called by codecgen) fastpathEncSliceBoolR: (called by fastpath...switch) (checks f.ti.mbs and calls one of them below) EncSliceBoolV (also called by codecgen) EncAsMapSliceBoolV (delegate when mapbyslice=true) fastpathDecSliceIntfR (called by fastpath...switch) (calls Y or N below depending on if it can be updated) 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 or X? below) DecMap...X (called by codecgen) DecMap...L (delegated to by both above) */ -}} import ( "reflect" "sort" ) const fastpathEnabled = true {{/* const fastpathMapBySliceErrMsg = "mapBySlice requires even slice length, but got %v" */ -}} type fastpathT struct {} var fastpathTV fastpathT type fastpathE struct { {{/* rtid uintptr */ -}} rt reflect.Type encfn func(*Encoder, *codecFnInfo, reflect.Value) decfn func(*Decoder, *codecFnInfo, reflect.Value) } type fastpathA [{{ .FastpathLen }}]fastpathE type fastpathARtid [{{ .FastpathLen }}]uintptr var fastpathAv fastpathA var fastpathAvRtid fastpathARtid type fastpathAslice struct{} func (fastpathAslice) Len() int { return {{ .FastpathLen }} } func (fastpathAslice) Less(i, j int) bool { return fastpathAvRtid[uint(i)] < fastpathAvRtid[uint(j)] } func (fastpathAslice) Swap(i, j int) { fastpathAvRtid[uint(i)], fastpathAvRtid[uint(j)] = fastpathAvRtid[uint(j)], fastpathAvRtid[uint(i)] fastpathAv[uint(i)], fastpathAv[uint(j)] = fastpathAv[uint(j)], fastpathAv[uint(i)] } func fastpathAvIndex(rtid uintptr) int { // use binary search to grab the index (adapted from sort/search.go) // Note: we use goto (instead of for loop) so this can be inlined. // h, i, j := 0, 0, {{ .FastpathLen }} var h, i uint var j uint = {{ .FastpathLen }} LOOP: if i < j { h = (i + j) >> 1 // avoid overflow when computing h // h = i + (j-i)/2 if fastpathAvRtid[h] < rtid { i = h + 1 } else { j = h } goto LOOP } if i < {{ .FastpathLen }} && fastpathAvRtid[i] == rtid { return int(i) } return -1 } // due to possible initialization loop error, make fastpath in an init() func init() { var i uint = 0 fn := func(v interface{}, fe func(*Encoder, *codecFnInfo, reflect.Value), fd func(*Decoder, *codecFnInfo, reflect.Value)) { xrt := reflect.TypeOf(v) xptr := rt2id(xrt) fastpathAvRtid[i] = xptr fastpathAv[i] = fastpathE{xrt, fe, fd} i++ } {{/* do not register []byte in fast-path */}} {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} fn([]{{ .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{}) } // -- encode // -- -- fast path type switch func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool { switch v := iv.(type) { {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} case []{{ .Elem }}: fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e) case *[]{{ .Elem }}: if *v == nil { e.e.EncodeNil() } else { fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e) } {{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 }}: if *v == nil { e.e.EncodeNil() } else { 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 } // -- -- fast path functions {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) { var v []{{ .Elem }} if rv.Kind() == reflect.Array { rvGetSlice4Array(rv, &v) } else { v = rv2i(rv).([]{{ .Elem }}) } if f.ti.mbs { fastpathTV.{{ .MethodNamePfx "EncAsMap" false }}V(v, e) } else { fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e) } } func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *Encoder) { {{/* if v == nil { e.e.EncodeNil(); return } */ -}} {{ if eq .Elem "uint8" "byte" -}} e.e.EncodeStringBytesRaw(v) {{ else -}} e.arrayStart(len(v)) for j := range v { e.arrayElem() {{ encmd .Elem "v[j]"}} } e.arrayEnd() {{ end -}} } func (fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) { {{/* if v == nil { e.e.EncodeNil() } else */ -}} e.haltOnMbsOddLen(len(v)) {{/* if len(v)&1 != 0 { // similar to &1==1 or %2 == 1 e.errorf(fastpathMapBySliceErrMsg, len(v)) } */ -}} e.mapStart(len(v) >> 1) // e.mapStart(len(v) / 2) for j := range v { if j&1 == 0 { // if j%2 == 0 { e.mapElemKey() } else { e.mapElemValue() } {{ encmd .Elem "v[j]"}} } e.mapEnd() } {{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) } func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, e *Encoder) { {{/* if v == nil { e.e.EncodeNil(); return } */ -}} e.mapStart(len(v)) if e.h.Canonical { {{/* need to figure out .NoCanonical */}} {{if eq .MapKey "interface{}"}}{{/* out of band */ -}} var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding e2 := NewEncoderBytes(&mksv, e.hh) v2 := make([]bytesIntf, len(v)) var i, l uint {{/* put loop variables outside. seems currently needed for better perf */}} var vp *bytesIntf for k2 := range v { l = uint(len(mksv)) e2.MustEncode(k2) vp = &v2[i] vp.v = mksv[l:] vp.i = k2 i++ } sort.Sort(bytesIntfSlice(v2)) for j := range v2 { e.mapElemKey() e.asis(v2[j].v) e.mapElemValue() e.encode(v[v2[j].i]) } {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v)) var i uint for k := range v { v2[i] = {{if eq $x .MapKey}}k{{else}}{{ $x }}(k){{end}} i++ } sort.Sort({{ sorttype .MapKey false}}(v2)) for _, k2 := range v2 { e.mapElemKey() {{if eq .MapKey "string"}} e.e.EncodeString(k2) {{else}}{{ $y := printf "%s(k2)" .MapKey }}{{if eq $x .MapKey }}{{ $y = "k2" }}{{end}}{{ encmd .MapKey $y }}{{end}} e.mapElemValue() {{ $y := printf "v[%s(k2)]" .MapKey }}{{if eq $x .MapKey }}{{ $y = "v[k2]" }}{{end}}{{ encmd .Elem $y }} } {{end}} } else { for k2, v2 := range v { e.mapElemKey() {{if eq .MapKey "string"}} e.e.EncodeString(k2) {{else}}{{ encmd .MapKey "k2"}}{{end}} e.mapElemValue() {{ encmd .Elem "v2"}} } } e.mapEnd() } {{end}}{{end}}{{end -}} // -- decode // -- -- fast path type switch func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool { var changed bool var containerLen int switch v := iv.(type) { {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} case []{{ .Elem }}: fastpathTV.{{ .MethodNamePfx "Dec" false }}N(v, d) case *[]{{ .Elem }}: var v2 []{{ .Elem }} if v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*v, d); changed { *v = v2 } {{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 }}: containerLen = d.mapStart(d.d.ReadMapStart()) if containerLen != containerLenNil { if containerLen != 0 { fastpathTV.{{ .MethodNamePfx "Dec" false }}L(v, containerLen, d) } d.mapEnd() } case *map[{{ .MapKey }}]{{ .Elem }}: {{/* containerLen = d.mapStart(d.d.ReadMapStart()) if containerLen == 0 { d.mapEnd() } else if containerLen == containerLenNil { *v = nil } else { if *v == nil { *v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})) } 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 return false } return true } func fastpathDecodeSetZeroTypeSwitch(iv interface{}) bool { switch v := iv.(type) { {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} case *[]{{ .Elem }}: *v = nil {{end}}{{end}}{{end}} {{range .Values}}{{if not .Primitive}}{{if .MapKey -}} case *map[{{ .MapKey }}]{{ .Elem }}: *v = nil {{end}}{{end}}{{end}} default: _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4 return false } return true } // -- -- fast path functions {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} {{/* 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) { {{/* // seqTypeArray=true means that we are not getting a pointer, so no need to check that. if f.seq != seqTypeArray && rv.Kind() == reflect.Ptr { */ -}} var v []{{ .Elem }} switch rv.Kind() { case reflect.Ptr: vp := rv2i(rv).(*[]{{ .Elem }}) var changed bool if v, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*vp, d); changed { *vp = v } case reflect.Array: rvGetSlice4Array(rv, &v) fastpathTV.{{ .MethodNamePfx "Dec" false }}N(v, d) default: 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 }}Y(*vp, d); changed { *vp = v } } func (fastpathT) {{ .MethodNamePfx "Dec" false }}Y(v []{{ .Elem }}, d *Decoder) (v2 []{{ .Elem }}, changed bool) { {{ if eq .Elem "uint8" "byte" -}} switch d.d.ContainerType() { case valueTypeNil, valueTypeMap: break default: v2 = d.decodeBytesInto(v[:len(v):len(v)]) changed = !(len(v2) > 0 && len(v2) == len(v) && &v2[0] == &v[0]) // not same slice return } {{ end -}} slh, containerLenS := d.decSliceHelperStart() if slh.IsNil { if v == nil { return } return nil, true } if containerLenS == 0 { if v == nil { v = []{{ .Elem }}{} } else if len(v) != 0 { v = v[:0] } slh.End() return v, true } hasLen := containerLenS > 0 var xlen int if hasLen { if containerLenS > cap(v) { xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }}) if xlen <= cap(v) { v = v[:uint(xlen)] } else { v = make([]{{ .Elem }}, uint(xlen)) } changed = true } else if containerLenS != len(v) { v = v[:containerLenS] changed = true } } var j int for j = 0; (hasLen && j < containerLenS) || !(hasLen || d.checkBreak()); j++ { if j == 0 && len(v) == 0 { // means hasLen == false xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }}) {{/* xlen = decDefSliceCap */}} v = make([]{{ .Elem }}, uint(xlen)) changed = true } {{/* // if indefinite, etc, then expand the slice if necessary */ -}} if j >= len(v) { v = append(v, {{ zerocmd .Elem }}) changed = true } slh.ElemContainerState(j) {{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem false }}{{ end }} } 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 } func (fastpathT) {{ .MethodNamePfx "Dec" false }}N(v []{{ .Elem }}, d *Decoder) { {{ if eq .Elem "uint8" "byte" -}} switch d.d.ContainerType() { case valueTypeNil, valueTypeMap: break default: v2 := d.decodeBytesInto(v[:len(v):len(v)]) if !(len(v2) > 0 && len(v2) == len(v) && &v2[0] == &v[0]) { // not same slice copy(v, v2) } return } {{ end -}} slh, containerLenS := d.decSliceHelperStart() if slh.IsNil { return } if containerLenS == 0 { slh.End() return } hasLen := containerLenS > 0 for j := 0; (hasLen && j < containerLenS) || !(hasLen || d.checkBreak()); j++ { {{/* // if indefinite, etc, then expand the slice if necessary */ -}} if j >= len(v) { slh.arrayCannotExpand(hasLen, len(v), j, containerLenS) return } slh.ElemContainerState(j) {{ if eq .Elem "interface{}" -}} d.decode(&v[uint(j)]) {{- else -}} v[uint(j)] = {{ decmd .Elem false }} {{- end }} } slh.End() } {{end}}{{end}}{{end -}} {{range .Values}}{{if not .Primitive}}{{if .MapKey -}} {{/* Maps can change if they are - addressable (from a ptr) - settable (e.g. contained in an interface{}) Also, these methods are called by decodeValue directly, after handling a TryNil. Consequently, there's no need to check for containerLenNil here. */ -}} func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) { containerLen := d.mapStart(d.d.ReadMapStart()) {{/* if containerLen == containerLenNil { if rv.Kind() == reflect.Ptr { *(rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})) = nil } return } */ -}} 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) } d.mapEnd() } func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) { containerLen := d.mapStart(d.d.ReadMapStart()) if containerLen == containerLenNil { *vp = nil } 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() } } func (fastpathT) {{ .MethodNamePfx "Dec" false }}L(v map[{{ .MapKey }}]{{ .Elem }}, containerLen int, d *Decoder) { {{/* No need to check if containerLen == containerLenNil, as that is checked by R and L above */ -}} if v == nil { d.errorf("cannot decode into nil map[{{ .MapKey }}]{{ .Elem }} given stream length: %v", containerLen) {{/* d.swallowMapContents(containerLen) */ -}} 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 -}} var mk {{ .MapKey }} var mv {{ .Elem }} hasLen := containerLen > 0 for j := 0; (hasLen && j < containerLen) || !(hasLen || d.checkBreak()); j++ { d.mapElemKey() {{ if eq .MapKey "interface{}" }}mk = nil d.decode(&mk) if bv, bok := mk.([]byte); bok { mk = d.stringZC(bv) {{/* // maps cannot have []byte as key. switch to string. */}} }{{ else }}mk = {{ decmd .MapKey true }}{{ end }} d.mapElemValue() {{ if eq .Elem "interface{}" "[]byte" "bytes" -}} if mapGet { mv = v[mk] } else { mv = nil } {{ end -}} {{ if eq .Elem "interface{}" -}} d.decode(&mv) {{ else if eq .Elem "[]byte" "bytes" -}} mv = d.decodeBytesInto(mv) {{ else -}} mv = {{ decmd .Elem false }} {{ end -}} v[mk] = mv } } {{end}}{{end}}{{end}}