// +build !notfastpath // Copyright (c) 2012-2018 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. // 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 func (x *fastpathA) index(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, len(x) var h, i uint var j = uint(len(x)) LOOP: if i < j { h = i + (j-i)/2 if x[h].rtid < rtid { i = h + 1 } else { j = h } goto LOOP } if i < uint(len(x)) && x[i].rtid == rtid { return int(i) } return -1 } type fastpathAslice []fastpathE func (x fastpathAslice) Len() int { return len(x) } func (x fastpathAslice) Less(i, j int) bool { return x[uint(i)].rtid < x[uint(j)].rtid } func (x fastpathAslice) Swap(i, j int) { x[uint(i)], x[uint(j)] = x[uint(j)], x[uint(i)] } var fastpathAV fastpathA // 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) fastpathAV[i] = fastpathE{xptr, xrt, fe, fd} 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 .MapKey }} fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}} sort.Sort(fastpathAslice(fastpathAV[:])) } // -- encode // -- -- 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 }} 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 } func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool { switch v := iv.(type) { {{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 **** */}} // -- -- 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) } else { fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).([]{{ .Elem }}), e) } } func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *Encoder) { if v == nil { e.e.EncodeNil() return } e.arrayStart(len(v)) for j := range v { e.arrayElem() {{ encmd .Elem "v[j]"}} } e.arrayEnd() } func (fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) { if v == nil { e.e.EncodeNil() } else if len(v)%2 == 1 { e.errorf(fastpathMapBySliceErrMsg, len(v)) } else { e.mapStart(len(v) / 2) for j := range v { 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 var vp *bytesIntf {{/* put loop variables outside. seems currently needed for better perf */}} 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"}} if e.h.StringToRaw {e.e.EncodeStringBytesRaw(bytesView(k2))} else {e.e.EncodeStringEnc(cUTF8, 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"}} if e.h.StringToRaw {e.e.EncodeStringBytesRaw(bytesView(k2))} else {e.e.EncodeStringEnc(cUTF8, 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 switch v := iv.(type) { {{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) } case *[]{{ .Elem }}: 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 }}{{/* // 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 }}: var v2 map[{{ .MapKey }}]{{ .Elem }} v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d) if changed { *v = v2 }{{/* */}}{{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) { if array := f.seq == seqTypeArray; !array && rv.Kind() == reflect.Ptr { vp := rv2i(rv).(*[]{{ .Elem }}) if v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, !array, 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) } } } 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 } } func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, 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 } slh.End() return v, changed } hasLen := containerLenS > 0 var xlen int if hasLen && canChange { 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.d.CheckBreak()); j++ { if j == 0 && len(v) == 0 && canChange { if hasLen { xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }}) } else { xlen = 8 } v = make([]{{ .Elem }}, uint(xlen)) 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 } } slh.ElemContainerState(j) if decodeIntoBlank { d.swallow() } else 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 } } slh.End() return v, changed } {{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{}) */}} 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 } } 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 } } 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() */ -}} containerLen := d.mapStart() if canChange && v == nil { v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})) changed = true } if containerLen == 0 { d.mapEnd() return v, changed } {{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.d.CheckBreak()); j++ { d.mapElemKey() {{ if eq .MapKey "interface{}" }}mk = nil d.decode(&mk) if bv, bok := mk.([]byte); bok { mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}} }{{ else }}mk = {{ decmd .MapKey }}{{ end }} d.mapElemValue() if d.d.TryDecodeAsNil() { if v == nil {} else if d.h.DeleteOnNilMapValue { delete(v, mk) } else { v[mk] = {{ zerocmd .Elem }} } continue } {{ 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.d.DecodeBytes(mv, false) {{ else -}} mv = {{ decmd .Elem }} {{ end -}} if v != nil { v[mk] = mv } } d.mapEnd() return v, changed } {{end}}{{end}}{{end}}