556 lines
17 KiB
Cheetah
556 lines
17 KiB
Cheetah
|
// +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; 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
|
||
|
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
|
||
|
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}}
|