// +build !notfastpath

// Copyright (c) 2012-2015 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 then 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,
//	  - map of all builtin types to string or interface value
//	  - symmetrical maps of all builtin types (e.g. str-str, uint8-uint8)
// 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

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)
	h, i, j := 0, 0, {{ .FastpathLen }} // len(x)
	for i < j {
		h = i + (j-i)/2
		if x[h].rtid < rtid {
			i = h + 1
		} else {
			j = h
		}
	}
	if i < {{ .FastpathLen }} && x[i].rtid == rtid {
		return i
	}
	return -1
}

type fastpathAslice []fastpathE

func (x fastpathAslice) Len() int { return len(x) }
func (x fastpathAslice) Less(i, j int) bool { return x[i].rtid < x[j].rtid }
func (x fastpathAslice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }

var fastpathAV fastpathA

// due to possible initialization loop error, make fastpath in an init()
func init() {
	i := 0
	fn := func(v interface{},
		fe func(*Encoder, *codecFnInfo, reflect.Value),
		fd func(*Decoder, *codecFnInfo, reflect.Value)) (f fastpathE) {
		xrt := reflect.TypeOf(v)
		xptr := rt2id(xrt)
		fastpathAV[i] = fastpathE{xptr, xrt, fe, fd}
		i++
		return
	}
	{{/* 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 }
	ee, esep := e.e, e.hh.hasElemSeparators()
	ee.WriteArrayStart(len(v))
	if esep {
		for _, v2 := range v {
			ee.WriteArrayElem()
			{{ encmd .Elem "v2"}}
		}
	} else {
		for _, v2 := range v {
			{{ encmd .Elem "v2"}}
		}
	} {{/*
	for _, v2 := range v {
		if esep { ee.WriteArrayElem() }
		{{ encmd .Elem "v2"}}
	} */}}
	ee.WriteArrayEnd()
}
func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) {
	ee, esep := e.e, e.hh.hasElemSeparators()
	if len(v)%2 == 1 {
		e.errorf("mapBySlice requires even slice length, but got %v", len(v))
		return
	}
	ee.WriteMapStart(len(v) / 2)
	if esep {
		for j, v2 := range v {
			if j%2 == 0 {
				ee.WriteMapElemKey()
			} else {
				ee.WriteMapElemValue()
			}
			{{ encmd .Elem "v2"}}
		}
	} else {
		for _, v2 := range v {
			{{ encmd .Elem "v2"}}
		}
	} {{/*
	for j, v2 := range v {
		if esep {
			if j%2 == 0 {
				ee.WriteMapElemKey()
			} else {
				ee.WriteMapElemValue()
			}
		}
		{{ encmd .Elem "v2"}}
	} */}}
	ee.WriteMapEnd()
}
{{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 }
	ee, esep := e.e, e.hh.hasElemSeparators() 
	ee.WriteMapStart(len(v))
	if e.h.Canonical {
		{{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([]bytesI, len(v))
		var i, l int
		var vp *bytesI {{/* put loop variables outside. seems currently needed for better perf */}}
		for k2, _ := range v {
			l = len(mksv)
			e2.MustEncode(k2)
			vp = &v2[i]
			vp.v = mksv[l:]
			vp.i = k2 
			i++
		}
		sort.Sort(bytesISlice(v2))
		if esep {
			for j := range v2 {
				ee.WriteMapElemKey()
				e.asis(v2[j].v)
				ee.WriteMapElemValue()
				e.encode(v[v2[j].i])
			}
		} else {
			for j := range v2 {
				e.asis(v2[j].v)
				e.encode(v[v2[j].i])
			}
		} {{/*
		for j := range v2 {
			if esep { ee.WriteMapElemKey() }
			e.asis(v2[j].v)
			if esep { ee.WriteMapElemValue() }
			e.encode(v[v2[j].i])
		} */}} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
		var i int 
		for k, _ := range v {
			v2[i] = {{ $x }}(k)
			i++
		}
		sort.Sort({{ sorttype .MapKey false}}(v2))
		if esep {
			for _, k2 := range v2 {
				ee.WriteMapElemKey()
				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
				ee.WriteMapElemValue()
				{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
			} 
		} else {
			for _, k2 := range v2 {
				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
				{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
			} 
		} {{/*
		for _, k2 := range v2 {
			if esep { ee.WriteMapElemKey() }
			{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
			if esep { ee.WriteMapElemValue() }
			{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
		} */}} {{end}}
	} else {
		if esep {
			for k2, v2 := range v {
				ee.WriteMapElemKey()
				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ encmd .MapKey "k2"}}{{end}}
				ee.WriteMapElemValue()
				{{ encmd .Elem "v2"}}
			}
		} else {
			for k2, v2 := range v {
				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ encmd .MapKey "k2"}}{{end}}
				{{ encmd .Elem "v2"}}
			}
		} {{/*
		for k2, v2 := range v {
			if esep { ee.WriteMapElemKey() }
			{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ encmd .MapKey "k2"}}{{end}}
			if esep { ee.WriteMapElemValue() }
			{{ encmd .Elem "v2"}}
		} */}}
	}
	ee.WriteMapEnd()
}
{{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 }})
		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, !array, d)
        if 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) {
	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
    if changed { *vp = v }
}
func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
	dd := d.d{{/*
	    // if dd.isContainerType(valueTypeNil) { dd.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[:xlen]
			} else {
				v = make([]{{ .Elem }}, xlen)
			}
			changed = true 
		} else if containerLenS != len(v) {
			v = v[:containerLenS]
			changed = true
		}
	}
	j := 0
	for ; (hasLen && j < containerLenS) || !(hasLen || dd.CheckBreak()); j++ {
		if j == 0 && len(v) == 0 && canChange {
			if hasLen {
				xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
			} else {
				xlen = 8
			}
			v = make([]{{ .Elem }}, 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 dd.TryDecodeAsNil() {
			v[j] = {{ zerocmd .Elem }}
		} else {
			{{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
		}
	}
	if canChange {
		if j < len(v) {
			v = v[: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 }})
		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d);
		if 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) {
	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
	if changed { *vp = v }
}
func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, canChange bool, 
	d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
	dd, esep := d.d, d.hh.hasElemSeparators(){{/*
	    // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil()
    */}}
	containerLen := dd.ReadMapStart()
	if canChange && v == nil {
		xlen := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
		v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
		changed = true
	}
	if containerLen == 0 {
		dd.ReadMapEnd()
		return v, changed
	}
	{{ if eq .Elem "interface{}" }}mapGet := v != nil && !d.h.MapValueReset && !d.h.InterfaceReset
    {{end}}var mk {{ .MapKey }}
	var mv {{ .Elem }}
	hasLen := containerLen > 0
	for j := 0; (hasLen && j < containerLen) || !(hasLen || dd.CheckBreak()); j++ {
		if esep { dd.ReadMapElemKey() }
		{{ 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 }}
		if esep { dd.ReadMapElemValue() }
		if dd.TryDecodeAsNil() {
			if v == nil {} else if d.h.DeleteOnNilMapValue { delete(v, mk) } else { v[mk] = {{ zerocmd .Elem }} }
			continue 
		}
		{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
		d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
		if v != nil { v[mk] = mv }
	}
	dd.ReadMapEnd()
	return v, changed
}
{{end}}{{end}}{{end}}
