mirror of
1
Fork 0
gotosocial/vendor/github.com/ugorji/go/codec/fast-path.go.tmpl

556 lines
17 KiB
Cheetah
Raw Normal View History

// +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
2023-02-25 13:12:40 +01:00
for j = 0; d.containerNext(j, containerLenS, hasLen); 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
2023-02-25 13:12:40 +01:00
for j := 0; d.containerNext(j, containerLenS, hasLen); 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
2023-02-25 13:12:40 +01:00
for j := 0; d.containerNext(j, containerLen, hasLen); 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}}