//+build ignore // Copyright (c) 2012, 2013 Ugorji Nwoke. All rights reserved. // Use of this source code is governed by a BSD-style license found in the LICENSE file. package main import ( "bytes" "go/format" "os" "strings" "text/template" ) const tmplstr = ` // Copyright (c) 2012, 2013 Ugorji Nwoke. All rights reserved. // Use of this source code is governed by a BSD-style license found in the LICENSE file. // ************************************************************ // DO NOT EDIT. // THIS FILE IS GENERATED BY RUNNING: go run gen-fast-path.go // ************************************************************ 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" ) func init() { if !fastpathEnabled { return // basically disable the fast path checks (since accessing empty map is basically free) } fx := func(i interface{}, fe func(*encFnInfo, reflect.Value), fd func(*decFnInfo, reflect.Value)) { xrt := reflect.TypeOf(i) xptr := reflect.ValueOf(xrt).Pointer() fastpathsTyp[xptr] = xrt fastpathsEnc[xptr] = fe fastpathsDec[xptr] = fd } {{range .Values}}{{if .Slice }} fx([]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodName true }}, (*decFnInfo).{{ .MethodName false}}){{end}}{{end}} {{range .Values}}{{if not .Slice }} fx(map[{{ .MapKey }}]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodName true }}, (*decFnInfo).{{ .MethodName false}}){{end}}{{end}} } // -- encode {{range .Values}}{{if .Slice }} func (f *encFnInfo) {{ .MethodName true }}(rv reflect.Value) { v := rv.Interface().([]{{ .Elem }}) f.ee.encodeArrayPreamble(len(v)) for _, v2 := range v { {{ encmd .Elem "v2"}} } } {{end}}{{end}} {{range .Values}}{{if not .Slice }} func (f *encFnInfo) {{ .MethodName true }}(rv reflect.Value) { v := rv.Interface().(map[{{ .MapKey }}]{{ .Elem }}) f.ee.encodeMapPreamble(len(v)) {{if eq .MapKey "string"}}asSymbols := f.e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0{{end}} for k2, v2 := range v { {{if eq .MapKey "string"}}if asSymbols { f.ee.encodeSymbol(k2) } else { f.ee.encodeString(c_UTF8, k2) }{{else}}{{ encmd .MapKey "k2"}}{{end}} {{ encmd .Elem "v2"}} } } {{end}}{{end}} // -- decode {{range .Values}}{{if .Slice }} func (f *decFnInfo) {{ .MethodName false }}(rv reflect.Value) { xaddr := rv.CanAddr() var vp *[]{{ .Elem }} var v []{{ .Elem }} if xaddr { vp = rv.Addr().Interface().(*[]{{ .Elem }}) v = *vp } else { v = rv.Interface().([]{{ .Elem }}) } vtype := f.dd.currentEncodedType() if vtype == valueTypeNil { if xaddr { v = nil *vp = v } // else do nothing. return } _, containerLenS := decContLens(f.dd, vtype) if v == nil { v = make([]{{ .Elem }}, containerLenS, containerLenS) } else if containerLenS > cap(v) { if f.array { decErr(msgDecCannotExpandArr, cap(v), containerLenS) } s := make([]{{ .Elem }}, containerLenS, containerLenS) copy(s, v) v = s } else if containerLenS > len(v) { v = v[:containerLenS] } for j := 0; j < containerLenS; j++ { {{ if eq .Elem "interface{}" }}f.d.decode(&v[j]) {{ else }}f.dd.initReadNext() v[j] = {{ decmd .Elem }} {{ end }} } if xaddr { *vp = v } } {{end}}{{end}} {{range .Values}}{{if not .Slice }} func (f *decFnInfo) {{ .MethodName false }}(rv reflect.Value) { xaddr := rv.CanAddr() var vp (*map[{{ .MapKey }}]{{ .Elem }}) var v map[{{ .MapKey }}]{{ .Elem }} if xaddr { vp = rv.Addr().Interface().(*map[{{ .MapKey }}]{{ .Elem }}) v = *vp } else { v = rv.Interface().(map[{{ .MapKey }}]{{ .Elem }}) } vtype := f.dd.currentEncodedType() if vtype == valueTypeNil { if xaddr { v = nil *vp = v } // else do nothing. We never remove from a map. return } containerLen := f.dd.readMapLen() if xaddr && v == nil { v = make(map[{{ .MapKey }}]{{ .Elem }}, containerLen) *vp = v } for j := 0; j < containerLen; j++ { {{ if eq .MapKey "interface{}" }}var mk interface{} f.d.decode(&mk) // special case if a byte array. if bv, bok := mk.([]byte); bok { mk = string(bv) } {{ else }}f.dd.initReadNext() mk := {{ decmd .MapKey }} {{ end }} mv := v[mk] {{ if eq .Elem "interface{}" }}f.d.decode(&mv) {{ else }}f.dd.initReadNext() mv = {{ decmd .Elem }} {{ end }} if v != nil { v[mk] = mv } } } {{end}}{{end}} ` type genInfo struct { Slice bool MapKey string Elem string } func EncCommandAsString(s string, vname string) string { switch s { case "uint", "uint8", "uint16", "uint32", "uint64": return "f.ee.encodeUint(uint64(" + vname + "))" case "int", "int8", "int16", "int32", "int64": return "f.ee.encodeInt(int64(" + vname + "))" case "string": return "f.ee.encodeString(c_UTF8, " + vname + ")" case "float32": return "f.ee.encodeFloat32(" + vname + ")" case "float64": return "f.ee.encodeFloat64(" + vname + ")" case "bool": return "f.ee.encodeBool(" + vname + ")" case "symbol": return "f.ee.encodeSymbol(" + vname + ")" default: return "f.e.encode(" + vname + ")" } } func DecCommandAsString(s string) string { switch s { case "uint": return "uint(f.dd.decodeUint(uintBitsize))" case "uint8": return "uint8(f.dd.decodeUint(8))" case "uint16": return "uint16(f.dd.decodeUint(16))" case "uint32": return "uint32(f.dd.decodeUint(32))" case "uint64": return "f.dd.decodeUint(64)" case "int": return "int(f.dd.decodeInt(intBitsize))" case "int8": return "int8(f.dd.decodeInt(8))" case "int16": return "int16(f.dd.decodeInt(16))" case "int32": return "int32(f.dd.decodeInt(32))" case "int64": return "f.dd.decodeInt(64)" case "string": return "f.dd.decodeString()" case "float32": return "float32(f.dd.decodeFloat(true))" case "float64": return "f.dd.decodeFloat(false)" case "bool": return "f.dd.decodeBool()" default: panic("unknown type for decode: " + s) } } func (x *genInfo) MethodName(encode bool) string { var name []byte name = append(name, "fast"...) if encode { name = append(name, "Enc"...) } else { name = append(name, "Dec"...) } if x.Slice { name = append(name, "Slice"...) } else { name = append(name, "Map"...) name = append(name, titleCaseName(x.MapKey)...) } name = append(name, titleCaseName(x.Elem)...) return string(name) } func titleCaseName(s string) string { switch s { case "interface{}": return "Intf" default: return strings.ToUpper(s[0:1]) + s[1:] } } type genTmpl struct { Values []genInfo } func main() { types := []string{ "interface{}", "string", "float32", "float64", "uint", "uint8", "uint16", "uint32", "uint64", "int", "int8", "int16", "int32", "int64", "bool", } // keep as slice, so it is in specific iteration order. // Initial order was uint64, string, interface{}, int, int64 mapvaltypes := []string{ "interface{}", "string", "uint", "uint32", "uint64", "int", "int32", "int64", } mapvaltypes2 := make(map[string]bool) for _, s := range mapvaltypes { mapvaltypes2[s] = true } var gt genTmpl // For each slice or map type, there must be a (symetrical) Encode and Decode fast-path function for _, s := range types { if s != "uint8" { // do not generate fast path for slice of bytes. Treat specially already. gt.Values = append(gt.Values, genInfo{true, "", s}) } if !mapvaltypes2[s] { gt.Values = append(gt.Values, genInfo{false, s, s}) } for _, ms := range mapvaltypes { gt.Values = append(gt.Values, genInfo{false, s, ms}) } } funcs := make(template.FuncMap) // funcs["haspfx"] = strings.HasPrefix funcs["encmd"] = EncCommandAsString funcs["decmd"] = DecCommandAsString t := template.New("") t = t.Funcs(funcs) t, err := t.Parse(tmplstr) if err != nil { panic(err) } var out bytes.Buffer err = t.Execute(&out, >) if err != nil { panic(err) } // os.Stdout.Write(out.Bytes()) bout, err := format.Source(out.Bytes()) if err != nil { panic(err) } os.Stdout.Write(bout) }