//go:build !notfastpath && !codec.notfastpath && (notmono || codec.notmono)

// 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 fastpath.notmono.go.tmpl - DO NOT EDIT.

package codec

import (
	"reflect"
	"sort"
	"slices"
)

type fastpathE[T encDriver] struct {
	rtid  uintptr
	rt    reflect.Type
	encfn func(*encoder[T], *encFnInfo, reflect.Value)
}
type fastpathD[T decDriver] struct {
	rtid  uintptr
	rt    reflect.Type
	decfn func(*decoder[T], *decFnInfo, reflect.Value)
}
type fastpathEs[T encDriver] [{{ .FastpathLen }}]fastpathE[T]
type fastpathDs[T decDriver] [{{ .FastpathLen }}]fastpathD[T]

type fastpathET[T encDriver] struct{}
type fastpathDT[T decDriver] struct{}

func (helperEncDriver[T]) fastpathEList() *fastpathEs[T] {
	var i uint = 0
	var s fastpathEs[T]
	fn := func(v interface{}, fe func(*encoder[T], *encFnInfo, reflect.Value)) {
		xrt := reflect.TypeOf(v)
		s[i] = fastpathE[T]{rt2id(xrt), xrt, fe}
		i++
	}
	{{/* do not register []byte in fastpath */}}
	{{range .Values}}{{if not .Primitive}}{{if not .MapKey -}}
	fn([]{{ .Elem }}(nil), (*encoder[T]).{{ .MethodNamePfx "fastpathEnc" false }}R)
	{{end}}{{end}}{{end}}
	
	{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
	fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*encoder[T]).{{ .MethodNamePfx "fastpathEnc" false }}R)
	{{end}}{{end}}{{end}}

	sort.Slice(s[:], func(i, j int) bool { return s[i].rtid < s[j].rtid })
	return &s
}

func (helperDecDriver[T]) fastpathDList() *fastpathDs[T] {
	var i uint = 0
	var s fastpathDs[T]
	fn := func(v interface{}, fd func(*decoder[T], *decFnInfo, reflect.Value)) {
		xrt := reflect.TypeOf(v)
		s[i] = fastpathD[T]{rt2id(xrt), xrt, fd}
		i++
	}
	{{/* do not register []byte in fastpath */}}
	{{range .Values}}{{if not .Primitive}}{{if not .MapKey -}}
	fn([]{{ .Elem }}(nil), (*decoder[T]).{{ .MethodNamePfx "fastpathDec" false }}R)
	{{end}}{{end}}{{end}}
	
	{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
	fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*decoder[T]).{{ .MethodNamePfx "fastpathDec" false }}R)
	{{end}}{{end}}{{end}}
	
	sort.Slice(s[:], func(i, j int) bool { return s[i].rtid < s[j].rtid })
	return &s
}

// -- encode

// -- -- fast path type switch
func (helperEncDriver[T]) fastpathEncodeTypeSwitch(iv interface{}, e *encoder[T]) bool {
	var ft fastpathET[T]
	switch v := iv.(type) {
		{{range .Values}}{{if not .Primitive}}{{if not .MapKey -}}
	case []{{ .Elem }}:
		if v == nil { e.e.writeNilArray() } else { ft.{{ .MethodNamePfx "Enc" false }}V(v, e) }
{{end}}{{end}}{{end -}}

{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
	case map[{{ .MapKey }}]{{ .Elem }}:
		if v == nil { e.e.writeNilMap() } else { ft.{{ .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[T]) {{ .MethodNamePfx "fastpathEnc" false }}R(f *encFnInfo, rv reflect.Value) {
	var ft fastpathET[T]
	var v  []{{ .Elem }}
	if rv.Kind() == reflect.Array {
		rvGetSlice4Array(rv, &v)
	} else {
		v = rv2i(rv).([]{{ .Elem }})
	}
	if f.ti.mbs {
		ft.{{ .MethodNamePfx "EncAsMap" false }}V(v, e)
		return
	}
	ft.{{ .MethodNamePfx "Enc" false }}V(v, e)
}
func (fastpathET[T]) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *encoder[T]) {
	{{ if eq .Elem "uint8" "byte" -}}
	e.e.EncodeStringBytesRaw(v)
	{{ else -}}
	if len(v) == 0 {
		e.c = 0; e.e.WriteArrayEmpty()
		return
	}
	e.arrayStart(len(v))
	for j := range v {
		e.c = containerArrayElem; e.e.WriteArrayElem(j == 0)
		{{ encmd .Elem "v[j]"}}
	}
	e.c = 0; e.e.WriteArrayEnd()
	{{ end -}}
}
func (fastpathET[T]) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *encoder[T]) {
	if len(v) == 0 {
		e.c = 0; e.e.WriteMapEmpty()
		return
	}
	e.haltOnMbsOddLen(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.c = containerMapKey; e.e.WriteMapElemKey(j == 0)
		} else {
			e.mapElemValue()
		}
		{{ encmd .Elem "v[j]"}}
	}
	e.c = 0; e.e.WriteMapEnd()
}

{{end}}{{end}}{{end -}}

{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
func (e *encoder[T]) {{ .MethodNamePfx "fastpathEnc" false }}R(f *encFnInfo, rv reflect.Value) {
	{{/* var ft fastpathET[T]
	ft.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), e) */ -}}
	fastpathET[T]{}.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), e)
}
func (fastpathET[T]) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, e *encoder[T]) {
	{{/* if v == nil { e.e.EncodeNil(); return } */ -}}
	if len(v) == 0 {
		e.e.WriteMapEmpty()
		return
	}
	var i uint
	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 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++
		}
		slices.SortFunc(v2, cmpBytesIntf)
		for j := range v2 {
			e.c = containerMapKey; e.e.WriteMapElemKey(j == 0)
			e.asis(v2[j].v)
			e.mapElemValue()
			e.encode(v[v2[j].i])
		} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
		for k := range v {
			v2[i] = {{if eq $x .MapKey}}k{{else}}{{ $x }}(k){{end}}
			i++
		}
		slices.Sort(v2)
		{{/* // sort.Sort({{ sorttype .MapKey false}}(v2)) */ -}}
		for i, k2 := range v2 {
			e.c = containerMapKey; e.e.WriteMapElemKey(i == 0)
			{{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 {
		i = 0
		for k2, v2 := range v {
			e.c = containerMapKey; e.e.WriteMapElemKey(i == 0)
			{{if eq .MapKey "string"}} e.e.EncodeString(k2) {{else}}{{ encmd .MapKey "k2"}}{{end}}
			e.mapElemValue()
			{{ encmd .Elem "v2"}}
			i++
		}
	}
	e.c = 0; e.e.WriteMapEnd()
}
{{end}}{{end}}{{end -}}

// -- decode

// -- -- fast path type switch
func (helperDecDriver[T]) fastpathDecodeTypeSwitch(iv interface{}, d *decoder[T]) bool {
	var ft fastpathDT[T]
	var changed bool
	var containerLen int
	switch v := iv.(type) {
{{range .Values}}{{if not .Primitive}}{{if not .MapKey -}}
	case []{{ .Elem }}:
		ft.{{ .MethodNamePfx "Dec" false }}N(v, d)
	case *[]{{ .Elem }}:
		var v2 []{{ .Elem }}
		if v2, changed = ft.{{ .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 }}:
		if containerLen = d.mapStart(d.d.ReadMapStart()); containerLen != containerLenNil {
			if containerLen != 0 {
				ft.{{ .MethodNamePfx "Dec" false }}L(v, containerLen, d)
			}
			d.mapEnd()
		}
	case *map[{{ .MapKey }}]{{ .Elem }}:
		if containerLen = d.mapStart(d.d.ReadMapStart()); containerLen == containerLenNil {
			*v = nil
		} else {
			if *v == nil {
				*v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.maxInitLen(), {{ .Size }}))
			}
			if containerLen != 0 {
				ft.{{ .MethodNamePfx "Dec" false }}L(*v, containerLen, d)
			}
			d.mapEnd()
		}
{{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[T]) {{ .MethodNamePfx "fastpathDec" false }}R(f *decFnInfo, rv reflect.Value) {
	var ft fastpathDT[T]
	{{/*
    // seqTypeArray=true means that we are not getting a pointer, so no need to check that.
    if f.seq != seqTypeArray && rv.Kind() == reflect.Ptr {
    */ -}}
	switch rv.Kind() {
	case reflect.Ptr: {{- /* this block is called for types that wrap a fastpath type e.g. wrapSliceUint64 */}}
		v := rv2i(rv).(*[]{{ .Elem }})
		if vv, changed := ft.{{ .MethodNamePfx "Dec" false }}Y(*v, d); changed {
			*v = vv
		}
	case reflect.Array:
		var v []{{ .Elem }}
		rvGetSlice4Array(rv, &v)
		ft.{{ .MethodNamePfx "Dec" false }}N(v, d)
	default:
		ft.{{ .MethodNamePfx "Dec" false }}N(rv2i(rv).([]{{ .Elem }}), d)
	}
}
func (fastpathDT[T]) {{ .MethodNamePfx "Dec" false }}Y(v []{{ .Elem }}, d *decoder[T]) (v2 []{{ .Elem }}, changed bool) {
	ctyp := d.d.ContainerType()
	if ctyp == valueTypeNil {
		return nil, v != nil
	}
	{{ if eq .Elem "uint8" "byte" -}}
	if ctyp != valueTypeMap {
		var dbi dBytesIntoState
		v2, dbi = d.decodeBytesInto(v[:len(v):len(v)], false)
		return v2, dbi != dBytesIntoParamOut
	}
	containerLenS := d.mapStart(d.d.ReadMapStart()) * 2
	{{ else -}}
	var containerLenS int
	isArray := ctyp == valueTypeArray
	if isArray {
		containerLenS = d.arrayStart(d.d.ReadArrayStart())
	} else if ctyp == valueTypeMap {
		containerLenS = d.mapStart(d.d.ReadMapStart()) * 2
	} else {
		halt.errorStr2("decoding into a slice, expect map/array - got ", ctyp.String())
	}
	{{ end -}}
	hasLen := containerLenS >= 0
	var j int
 	fnv := func(dst []{{ .Elem }}) { v, changed = dst, true }
	for ; d.containerNext(j, containerLenS, hasLen); j++ {
		if j == 0 {
			if containerLenS == len(v) {
			} else if containerLenS < 0 || containerLenS > cap(v) {
				if xlen := int(decInferLen(containerLenS, d.maxInitLen(), {{ .Size }})); xlen <= cap(v) {
					fnv(v[:uint(xlen)])
				} else {
					v2 = make([]{{ .Elem }}, uint(xlen))
					copy(v2, v)
					fnv(v2)
				}
			} else {
				fnv(v[:containerLenS])
			}
		}
		{{ if eq .Elem "uint8" "byte" }}{{ else -}}
		if isArray { d.arrayElem(j == 0) } else
		{{ end -}}
		if j&1 == 0 { d.mapElemKey(j == 0) } else { d.mapElemValue() }
		if j >= len(v) { {{- /* // if indefinite, json, etc, then expand the slice (if necessary) */}}
			fnv(append(v, {{ zerocmd .Elem }}))
		}
		{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem false }}{{ end }}
	}
	if j < len(v) {
		fnv(v[:uint(j)])
	} else if j == 0 && v == nil {
		fnv([]{{ .Elem }}{})
	} 
	{{ if eq .Elem "uint8" "byte" -}}
	d.mapEnd()
	{{ else -}}
	if isArray { d.arrayEnd() } else { d.mapEnd() }
	{{ end -}}
	return v, changed
}
func (fastpathDT[T]) {{ .MethodNamePfx "Dec" false }}N(v []{{ .Elem }}, d *decoder[T]) {
	ctyp := d.d.ContainerType()
	if ctyp == valueTypeNil {
		return
	}
	{{ if eq .Elem "uint8" "byte" -}}
	if ctyp != valueTypeMap {
		d.decodeBytesInto(v[:len(v):len(v)], true)
		return
	}
	containerLenS := d.mapStart(d.d.ReadMapStart()) * 2
	{{ else -}}
	var containerLenS int
	isArray := ctyp == valueTypeArray
	if isArray {
		containerLenS = d.arrayStart(d.d.ReadArrayStart())
	} else if ctyp == valueTypeMap {
		containerLenS = d.mapStart(d.d.ReadMapStart()) * 2
	} else {
		halt.errorStr2("decoding into a slice, expect map/array - got ", ctyp.String())
	}
	{{ end -}}
	hasLen := containerLenS >= 0
    for j := 0; d.containerNext(j, containerLenS, hasLen); j++ {
		{{/* // if indefinite, etc, then expand the slice if necessary */ -}}
		{{ if not (eq .Elem "uint8" "byte") -}}
		if isArray { d.arrayElem(j == 0) } else
		{{ end -}}
		if j&1 == 0 { d.mapElemKey(j == 0) } else { d.mapElemValue() }
		if j < len(v) {
			{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem false }}{{ end }}
		} else {
			d.arrayCannotExpand(len(v), j+1)
			d.swallow()
		}
	}
	{{ if eq .Elem "uint8" "byte" -}}
	d.mapEnd()
	{{ else -}}
	if isArray { d.arrayEnd() } else { d.mapEnd() }
	{{ 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{})
*/ -}}
func (d *decoder[T]) {{ .MethodNamePfx "fastpathDec" false }}R(f *decFnInfo, rv reflect.Value) {
	var ft fastpathDT[T]
	containerLen := d.mapStart(d.d.ReadMapStart())
	if rv.Kind() == reflect.Ptr { {{- /* this block is called for types that wrap a fastpath type e.g. wrapMapStringUint64 */}}
		vp, _ := rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})
		if *vp == nil {
			*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.maxInitLen(), {{ .Size }}))
		}
		if containerLen != 0 {
			ft.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
		}
	} else if containerLen != 0 {
		ft.{{ .MethodNamePfx "Dec" false }}L(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), containerLen, d)
	}
	d.mapEnd()
}
func (fastpathDT[T]) {{ .MethodNamePfx "Dec" false }}L(v map[{{ .MapKey }}]{{ .Elem }}, containerLen int, d *decoder[T]) {
	if v == nil {
		halt.errorInt("cannot decode into nil map[{{ .MapKey }}]{{ .Elem }} given stream length: ", int64(containerLen))
        {{/* d.swallowMapContents(containerLen); return */ -}}
	}	
	{{if eq .MapKey "interface{}" -}}
    var mk {{ .MapKey }}
	{{end -}}
	{{ if eq .Elem "interface{}" "[]byte" "bytes" -}}
	var mv {{ .Elem }}
	mapGet := !d.h.MapValueReset
	{{- if eq .Elem "interface{}" -}}
	&& !d.h.InterfaceReset
	{{- end}}
	{{end -}}
	hasLen := containerLen >= 0
    for j := 0; d.containerNext(j, containerLen, hasLen); j++ {
		d.mapElemKey(j == 0)
		{{ if eq .MapKey "interface{}" -}}
		mk = nil 
		d.decode(&mk)
		if bv, bok := mk.([]byte); bok {
			mk = d.detach2Str(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)
		v[mk] = mv
		{{ else if eq .Elem "[]byte" "bytes" -}}
		v[mk], _ = d.decodeBytesInto(mv, false)
		{{ else -}}
		v[mk] = {{ decmd .Elem false }}
		{{ end -}}
	}
}
{{end}}{{end}}{{end}}

{{- /*

// -- -- fast path type switch
func (helperEncDriver[T]) fastpathEncodeTypeSwitch(iv interface{}, e *encoder[T]) bool {
	var ft fastpathET[T]
	switch v := iv.(type) {
		{{range .Values}}{{if not .Primitive}}{{if not .MapKey -}}
	case []{{ .Elem }}:
		if v != nil {
			ft.{{ .MethodNamePfx "Enc" false }}V(v, e)
		} else if e.h.NilCollectionToZeroLength {
			e.e.WriteArrayEmpty()
		} else {
			e.e.EncodeNil()
		}
	case *[]{{ .Elem }}:
		if *v != nil {
			ft.{{ .MethodNamePfx "Enc" false }}V(*v, e)
		} else if e.h.NilCollectionToZeroLength {
			e.e.WriteArrayEmpty()
		} else {
			e.e.EncodeNil()
		}
{{end}}{{end}}{{end -}}

{{range .Values}}{{if not .Primitive}}{{if .MapKey -}}
	case map[{{ .MapKey }}]{{ .Elem }}:
		if v != nil {
			ft.{{ .MethodNamePfx "Enc" false }}V(v, e)
		} else if e.h.NilCollectionToZeroLength {
			e.e.WriteMapEmpty()
		} else {
			e.e.EncodeNil()
		}
	case *map[{{ .MapKey }}]{{ .Elem }}:
		if *v != nil {
			ft.{{ .MethodNamePfx "Enc" false }}V(*v, e)
		} else if e.h.NilCollectionToZeroLength {
			e.e.WriteMapEmpty()
		} else {
			e.e.EncodeNil()
		}
{{end}}{{end}}{{end -}}

	default:
		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
		return false
	}
	return true
}

// used within codecgen, which is no longer supported
func (f fastpathDT[T]) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, d *decoder[T]) {
	if v, changed := f.{{ .MethodNamePfx "Dec" false }}Y(*vp, d); changed { *vp = v }
}

func (f fastpathDT[T]) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, d *decoder[T]) {
	containerLen := d.mapStart(d.d.ReadMapStart())
	if containerLen == containerLenNil {
		*vp = nil
		return
	}
	if *vp == nil {
		*vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.maxInitLen(), {{ .Size }}))
	}
	if containerLen != 0 {
		f.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d)
	}
	d.mapEnd()
}

*/ -}}
