// //+build ignore

// 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.

// ************************************************************
// DO NOT EDIT. 
// THIS FILE IS AUTO-GENERATED from fast-path.go.tmpl
// ************************************************************

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
//    - symetrical 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 fastpathCheckNilFalse = false // for reflect
const fastpathCheckNilTrue = true // for type switch

type fastpathT struct {}

var fastpathTV fastpathT

type fastpathE struct {
	rtid uintptr
	rt reflect.Type 
	encfn func(encFnInfo, reflect.Value)
	decfn func(decFnInfo, 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() {
	if !fastpathEnabled {
		return
	}
	i := 0
	fn := func(v interface{}, fe func(encFnInfo, reflect.Value), fd func(decFnInfo, reflect.Value)) (f fastpathE) {
		xrt := reflect.TypeOf(v)
		xptr := reflect.ValueOf(xrt).Pointer()
		fastpathAV[i] = fastpathE{xptr, xrt, fe, fd}
		i++
		return
	}
	
	{{range .Values}}{{if not .Primitive}}{{if .Slice }}
	fn([]{{ .Elem }}(nil), (encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (decFnInfo).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
	
	{{range .Values}}{{if not .Primitive}}{{if not .Slice }}
	fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (decFnInfo).{{ .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 .Slice }}
	case []{{ .Elem }}:{{else}}
	case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e){{if .Slice }}
	case *[]{{ .Elem }}:{{else}}
	case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
{{end}}{{end}}
	default:
		return false
	}
	return true
}

func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
	switch v := iv.(type) {
{{range .Values}}{{if not .Primitive}}{{if .Slice }}
	case []{{ .Elem }}:
		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
	case *[]{{ .Elem }}:
		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
{{end}}{{end}}{{end}}
	default:
		return false
	}
	return true
}

func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
	switch v := iv.(type) {
{{range .Values}}{{if not .Primitive}}{{if not .Slice }}
	case map[{{ .MapKey }}]{{ .Elem }}:
		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
	case *map[{{ .MapKey }}]{{ .Elem }}:
		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
{{end}}{{end}}{{end}}
	default:
		return false
	}
	return true
}

// -- -- fast path functions
{{range .Values}}{{if not .Primitive}}{{if .Slice }} 

func (f encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
	fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e)
}
func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) {
	ee := e.e 
	if checkNil && v == nil {
		ee.EncodeNil()
		return
	}
	ee.EncodeArrayStart(len(v))
	if e.be {
		for _, v2 := range v {
			{{ encmd .Elem "v2"}}
		}
	} else {
		for j, v2 := range v {
			if j > 0 {
				ee.EncodeArrayEntrySeparator()
			}
			{{ encmd .Elem "v2"}}
		}
		ee.EncodeArrayEnd()
	}
}

{{end}}{{end}}{{end}}

{{range .Values}}{{if not .Primitive}}{{if not .Slice }}

func (f encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
	fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().(map[{{ .MapKey }}]{{ .Elem }}), fastpathCheckNilFalse, f.e)
}
func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, e *Encoder) {
	ee := e.e
	if checkNil && v == nil {
		ee.EncodeNil()
		return
	}
	ee.EncodeMapStart(len(v))
	{{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0{{end}}
	if e.be {
		for k2, v2 := range v {
			{{if eq .MapKey "string"}}if asSymbols {
				ee.EncodeSymbol(k2)
			} else {
				ee.EncodeString(c_UTF8, k2)
			}{{else}}{{ encmd .MapKey "k2"}}{{end}}
			{{ encmd .Elem "v2"}}
		}
	} else {
		j := 0
		for k2, v2 := range v {
			if j > 0 {
				ee.EncodeMapEntrySeparator()
			}
			{{if eq .MapKey "string"}}if asSymbols {
				ee.EncodeSymbol(k2)
			} else {
				ee.EncodeString(c_UTF8, k2)
			}{{else}}{{ encmd .MapKey "k2"}}{{end}}
			ee.EncodeMapKVSeparator()
			{{ encmd .Elem "v2"}}
			j++
		}
		ee.EncodeMapEnd()
	}
}

{{end}}{{end}}{{end}}

// -- decode

// -- -- fast path type switch
func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
	switch v := iv.(type) {
{{range .Values}}{{if not .Primitive}}{{if .Slice }}
	case []{{ .Elem }}:{{else}}
	case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, d){{if .Slice }}
	case *[]{{ .Elem }}:{{else}}
	case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
		v2, changed2 := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, fastpathCheckNilFalse, true, d)
		if changed2 {
			*v = v2 
		}
{{end}}{{end}}
	default:
		return false
	}
	return true
}

// -- -- fast path functions
{{range .Values}}{{if not .Primitive}}{{if .Slice }}
{{/*
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 (f decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) { 
	array := f.seq == seqTypeArray
	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported 
		vp := rv.Addr().Interface().(*[]{{ .Elem }})
		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, !array, f.d)
		if changed {
			*vp = v
		}
	} else {
		v := rv.Interface().([]{{ .Elem }})
		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
	}
}

func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, checkNil bool, d *Decoder) {
	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
	if changed {
		*vp = v 
	}
}
func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, checkNil bool, canChange bool, 
	d *Decoder) (_ []{{ .Elem }}, changed bool) {
	dd := d.d
	// if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil()
	if checkNil && dd.TryDecodeAsNil() {
		if v != nil {
			changed = true 
		}
		return nil, changed 
	}

	slh, containerLenS := d.decSliceHelperStart()
	if canChange && v == nil {
		if containerLenS <= 0 {
			v = []{{ .Elem }}{}
		} else {
			v = make([]{{ .Elem }}, containerLenS, containerLenS)
		}
		changed = true
	}
	if containerLenS == 0 {
		if canChange && len(v) != 0 {
			v = v[:0]
			changed = true 
		}{{/*
		// slh.End() // dd.ReadArrayEnd()
		*/}}
		return v, changed 
	}
	
	// for j := 0; j < containerLenS; j++ {
	if containerLenS > 0 {
		decLen := containerLenS
		if containerLenS > cap(v) {
			if canChange {
				s := make([]{{ .Elem }}, containerLenS, containerLenS)
				// copy(s, v[:cap(v)])
				v = s
				changed = true
			} else {
				d.arrayCannotExpand(len(v), containerLenS)
				decLen = len(v)
			}
		} else if containerLenS != len(v) {
			v = v[:containerLenS]
			changed = true
		}
		// all checks done. cannot go past len.
		j := 0
		for ; j < decLen; j++ { 
			{{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
		}
		if !canChange {
			for ; j < containerLenS; j++ { 
				d.swallow()
			}
		}
	} else {
		j := 0
		for ; !dd.CheckBreak(); j++ {
			if j >= len(v) { 
				if canChange {
					v = append(v, {{ zerocmd .Elem }})
					changed = true
				} else {
					d.arrayCannotExpand(len(v), j+1)
				}
			} 
			if j > 0 {
				slh.Sep(j)
			}
			if j < len(v) { // all checks done. cannot go past len.
				{{ if eq .Elem "interface{}" }}d.decode(&v[j])
				{{ else }}v[j] = {{ decmd .Elem }}{{ end }}
			} else {
				d.swallow()
			}
		}
		slh.End() 
	}
	return v, changed 
}

{{end}}{{end}}{{end}}


{{range .Values}}{{if not .Primitive}}{{if not .Slice }}
{{/*
Maps can change if they are
- addressable (from a ptr)
- settable (e.g. contained in an interface{})
*/}}
func (f decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) { 
	if rv.CanAddr() {
		vp := rv.Addr().Interface().(*map[{{ .MapKey }}]{{ .Elem }})
		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, true, f.d)
		if changed {
			*vp = v
		}
	} else {
		v := rv.Interface().(map[{{ .MapKey }}]{{ .Elem }})
		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
	}
}
func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, d *Decoder) {
	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
	if changed {
		*vp = v 
	}
}
func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, canChange bool, 
	d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
	dd := d.d
	// if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil()
	if checkNil && dd.TryDecodeAsNil() {
		if v != nil {
			changed = true
		} 
		return nil, changed
	}

	containerLen := dd.ReadMapStart()
	if canChange && v == nil {
		if containerLen > 0 {
			v = make(map[{{ .MapKey }}]{{ .Elem }}, containerLen)
		} else {
			v = make(map[{{ .MapKey }}]{{ .Elem }}) // supports indefinite-length, etc
		}
		changed = true
	}
	if containerLen > 0 {
		for j := 0; j < containerLen; j++ {
			{{ if eq .MapKey "interface{}" }}var mk interface{}
			d.decode(&mk)
			if bv, bok := mk.([]byte); bok {
				mk = string(bv) // maps cannot have []byte as key. switch to string.
			}{{ else }}mk := {{ decmd .MapKey }}{{ end }}
			mv := v[mk]
			{{ if eq .Elem "interface{}" }}d.decode(&mv)
			{{ else }}mv = {{ decmd .Elem }}{{ end }}
			if v != nil {
				v[mk] = mv
			}
		}
	} else if containerLen < 0 {
		for j := 0; !dd.CheckBreak(); j++ {
			if j > 0 {
				dd.ReadMapEntrySeparator()
			}
			{{ if eq .MapKey "interface{}" }}var mk interface{}
			d.decode(&mk)
			if bv, bok := mk.([]byte); bok {
				mk = string(bv) // maps cannot have []byte as key. switch to string.
			}{{ else }}mk := {{ decmd .MapKey }}{{ end }}
			dd.ReadMapKVSeparator()
			mv := v[mk]
			{{ if eq .Elem "interface{}" }}d.decode(&mv)
			{{ else }}mv = {{ decmd .Elem }}{{ end }}
			if v != nil {
				v[mk] = mv
			}
		}
		dd.ReadMapEnd()
	}
	return v, changed
}

{{end}}{{end}}{{end}}
