Files
pig-farm-controller/vendor/github.com/ugorji/go/codec/gen.go

427 lines
10 KiB
Go

// 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.
//go:build codec.build
package codec
import (
"bytes"
"encoding/base32"
"errors"
"fmt"
"go/format"
"io"
"os"
"regexp"
"strings"
"sync"
"text/template"
// "ugorji.net/zz"
)
// ---------------------------------------------------
const (
genTopLevelVarName = "x"
// genFastpathCanonical configures whether we support Canonical in fast path. Low savings.
//
// MARKER: This MUST ALWAYS BE TRUE. fastpath.go.tmpl doesn't handle it being false.
genFastpathCanonical = true
// genFastpathTrimTypes configures whether we trim uncommon fastpath types.
genFastpathTrimTypes = true
)
var genFormats = []string{"Json", "Cbor", "Msgpack", "Binc", "Simple"}
var (
errGenAllTypesSamePkg = errors.New("All types must be in the same package")
errGenExpectArrayOrMap = errors.New("unexpected type - expecting array/map/slice")
errGenUnexpectedTypeFastpath = errors.New("fastpath: unexpected type - requires map or slice")
// don't use base64, only 63 characters allowed in valid go identifiers
// ie ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_
//
// don't use numbers, as a valid go identifer must start with a letter.
genTypenameEnc = base32.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
genQNameRegex = regexp.MustCompile(`[A-Za-z_.]+`)
)
// --------
func genCheckErr(err error) {
halt.onerror(err)
}
func genTitleCaseName(s string) string {
switch s {
case "interface{}", "interface {}":
return "Intf"
case "[]byte", "[]uint8", "bytes":
return "Bytes"
default:
return strings.ToUpper(s[0:1]) + s[1:]
}
}
// --------
type genFastpathV struct {
// genFastpathV is either a primitive (Primitive != "") or a map (MapKey != "") or a slice
MapKey string
Elem string
Primitive string
Size int
NoCanonical bool
}
func (x *genFastpathV) MethodNamePfx(prefix string, prim bool) string {
var name []byte
if prefix != "" {
name = append(name, prefix...)
}
if prim {
name = append(name, genTitleCaseName(x.Primitive)...)
} else {
if x.MapKey == "" {
name = append(name, "Slice"...)
} else {
name = append(name, "Map"...)
name = append(name, genTitleCaseName(x.MapKey)...)
}
name = append(name, genTitleCaseName(x.Elem)...)
}
return string(name)
}
// --------
type genTmpl struct {
Values []genFastpathV
Formats []string
}
func (x genTmpl) FastpathLen() (l int) {
for _, v := range x.Values {
// if v.Primitive == "" && !(v.MapKey == "" && v.Elem == "uint8") {
if v.Primitive == "" {
l++
}
}
return
}
func genTmplZeroValue(s string) string {
switch s {
case "interface{}", "interface {}":
return "nil"
case "[]byte", "[]uint8", "bytes":
return "nil"
case "bool":
return "false"
case "string":
return `""`
default:
return "0"
}
}
var genTmplNonZeroValueIdx [6]uint64
var genTmplNonZeroValueStrs = [...][6]string{
{`"string-is-an-interface-1"`, "true", `"some-string-1"`, `[]byte("some-string-1")`, "11.1", "111"},
{`"string-is-an-interface-2"`, "false", `"some-string-2"`, `[]byte("some-string-2")`, "22.2", "77"},
{`"string-is-an-interface-3"`, "true", `"some-string-3"`, `[]byte("some-string-3")`, "33.3e3", "127"},
}
// Note: last numbers must be in range: 0-127 (as they may be put into a int8, uint8, etc)
func genTmplNonZeroValue(s string) string {
var i int
switch s {
case "interface{}", "interface {}":
i = 0
case "bool":
i = 1
case "string":
i = 2
case "bytes", "[]byte", "[]uint8":
i = 3
case "float32", "float64", "float", "double", "complex", "complex64", "complex128":
i = 4
default:
i = 5
}
genTmplNonZeroValueIdx[i]++
idx := genTmplNonZeroValueIdx[i]
slen := uint64(len(genTmplNonZeroValueStrs))
return genTmplNonZeroValueStrs[idx%slen][i] // return string, to remove ambiguity
}
// Note: used for fastpath only
func genTmplEncCommandAsString(s string, vname string) string {
switch s {
case "uint64":
return "e.e.EncodeUint(" + vname + ")"
case "uint", "uint8", "uint16", "uint32":
return "e.e.EncodeUint(uint64(" + vname + "))"
case "int64":
return "e.e.EncodeInt(" + vname + ")"
case "int", "int8", "int16", "int32":
return "e.e.EncodeInt(int64(" + vname + "))"
case "[]byte", "[]uint8", "bytes":
// return fmt.Sprintf(
// "if %s != nil { e.e.EncodeStringBytesRaw(%s) } "+
// "else if e.h.NilCollectionToZeroLength { e.e.WriteArrayEmpty() } "+
// "else { e.e.EncodeNil() }", vname, vname)
// return "e.e.EncodeStringBytesRaw(" + vname + ")"
return "e.e.EncodeBytes(" + vname + ")"
case "string":
return "e.e.EncodeString(" + vname + ")"
case "float32":
return "e.e.EncodeFloat32(" + vname + ")"
case "float64":
return "e.e.EncodeFloat64(" + vname + ")"
case "bool":
return "e.e.EncodeBool(" + vname + ")"
// case "symbol":
// return "e.e.EncodeSymbol(" + vname + ")"
default:
return fmt.Sprintf("if !e.encodeBuiltin(%s) { e.encodeR(reflect.ValueOf(%s)) }", vname, vname)
// return "e.encodeI(" + vname + ")"
}
}
// Note: used for fastpath only
func genTmplDecCommandAsString(s string, mapkey bool) string {
switch s {
case "uint":
return "uint(chkOvf.UintV(d.d.DecodeUint64(), uintBitsize))"
case "uint8":
return "uint8(chkOvf.UintV(d.d.DecodeUint64(), 8))"
case "uint16":
return "uint16(chkOvf.UintV(d.d.DecodeUint64(), 16))"
case "uint32":
return "uint32(chkOvf.UintV(d.d.DecodeUint64(), 32))"
case "uint64":
return "d.d.DecodeUint64()"
case "uintptr":
return "uintptr(chkOvf.UintV(d.d.DecodeUint64(), uintBitsize))"
case "int":
return "int(chkOvf.IntV(d.d.DecodeInt64(), intBitsize))"
case "int8":
return "int8(chkOvf.IntV(d.d.DecodeInt64(), 8))"
case "int16":
return "int16(chkOvf.IntV(d.d.DecodeInt64(), 16))"
case "int32":
return "int32(chkOvf.IntV(d.d.DecodeInt64(), 32))"
case "int64":
return "d.d.DecodeInt64()"
case "string":
// if mapkey {
// return "d.stringZC(d.d.DecodeStringAsBytes())"
// }
// return "string(d.d.DecodeStringAsBytes())"
return "d.detach2Str(d.d.DecodeStringAsBytes())"
case "[]byte", "[]uint8", "bytes":
// return "bytesOk(d.d.DecodeBytes())"
return "bytesOKdbi(d.decodeBytesInto(v[uint(j)], false))"
case "float32":
return "float32(d.d.DecodeFloat32())"
case "float64":
return "d.d.DecodeFloat64()"
case "complex64":
return "complex(d.d.DecodeFloat32(), 0)"
case "complex128":
return "complex(d.d.DecodeFloat64(), 0)"
case "bool":
return "d.d.DecodeBool()"
default:
halt.error(errors.New("gen internal: unknown type for decode: " + s))
}
return ""
}
func genTmplSortType(s string, elem bool) string {
if elem {
return s
}
return s + "Slice"
}
// var genTmplMu sync.Mutex
var genTmplV = genTmpl{}
var genTmplFuncs template.FuncMap
var genTmplOnce sync.Once
func genTmplInit() {
wordSizeBytes := int(intBitsize) / 8
typesizes := map[string]int{
"interface{}": 2 * wordSizeBytes,
"string": 2 * wordSizeBytes,
"[]byte": 3 * wordSizeBytes,
"uint": 1 * wordSizeBytes,
"uint8": 1,
"uint16": 2,
"uint32": 4,
"uint64": 8,
"uintptr": 1 * wordSizeBytes,
"int": 1 * wordSizeBytes,
"int8": 1,
"int16": 2,
"int32": 4,
"int64": 8,
"float32": 4,
"float64": 8,
"complex64": 8,
"complex128": 16,
"bool": 1,
}
// keep as slice, so it is in specific iteration order.
// Initial order was uint64, string, interface{}, int, int64, ...
var types = [...]string{
"interface{}",
"string",
"[]byte",
"float32",
"float64",
"uint",
"uint8",
"uint16",
"uint32",
"uint64",
"uintptr",
"int",
"int8",
"int16",
"int32",
"int64",
"bool",
}
var primitivetypes, slicetypes, mapkeytypes, mapvaltypes []string
primitivetypes = types[:]
slicetypes = types[:]
mapkeytypes = types[:]
mapvaltypes = types[:]
if genFastpathTrimTypes {
// Note: we only create fastpaths for commonly used types.
// Consequently, things like int8, uint16, uint, etc are commented out.
slicetypes = []string{
"interface{}",
"string",
"[]byte",
"float32",
"float64",
"uint8", // keep fastpath, so it doesn't have to go through reflection
"uint64",
"int",
"int32", // rune
"int64",
"bool",
}
mapkeytypes = []string{
"string",
"uint8", // byte
"uint64", // used for keys
"int", // default number key
"int32", // rune
}
mapvaltypes = []string{
"interface{}",
"string",
"[]byte",
"uint8", // byte
"uint64", // used for keys, etc
"int", // default number
"int32", // rune (mostly used for unicode)
"float64",
"bool",
}
}
var gt = genTmpl{Formats: genFormats}
// For each slice or map type, there must be a (symmetrical) Encode and Decode fastpath function
for _, s := range primitivetypes {
gt.Values = append(gt.Values,
genFastpathV{Primitive: s, Size: typesizes[s], NoCanonical: !genFastpathCanonical})
}
for _, s := range slicetypes {
gt.Values = append(gt.Values,
genFastpathV{Elem: s, Size: typesizes[s], NoCanonical: !genFastpathCanonical})
}
for _, s := range mapkeytypes {
for _, ms := range mapvaltypes {
gt.Values = append(gt.Values,
genFastpathV{MapKey: s, Elem: ms, Size: typesizes[s] + typesizes[ms], NoCanonical: !genFastpathCanonical})
}
}
funcs := make(template.FuncMap)
// funcs["haspfx"] = strings.HasPrefix
funcs["encmd"] = genTmplEncCommandAsString
funcs["decmd"] = genTmplDecCommandAsString
funcs["zerocmd"] = genTmplZeroValue
funcs["nonzerocmd"] = genTmplNonZeroValue
funcs["hasprefix"] = strings.HasPrefix
funcs["sorttype"] = genTmplSortType
genTmplV = gt
genTmplFuncs = funcs
}
// genTmplGoFile is used to generate source files from templates.
func genTmplGoFile(r io.Reader, w io.Writer) (err error) {
genTmplOnce.Do(genTmplInit)
gt := genTmplV
t := template.New("").Funcs(genTmplFuncs)
tmplstr, err := io.ReadAll(r)
if err != nil {
return
}
if t, err = t.Parse(string(tmplstr)); err != nil {
return
}
var out bytes.Buffer
err = t.Execute(&out, gt)
if err != nil {
return
}
bout, err := format.Source(out.Bytes())
if err != nil {
w.Write(out.Bytes()) // write out if error, so we can still see.
// w.Write(bout) // write out if error, as much as possible, so we can still see.
return
}
w.Write(bout)
return
}
func genTmplRun2Go(fnameIn, fnameOut string) {
// println("____ " + fnameIn + " --> " + fnameOut + " ______")
fin, err := os.Open(fnameIn)
genCheckErr(err)
defer fin.Close()
fout, err := os.Create(fnameOut)
genCheckErr(err)
defer fout.Close()
err = genTmplGoFile(fin, fout)
genCheckErr(err)
}