fast-path.go.tmpl

  1// +build !notfastpath
  2
  3// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
  4// Use of this source code is governed by a MIT license found in the LICENSE file.
  5
  6// Code generated from fast-path.go.tmpl - DO NOT EDIT.
  7
  8package codec
  9
 10// Fast path functions try to create a fast path encode or decode implementation
 11// for common maps and slices.
 12//
 13// We define the functions and register then in this single file
 14// so as not to pollute the encode.go and decode.go, and create a dependency in there.
 15// This file can be omitted without causing a build failure.
 16//
 17// The advantage of fast paths is:
 18//	  - Many calls bypass reflection altogether
 19// 
 20// Currently support
 21//	  - slice of all builtin types,
 22//	  - map of all builtin types to string or interface value
 23//	  - symmetrical maps of all builtin types (e.g. str-str, uint8-uint8)
 24// This should provide adequate "typical" implementations.
 25// 
 26// Note that fast track decode functions must handle values for which an address cannot be obtained.
 27// For example: 
 28//	 m2 := map[string]int{}
 29//	 p2 := []interface{}{m2}
 30//	 // decoding into p2 will bomb if fast track functions do not treat like unaddressable.
 31// 
 32
 33import (
 34	"reflect"
 35	"sort"
 36)
 37
 38const fastpathEnabled = true
 39
 40type fastpathT struct {}
 41
 42var fastpathTV fastpathT
 43
 44type fastpathE struct {
 45	rtid uintptr
 46	rt reflect.Type 
 47	encfn func(*Encoder, *codecFnInfo, reflect.Value)
 48	decfn func(*Decoder, *codecFnInfo, reflect.Value)
 49}
 50
 51type fastpathA [{{ .FastpathLen }}]fastpathE
 52
 53func (x *fastpathA) index(rtid uintptr) int {
 54	// use binary search to grab the index (adapted from sort/search.go)
 55	h, i, j := 0, 0, {{ .FastpathLen }} // len(x)
 56	for i < j {
 57		h = i + (j-i)/2
 58		if x[h].rtid < rtid {
 59			i = h + 1
 60		} else {
 61			j = h
 62		}
 63	}
 64	if i < {{ .FastpathLen }} && x[i].rtid == rtid {
 65		return i
 66	}
 67	return -1
 68}
 69
 70type fastpathAslice []fastpathE
 71
 72func (x fastpathAslice) Len() int { return len(x) }
 73func (x fastpathAslice) Less(i, j int) bool { return x[i].rtid < x[j].rtid }
 74func (x fastpathAslice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
 75
 76var fastpathAV fastpathA
 77
 78// due to possible initialization loop error, make fastpath in an init()
 79func init() {
 80	i := 0
 81	fn := func(v interface{},
 82		fe func(*Encoder, *codecFnInfo, reflect.Value),
 83		fd func(*Decoder, *codecFnInfo, reflect.Value)) (f fastpathE) {
 84		xrt := reflect.TypeOf(v)
 85		xptr := rt2id(xrt)
 86		fastpathAV[i] = fastpathE{xptr, xrt, fe, fd}
 87		i++
 88		return
 89	}
 90	{{/* do not register []uint8 in fast-path */}}
 91	{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
 92	fn([]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}{{end}}
 93	
 94	{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
 95	fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
 96	
 97	sort.Sort(fastpathAslice(fastpathAV[:]))
 98}
 99
100// -- encode
101
102// -- -- fast path type switch
103func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
104	switch v := iv.(type) {
105
106{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
107	case []{{ .Elem }}:
108		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
109	case *[]{{ .Elem }}:
110		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e){{/*
111*/}}{{end}}{{end}}{{end}}{{end}}
112
113{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
114	case map[{{ .MapKey }}]{{ .Elem }}:
115		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
116	case *map[{{ .MapKey }}]{{ .Elem }}:
117		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e){{/*
118*/}}{{end}}{{end}}{{end}}
119
120	default:
121        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
122		return false
123	}
124	return true
125}
126
127{{/*
128**** removing this block, as they are never called directly ****
129
130
131
132**** removing this block, as they are never called directly ****
133
134
135
136func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
137	switch v := iv.(type) {
138{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
139	case []{{ .Elem }}:
140		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
141	case *[]{{ .Elem }}:
142		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e)
143{{end}}{{end}}{{end}}
144	default:
145        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
146		return false
147	}
148	return true
149}
150
151func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
152	switch v := iv.(type) {
153{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
154	case map[{{ .MapKey }}]{{ .Elem }}:
155		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e)
156	case *map[{{ .MapKey }}]{{ .Elem }}:
157		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e)
158{{end}}{{end}}{{end}}
159	default:
160        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
161		return false
162	}
163	return true
164}
165
166
167
168**** removing this block, as they are never called directly ****
169
170
171
172**** removing this block, as they are never called directly ****
173*/}}
174
175// -- -- fast path functions
176{{range .Values}}{{if not .Primitive}}{{if not .MapKey }} 
177func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) {
178	if f.ti.mbs {
179		fastpathTV.{{ .MethodNamePfx "EncAsMap" false }}V(rv2i(rv).([]{{ .Elem }}), e)
180	} else {
181		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).([]{{ .Elem }}), e)
182	}
183}
184func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *Encoder) {
185	if v == nil { e.e.EncodeNil(); return }
186	ee, esep := e.e, e.hh.hasElemSeparators()
187	ee.WriteArrayStart(len(v))
188	if esep {
189		for _, v2 := range v {
190			ee.WriteArrayElem()
191			{{ encmd .Elem "v2"}}
192		}
193	} else {
194		for _, v2 := range v {
195			{{ encmd .Elem "v2"}}
196		}
197	} {{/*
198	for _, v2 := range v {
199		if esep { ee.WriteArrayElem() }
200		{{ encmd .Elem "v2"}}
201	} */}}
202	ee.WriteArrayEnd()
203}
204func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) {
205	ee, esep := e.e, e.hh.hasElemSeparators()
206	if len(v)%2 == 1 {
207		e.errorf("mapBySlice requires even slice length, but got %v", len(v))
208		return
209	}
210	ee.WriteMapStart(len(v) / 2)
211	if esep {
212		for j, v2 := range v {
213			if j%2 == 0 {
214				ee.WriteMapElemKey()
215			} else {
216				ee.WriteMapElemValue()
217			}
218			{{ encmd .Elem "v2"}}
219		}
220	} else {
221		for _, v2 := range v {
222			{{ encmd .Elem "v2"}}
223		}
224	} {{/*
225	for j, v2 := range v {
226		if esep {
227			if j%2 == 0 {
228				ee.WriteMapElemKey()
229			} else {
230				ee.WriteMapElemValue()
231			}
232		}
233		{{ encmd .Elem "v2"}}
234	} */}}
235	ee.WriteMapEnd()
236}
237{{end}}{{end}}{{end}}
238
239{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
240func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) {
241	fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), e)
242}
243func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, e *Encoder) {
244	if v == nil { e.e.EncodeNil(); return }
245	ee, esep := e.e, e.hh.hasElemSeparators() 
246	ee.WriteMapStart(len(v))
247	if e.h.Canonical {
248		{{if eq .MapKey "interface{}"}}{{/* out of band 
249		*/}}var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding
250		e2 := NewEncoderBytes(&mksv, e.hh)
251		v2 := make([]bytesI, len(v))
252		var i, l int
253		var vp *bytesI {{/* put loop variables outside. seems currently needed for better perf */}}
254		for k2, _ := range v {
255			l = len(mksv)
256			e2.MustEncode(k2)
257			vp = &v2[i]
258			vp.v = mksv[l:]
259			vp.i = k2 
260			i++
261		}
262		sort.Sort(bytesISlice(v2))
263		if esep {
264			for j := range v2 {
265				ee.WriteMapElemKey()
266				e.asis(v2[j].v)
267				ee.WriteMapElemValue()
268				e.encode(v[v2[j].i])
269			}
270		} else {
271			for j := range v2 {
272				e.asis(v2[j].v)
273				e.encode(v[v2[j].i])
274			}
275		} {{/*
276		for j := range v2 {
277			if esep { ee.WriteMapElemKey() }
278			e.asis(v2[j].v)
279			if esep { ee.WriteMapElemValue() }
280			e.encode(v[v2[j].i])
281		} */}} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
282		var i int 
283		for k, _ := range v {
284			v2[i] = {{ $x }}(k)
285			i++
286		}
287		sort.Sort({{ sorttype .MapKey false}}(v2))
288		if esep {
289			for _, k2 := range v2 {
290				ee.WriteMapElemKey()
291				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
292				ee.WriteMapElemValue()
293				{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
294			} 
295		} else {
296			for _, k2 := range v2 {
297				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
298				{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
299			} 
300		} {{/*
301		for _, k2 := range v2 {
302			if esep { ee.WriteMapElemKey() }
303			{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
304			if esep { ee.WriteMapElemValue() }
305			{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
306		} */}} {{end}}
307	} else {
308		if esep {
309			for k2, v2 := range v {
310				ee.WriteMapElemKey()
311				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ encmd .MapKey "k2"}}{{end}}
312				ee.WriteMapElemValue()
313				{{ encmd .Elem "v2"}}
314			}
315		} else {
316			for k2, v2 := range v {
317				{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ encmd .MapKey "k2"}}{{end}}
318				{{ encmd .Elem "v2"}}
319			}
320		} {{/*
321		for k2, v2 := range v {
322			if esep { ee.WriteMapElemKey() }
323			{{if eq .MapKey "string"}}ee.EncodeString(cUTF8, k2){{else}}{{ encmd .MapKey "k2"}}{{end}}
324			if esep { ee.WriteMapElemValue() }
325			{{ encmd .Elem "v2"}}
326		} */}}
327	}
328	ee.WriteMapEnd()
329}
330{{end}}{{end}}{{end}}
331
332// -- decode
333
334// -- -- fast path type switch
335func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
336     var changed bool
337	switch v := iv.(type) {
338{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
339	case []{{ .Elem }}:
340        var v2 []{{ .Elem }}
341		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, false, d)
342        if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
343			copy(v, v2)
344		}
345	case *[]{{ .Elem }}:
346        var v2 []{{ .Elem }}
347		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
348        if changed {
349			*v = v2 
350		}{{/*
351*/}}{{end}}{{end}}{{end}}{{end}}
352{{range .Values}}{{if not .Primitive}}{{if .MapKey }}{{/*
353// maps only change if nil, and in that case, there's no point copying
354*/}}
355	case map[{{ .MapKey }}]{{ .Elem }}:
356		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, false, d)
357	case *map[{{ .MapKey }}]{{ .Elem }}:
358         var v2 map[{{ .MapKey }}]{{ .Elem }}
359		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
360        if changed {
361			*v = v2 
362		}{{/*
363*/}}{{end}}{{end}}{{end}}
364	default:
365        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
366		return false
367	}
368	return true
369}
370
371func fastpathDecodeSetZeroTypeSwitch(iv interface{}) bool {
372	switch v := iv.(type) {
373{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
374	case *[]{{ .Elem }}: 
375		*v = nil {{/*
376*/}}{{end}}{{end}}{{end}}
377{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
378	case *map[{{ .MapKey }}]{{ .Elem }}: 
379		*v = nil {{/*
380*/}}{{end}}{{end}}{{end}}
381	default:
382        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
383		return false
384	}
385	return true
386}
387
388// -- -- fast path functions
389{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
390{{/*
391Slices can change if they 
392- did not come from an array
393- are addressable (from a ptr)
394- are settable (e.g. contained in an interface{})
395*/}}
396func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) {
397	if array := f.seq == seqTypeArray; !array && rv.Kind() == reflect.Ptr {
398		vp := rv2i(rv).(*[]{{ .Elem }})
399		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, !array, d)
400        if changed { *vp = v }
401	} else {
402		v := rv2i(rv).([]{{ .Elem }})
403        v2, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, !array, d)
404        if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
405           copy(v, v2)
406        }
407	}
408}
409func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, d *Decoder) {
410	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
411    if changed { *vp = v }
412}
413func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
414	dd := d.d{{/*
415	    // if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil()
416    */}}
417	slh, containerLenS := d.decSliceHelperStart()
418	if containerLenS == 0 {
419		if canChange {
420			if v == nil { v = []{{ .Elem }}{} } else if len(v) != 0 { v = v[:0] }
421			changed = true
422		}
423		slh.End()
424		return v, changed
425	}
426	hasLen := containerLenS > 0
427	var xlen int 
428	if hasLen && canChange {
429		if containerLenS > cap(v) {
430			xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
431			if xlen <= cap(v) {
432				v = v[:xlen]
433			} else {
434				v = make([]{{ .Elem }}, xlen)
435			}
436			changed = true 
437		} else if containerLenS != len(v) {
438			v = v[:containerLenS]
439			changed = true
440		}
441	}
442	j := 0
443	for ; (hasLen && j < containerLenS) || !(hasLen || dd.CheckBreak()); j++ {
444		if j == 0 && len(v) == 0 && canChange {
445			if hasLen {
446				xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
447			} else {
448				xlen = 8
449			}
450			v = make([]{{ .Elem }}, xlen)
451			changed = true 
452		}
453		// if indefinite, etc, then expand the slice if necessary
454		var decodeIntoBlank bool
455		if j >= len(v) {
456			if canChange {
457				v = append(v, {{ zerocmd .Elem }})
458				changed = true
459			} else {
460				d.arrayCannotExpand(len(v), j+1)
461				decodeIntoBlank = true
462			}
463		} 
464		slh.ElemContainerState(j)
465		if decodeIntoBlank {
466			d.swallow()
467		} else if dd.TryDecodeAsNil() {
468			v[j] = {{ zerocmd .Elem }}
469		} else {
470			{{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
471		}
472	}
473	if canChange {
474		if j < len(v) {
475			v = v[:j]
476			changed = true
477		} else if j == 0 && v == nil {
478			v = make([]{{ .Elem }}, 0)
479			changed = true
480		}
481	}
482	slh.End()
483	return v, changed 
484}
485{{end}}{{end}}{{end}}
486
487{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
488{{/*
489Maps can change if they are
490- addressable (from a ptr)
491- settable (e.g. contained in an interface{})
492*/}}
493func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) {
494	if rv.Kind() == reflect.Ptr {
495		vp := rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})
496		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d);
497		if changed { *vp = v }
498	} else {
499	    fastpathTV.{{ .MethodNamePfx "Dec" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), false, d)
500    }
501}
502func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) {
503	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
504	if changed { *vp = v }
505}
506func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, canChange bool, 
507	d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
508	dd, esep := d.d, d.hh.hasElemSeparators(){{/*
509	    // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil()
510    */}}
511	containerLen := dd.ReadMapStart()
512	if canChange && v == nil {
513		xlen := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
514		v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
515		changed = true
516	}
517	if containerLen == 0 {
518		dd.ReadMapEnd()
519		return v, changed
520	}
521	{{ if eq .Elem "interface{}" }}mapGet := v != nil && !d.h.MapValueReset && !d.h.InterfaceReset
522    {{end}}var mk {{ .MapKey }}
523	var mv {{ .Elem }}
524	hasLen := containerLen > 0
525	for j := 0; (hasLen && j < containerLen) || !(hasLen || dd.CheckBreak()); j++ {
526		if esep { dd.ReadMapElemKey() }
527		{{ if eq .MapKey "interface{}" }}mk = nil 
528		d.decode(&mk)
529		if bv, bok := mk.([]byte); bok {
530			mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
531		}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
532		if esep { dd.ReadMapElemValue() }
533		if dd.TryDecodeAsNil() {
534			if v == nil {} else if d.h.DeleteOnNilMapValue { delete(v, mk) } else { v[mk] = {{ zerocmd .Elem }} }
535			continue 
536		}
537		{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
538		d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
539		if v != nil { v[mk] = mv }
540	}
541	dd.ReadMapEnd()
542	return v, changed
543}
544{{end}}{{end}}{{end}}