aboutsummaryrefslogtreecommitdiffstats
path: root/binapigen/generate.go
diff options
context:
space:
mode:
Diffstat (limited to 'binapigen/generate.go')
-rw-r--r--binapigen/generate.go1518
1 files changed, 324 insertions, 1194 deletions
diff --git a/binapigen/generate.go b/binapigen/generate.go
index d35427f..689463e 100644
--- a/binapigen/generate.go
+++ b/binapigen/generate.go
@@ -16,1338 +16,468 @@ package binapigen
import (
"fmt"
- "io"
+ "path"
"sort"
+ "strconv"
"strings"
- "git.fd.io/govpp.git/version"
- "github.com/sirupsen/logrus"
+ "git.fd.io/govpp.git/internal/version"
)
-// generatedCodeVersion indicates a version of the generated code.
-// It is incremented whenever an incompatibility between the generated code and
-// GoVPP api package is introduced; the generated code references
-// a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion).
-const generatedCodeVersion = 2
-
-// common message fields
+// library dependencies
const (
- msgIdField = "_vl_msg_id"
- clientIndexField = "client_index"
- contextField = "context"
- retvalField = "retval"
-)
+ strconvPkg = GoImportPath("strconv")
-// global API info
-const (
- constModuleName = "ModuleName" // module name constant
- constAPIVersion = "APIVersion" // API version constant
- constVersionCrc = "VersionCrc" // version CRC constant
+ govppApiPkg = GoImportPath("git.fd.io/govpp.git/api")
+ govppCodecPkg = GoImportPath("git.fd.io/govpp.git/codec")
)
-// generated fiels
+// generated names
const (
- unionDataField = "XXX_UnionData" // name for the union data field
-)
-
-// MessageType represents the type of a VPP message
-type MessageType int
+ apiName = "APIFile" // API file name
+ apiVersion = "APIVersion" // API version number
+ apiCrc = "VersionCrc" // version checksum
-const (
- requestMessage MessageType = iota // VPP request message
- replyMessage // VPP reply message
- eventMessage // VPP event message
- otherMessage // other VPP message
+ fieldUnionData = "XXX_UnionData" // name for the union data field
)
-func generateFileBinapi(ctx *GenFile, w io.Writer) {
+func GenerateAPI(gen *Generator, file *File) *GenFile {
logf("----------------------------")
- logf("generating BINAPI file package: %q", ctx.file.PackageName)
+ logf(" Generate API - %s", file.Desc.Name)
logf("----------------------------")
- // generate file header
- fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
- fmt.Fprintln(w, "// versions:")
- fmt.Fprintf(w, "// binapi-generator: %s\n", version.Version())
- if ctx.IncludeVppVersion {
- fmt.Fprintf(w, "// VPP: %s\n", ctx.VPPVersion)
+ filename := path.Join(file.FilenamePrefix, file.Desc.Name+".ba.go")
+ g := gen.NewGenFile(filename, file.GoImportPath)
+ g.file = file
+
+ g.P("// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
+ if !gen.opts.NoVersionInfo {
+ g.P("// versions:")
+ g.P("// binapi-generator: ", version.Version())
+ g.P("// VPP: ", g.gen.vppVersion)
+ g.P("// source: ", g.file.Desc.Path)
}
- fmt.Fprintf(w, "// source: %s\n", ctx.file.Path)
- fmt.Fprintln(w)
+ g.P()
- generatePackageHeader(ctx, w)
- generateImports(ctx, w)
+ genPackageComment(g)
+ g.P("package ", file.PackageName)
+ g.P()
- generateApiInfo(ctx, w)
- generateTypes(ctx, w)
- generateMessages(ctx, w)
+ for _, imp := range g.file.Imports {
+ genImport(g, imp)
+ }
- generateImportRefs(ctx, w)
-}
+ // generate version assertion
+ g.P("// This is a compile-time assertion to ensure that this generated file")
+ g.P("// is compatible with the GoVPP api package it is being compiled against.")
+ g.P("// A compilation error at this line likely means your copy of the")
+ g.P("// GoVPP api package needs to be updated.")
+ g.P("const _ = ", govppApiPkg.Ident("GoVppAPIPackageIsVersion"), generatedCodeVersion)
+ g.P()
-func generatePackageHeader(ctx *GenFile, w io.Writer) {
- fmt.Fprintln(w, "/*")
- fmt.Fprintf(w, "Package %s contains generated code for VPP API file %s.api (%s).\n",
- ctx.file.PackageName, ctx.file.Name, ctx.file.Version())
- fmt.Fprintln(w)
- fmt.Fprintln(w, "It consists of:")
- printObjNum := func(obj string, num int) {
- if num > 0 {
- if num > 1 {
- if strings.HasSuffix(obj, "s") {
- obj += "es"
- } else {
- obj += "s"
- }
- }
- fmt.Fprintf(w, "\t%3d %s\n", num, obj)
- }
+ if !file.isTypesFile() {
+ g.P("const (")
+ g.P(apiName, " = ", strconv.Quote(g.file.Desc.Name))
+ g.P(apiVersion, " = ", strconv.Quote(g.file.Version))
+ g.P(apiCrc, " = ", g.file.Desc.CRC)
+ g.P(")")
+ g.P()
}
- printObjNum("alias", len(ctx.file.Aliases))
- printObjNum("enum", len(ctx.file.Enums))
- printObjNum("message", len(ctx.file.Messages))
- printObjNum("type", len(ctx.file.Structs))
- printObjNum("union", len(ctx.file.Unions))
- fmt.Fprintln(w, "*/")
- fmt.Fprintf(w, "package %s\n", ctx.file.PackageName)
- fmt.Fprintln(w)
-}
-func generateImports(ctx *GenFile, w io.Writer) {
- fmt.Fprintln(w, "import (")
- fmt.Fprintln(w, ` "bytes"`)
- fmt.Fprintln(w, ` "context"`)
- fmt.Fprintln(w, ` "encoding/binary"`)
- fmt.Fprintln(w, ` "fmt"`)
- fmt.Fprintln(w, ` "io"`)
- fmt.Fprintln(w, ` "math"`)
- fmt.Fprintln(w, ` "net"`)
- fmt.Fprintln(w, ` "strconv"`)
- fmt.Fprintln(w, ` "strings"`)
- fmt.Fprintln(w)
- fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
- fmt.Fprintf(w, "\tcodec \"%s\"\n", "git.fd.io/govpp.git/codec")
- fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
- imports := listImports(ctx)
- if len(imports) > 0 {
- fmt.Fprintln(w)
- for imp, importPath := range imports {
- fmt.Fprintf(w, "\t%s \"%s\"\n", imp, importPath)
- }
+ for _, enum := range g.file.Enums {
+ genEnum(g, enum)
}
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w)
-
- fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
- fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
- fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the")
- fmt.Fprintln(w, "// GoVPP api package needs to be updated.")
- fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
- fmt.Fprintln(w)
-}
-
-func generateApiInfo(ctx *GenFile, w io.Writer) {
- // generate module desc
- fmt.Fprintln(w, "const (")
- fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName)
- fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.file.Name)
-
- if ctx.IncludeAPIVersion {
- fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion)
- fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.file.Version())
- fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc)
- fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.file.CRC)
+ for _, alias := range g.file.Aliases {
+ genAlias(g, alias)
}
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w)
-}
-
-func generateTypes(ctx *GenFile, w io.Writer) {
- // generate enums
- if len(ctx.file.Enums) > 0 {
- for _, enum := range ctx.file.Enums {
- if imp, ok := ctx.file.imports[enum.Name]; ok {
- if strings.HasSuffix(ctx.file.Name, "_types") {
- generateImportedAlias(ctx, w, enum.GoName, imp)
- }
- continue
- }
- generateEnum(ctx, w, enum)
- }
+ for _, typ := range g.file.Structs {
+ genStruct(g, typ)
}
-
- // generate aliases
- if len(ctx.file.Aliases) > 0 {
- for _, alias := range ctx.file.Aliases {
- if imp, ok := ctx.file.imports[alias.Name]; ok {
- if strings.HasSuffix(ctx.file.Name, "_types") {
- generateImportedAlias(ctx, w, alias.GoName, imp)
- }
- continue
- }
- generateAlias(ctx, w, alias)
- }
+ for _, union := range g.file.Unions {
+ genUnion(g, union)
}
+ genMessages(g)
- // generate types
- if len(ctx.file.Structs) > 0 {
- for _, typ := range ctx.file.Structs {
- if imp, ok := ctx.file.imports[typ.Name]; ok {
- if strings.HasSuffix(ctx.file.Name, "_types") {
- generateImportedAlias(ctx, w, typ.GoName, imp)
- }
- continue
- }
- generateStruct(ctx, w, typ)
- }
- }
+ return g
+}
- // generate unions
- if len(ctx.file.Unions) > 0 {
- for _, union := range ctx.file.Unions {
- if imp, ok := ctx.file.imports[union.Name]; ok {
- if strings.HasSuffix(ctx.file.Name, "_types") {
- generateImportedAlias(ctx, w, union.GoName, imp)
+func genPackageComment(g *GenFile) {
+ apifile := g.file.Desc.Name + ".api"
+ g.P("// Package ", g.file.PackageName, " contains generated bindings for API file ", apifile, ".")
+ g.P("//")
+ g.P("// Contents:")
+ printObjNum := func(obj string, num int) {
+ if num > 0 {
+ if num > 1 {
+ if strings.HasSuffix(obj, "s") {
+ obj += "es"
+ } else {
+ obj += "s"
}
- continue
}
- generateUnion(ctx, w, union)
+ g.P("// ", fmt.Sprintf("%3d", num), " ", obj)
}
}
+ printObjNum("alias", len(g.file.Aliases))
+ printObjNum("enum", len(g.file.Enums))
+ printObjNum("struct", len(g.file.Structs))
+ printObjNum("union", len(g.file.Unions))
+ printObjNum("message", len(g.file.Messages))
+ g.P("//")
}
-func generateMessages(ctx *GenFile, w io.Writer) {
- if len(ctx.file.Messages) == 0 {
+func genImport(g *GenFile, imp string) {
+ impFile, ok := g.gen.FilesByName[imp]
+ if !ok {
return
}
-
- for _, msg := range ctx.file.Messages {
- generateMessage(ctx, w, msg)
- }
-
- // generate message registrations
- initFnName := fmt.Sprintf("file_%s_binapi_init", ctx.file.PackageName)
-
- fmt.Fprintf(w, "func init() { %s() }\n", initFnName)
- fmt.Fprintf(w, "func %s() {\n", initFnName)
- for _, msg := range ctx.file.Messages {
- fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n",
- msg.GoName, ctx.file.Name+"."+msg.GoName)
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-
- // generate list of messages
- fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n")
- fmt.Fprintln(w, "func AllMessages() []api.Message {")
- fmt.Fprintln(w, "\treturn []api.Message{")
- for _, msg := range ctx.file.Messages {
- fmt.Fprintf(w, "\t(*%s)(nil),\n", msg.GoName)
+ if impFile.GoImportPath == g.file.GoImportPath {
+ // Skip generating imports for types in the same package
+ return
}
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w, "}")
+ // Generate imports for all dependencies, even if not used
+ g.Import(impFile.GoImportPath)
}
-func generateImportRefs(ctx *GenFile, w io.Writer) {
- fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
- fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
- fmt.Fprintf(w, "var _ = codec.DecodeString\n")
- fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
- fmt.Fprintf(w, "var _ = context.Background\n")
- fmt.Fprintf(w, "var _ = io.Copy\n")
- fmt.Fprintf(w, "var _ = strconv.Itoa\n")
- fmt.Fprintf(w, "var _ = strings.Contains\n")
- fmt.Fprintf(w, "var _ = struc.Pack\n")
- fmt.Fprintf(w, "var _ = binary.BigEndian\n")
- fmt.Fprintf(w, "var _ = math.Float32bits\n")
- fmt.Fprintf(w, "var _ = net.ParseIP\n")
- fmt.Fprintf(w, "var _ = fmt.Errorf\n")
+func genTypeComment(g *GenFile, goName string, vppName string, objKind string) {
+ g.P("// ", goName, " defines ", objKind, " '", vppName, "'.")
}
-func generateComment(ctx *GenFile, w io.Writer, goName string, vppName string, objKind string) {
- if objKind == "service" {
- fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.file.Name)
- } else {
- fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
- }
-}
+func genEnum(g *GenFile, enum *Enum) {
+ logf("gen ENUM %s (%s) - %d entries", enum.GoName, enum.Name, len(enum.Entries))
-func generateEnum(ctx *GenFile, w io.Writer, enum *Enum) {
- name := enum.GoName
- typ := binapiTypes[enum.Type]
+ genTypeComment(g, enum.GoName, enum.Name, "enum")
- logf(" writing ENUM %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
+ gotype := BaseTypesGo[enum.Type]
- // generate enum comment
- generateComment(ctx, w, name, enum.Name, "enum")
-
- // generate enum definition
- fmt.Fprintf(w, "type %s %s\n", name, typ)
- fmt.Fprintln(w)
+ g.P("type ", enum.GoName, " ", gotype)
+ g.P()
// generate enum entries
- fmt.Fprintln(w, "const (")
+ g.P("const (")
for _, entry := range enum.Entries {
- fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value)
+ g.P(entry.Name, " ", enum.GoName, " = ", entry.Value)
}
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w)
+ g.P(")")
+ g.P()
// generate enum conversion maps
- fmt.Fprintln(w, "var (")
- fmt.Fprintf(w, "%s_name = map[%s]string{\n", name, typ)
+ g.P("var (")
+ g.P(enum.GoName, "_name = map[", gotype, "]string{")
for _, entry := range enum.Entries {
- fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name)
+ g.P(entry.Value, ": ", strconv.Quote(entry.Name), ",")
}
- fmt.Fprintln(w, "}")
- fmt.Fprintf(w, "%s_value = map[string]%s{\n", name, typ)
+ g.P("}")
+ g.P(enum.GoName, "_value = map[string]", gotype, "{")
for _, entry := range enum.Entries {
- fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value)
+ g.P(strconv.Quote(entry.Name), ": ", entry.Value, ",")
+ }
+ g.P("}")
+ g.P(")")
+ g.P()
+
+ if isEnumFlag(enum) {
+ size := BaseTypeSizes[enum.Type] * 8
+ g.P("func (x ", enum.GoName, ") String() string {")
+ g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
+ g.P(" if ok { return s }")
+ g.P(" str := func(n ", gotype, ") string {")
+ g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(n)]")
+ g.P(" if ok {")
+ g.P(" return s")
+ g.P(" }")
+ g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(n)) + \")\"")
+ g.P(" }")
+ g.P(" for i := ", gotype, "(0); i <= ", size, "; i++ {")
+ g.P(" val := ", gotype, "(x)")
+ g.P(" if val&(1<<i) != 0 {")
+ g.P(" if s != \"\" {")
+ g.P(" s += \"|\"")
+ g.P(" }")
+ g.P(" s += str(1<<i)")
+ g.P(" }")
+ g.P(" }")
+ g.P(" if s == \"\" {")
+ g.P(" return str(", gotype, "(x))")
+ g.P(" }")
+ g.P(" return s")
+ g.P("}")
+ g.P()
+ } else {
+ g.P("func (x ", enum.GoName, ") String() string {")
+ g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]")
+ g.P(" if ok { return s }")
+ g.P(" return \"", enum.GoName, "(\" + ", strconvPkg.Ident("Itoa"), "(int(x)) + \")\"")
+ g.P("}")
+ g.P()
}
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w)
-
- fmt.Fprintf(w, "func (x %s) String() string {\n", name)
- fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ)
- fmt.Fprintf(w, "\tif ok { return s }\n")
- fmt.Fprintf(w, "\treturn \"%s(\" + strconv.Itoa(int(x)) + \")\"\n", name)
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
}
-func generateImportedAlias(ctx *GenFile, w io.Writer, name string, imp string) {
- fmt.Fprintf(w, "type %s = %s.%s\n", name, imp, name)
- fmt.Fprintln(w)
-}
-
-func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
- name := alias.GoName
-
- logf(" writing ALIAS %q (%s), length: %d", alias.Name, name, alias.Length)
+func genAlias(g *GenFile, alias *Alias) {
+ logf("gen ALIAS %s (%s) - type: %s length: %d", alias.GoName, alias.Name, alias.Type, alias.Length)
- // generate struct comment
- generateComment(ctx, w, name, alias.Name, "alias")
-
- // generate struct definition
- fmt.Fprintf(w, "type %s ", name)
+ genTypeComment(g, alias.GoName, alias.Name, "alias")
+ var gotype string
+ switch {
+ case alias.TypeStruct != nil:
+ gotype = g.GoIdent(alias.TypeStruct.GoIdent)
+ case alias.TypeUnion != nil:
+ gotype = g.GoIdent(alias.TypeUnion.GoIdent)
+ default:
+ gotype = BaseTypesGo[alias.Type]
+ }
if alias.Length > 0 {
- fmt.Fprintf(w, "[%d]", alias.Length)
+ gotype = fmt.Sprintf("[%d]%s", alias.Length, gotype)
}
- dataType := convertToGoType(ctx.file, alias.Type)
- fmt.Fprintf(w, "%s\n", dataType)
+ g.P("type ", alias.GoName, " ", gotype)
+ g.P()
// generate alias-specific methods
switch alias.Name {
+ case "ip4_address":
+ generateIPConversion(g, alias.GoName, 4)
+ case "ip6_address":
+ generateIPConversion(g, alias.GoName, 16)
+ case "address_with_prefix":
+ generateAddressWithPrefixConversion(g, alias.GoName)
case "mac_address":
- fmt.Fprintln(w)
- generateMacAddressConversion(w, name)
+ generateMacAddressConversion(g, alias.GoName)
}
-
- fmt.Fprintln(w)
}
-func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
- name := typ.GoName
-
- logf(" writing STRUCT %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
+func genStruct(g *GenFile, typ *Struct) {
+ logf("gen STRUCT %s (%s) - %d fields", typ.GoName, typ.Name, len(typ.Fields))
- // generate struct comment
- generateComment(ctx, w, name, typ.Name, "type")
+ genTypeComment(g, typ.GoName, typ.Name, "type")
- // generate struct definition
- fmt.Fprintf(w, "type %s struct {\n", name)
-
- // generate struct fields
- for i := range typ.Fields {
- // skip internal fields
- switch strings.ToLower(typ.Name) {
- case msgIdField:
- continue
+ if len(typ.Fields) == 0 {
+ g.P("type ", typ.GoName, " struct {}")
+ } else {
+ g.P("type ", typ.GoName, " struct {")
+ for i := range typ.Fields {
+ generateField(g, typ.Fields, i)
}
-
- generateField(ctx, w, typ.Fields, i)
+ g.P("}")
}
-
- // generate end of the struct
- fmt.Fprintln(w, "}")
-
- // generate name getter
- generateTypeNameGetter(w, name, typ.Name)
+ g.P()
// generate type-specific methods
switch typ.Name {
case "address":
- fmt.Fprintln(w)
- generateIPAddressConversion(w, name)
+ generateAddressConversion(g, typ.GoName)
case "prefix":
- fmt.Fprintln(w)
- generatePrefixConversion(w, name)
+ generatePrefixConversion(g, typ.GoName)
+ case "ip4_prefix":
+ generateIPPrefixConversion(g, typ.GoName, 4)
+ case "ip6_prefix":
+ generateIPPrefixConversion(g, typ.GoName, 6)
}
-
- fmt.Fprintln(w)
}
-// generateUnionMethods generates methods that implement struc.Custom
-// interface to allow having XXX_uniondata field unexported
-// TODO: do more testing when unions are actually used in some messages
-/*func generateUnionMethods(w io.Writer, structName string) {
- // generate struc.Custom implementation for union
- fmt.Fprintf(w, `
-func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) {
- var b = new(bytes.Buffer)
- if err := struc.PackWithOptions(b, u.union_data, opt); err != nil {
- return 0, err
- }
- copy(p, b.Bytes())
- return b.Len(), nil
-}
-func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
- return struc.UnpackWithOptions(r, u.union_data[:], opt)
-}
-func (u *%[1]s) Size(opt *struc.Options) int {
- return len(u.union_data)
-}
-func (u *%[1]s) String() string {
- return string(u.union_data[:])
-}
-`, structName)
-}*/
-
-/*func generateUnionGetterSetterNew(w io.Writer, structName string, getterField, getterStruct string) {
- fmt.Fprintf(w, `
-func %[1]s%[2]s(a %[3]s) (u %[1]s) {
- u.Set%[2]s(a)
- return
-}
-func (u *%[1]s) Set%[2]s(a %[3]s) {
- copy(u.%[4]s[:], a[:])
-}
-func (u *%[1]s) Get%[2]s() (a %[3]s) {
- copy(a[:], u.%[4]s[:])
- return
-}
-`, structName, getterField, getterStruct, unionDataField)
-}*/
-
-func generateUnion(ctx *GenFile, w io.Writer, union *Union) {
- name := union.GoName
+func genUnion(g *GenFile, union *Union) {
+ logf("gen UNION %s (%s) - %d fields", union.GoName, union.Name, len(union.Fields))
- logf(" writing UNION %q (%s) with %d fields", union.Name, name, len(union.Fields))
+ genTypeComment(g, union.GoName, union.Name, "union")
- // generate struct comment
- generateComment(ctx, w, name, union.Name, "union")
+ g.P("type ", union.GoName, " struct {")
- // generate struct definition
- fmt.Fprintln(w, "type", name, "struct {")
-
- // maximum size for union
- maxSize := getUnionSize(ctx.file, union)
+ for _, field := range union.Fields {
+ g.P("// ", field.GoName, " *", getFieldType(g, field))
+ }
// generate data field
- fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
+ maxSize := getUnionSize(union)
+ g.P(fieldUnionData, " [", maxSize, "]byte")
// generate end of the struct
- fmt.Fprintln(w, "}")
+ g.P("}")
+ g.P()
- // generate name getter
- generateTypeNameGetter(w, name, union.Name)
-
- // generate getters for fields
+ // generate methods for fields
for _, field := range union.Fields {
- fieldType := convertToGoType(ctx.file, field.Type)
- generateUnionGetterSetter(w, name, field.GoName, fieldType)
+ genUnionFieldMethods(g, union.GoName, field)
}
-
- // generate union methods
- //generateUnionMethods(w, name)
-
- fmt.Fprintln(w)
-}
-
-func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
- fmt.Fprintf(w, `
-func %[1]s%[2]s(a %[3]s) (u %[1]s) {
- u.Set%[2]s(a)
- return
+ g.P()
}
-func (u *%[1]s) Set%[2]s(a %[3]s) {
- var b = new(bytes.Buffer)
- if err := struc.Pack(b, &a); err != nil {
- return
- }
- copy(u.%[4]s[:], b.Bytes())
-}
-func (u *%[1]s) Get%[2]s() (a %[3]s) {
- var b = bytes.NewReader(u.%[4]s[:])
- struc.Unpack(b, &a)
- return
-}
-`, structName, getterField, getterStruct, unionDataField)
-}
-
-func generateMessage(ctx *GenFile, w io.Writer, msg *Message) {
- name := msg.GoName
- logf(" writing MESSAGE %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
+func genUnionFieldMethods(g *GenFile, structName string, field *Field) {
+ getterStruct := fieldGoType(g, field)
- // generate struct comment
- generateComment(ctx, w, name, msg.Name, "message")
+ // Constructor
+ g.P("func ", structName, field.GoName, "(a ", getterStruct, ") (u ", structName, ") {")
+ g.P(" u.Set", field.GoName, "(a)")
+ g.P(" return")
+ g.P("}")
- // generate struct definition
- fmt.Fprintf(w, "type %s struct {", name)
+ // Setter
+ g.P("func (u *", structName, ") Set", field.GoName, "(a ", getterStruct, ") {")
+ g.P(" var buf = ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
+ encodeField(g, field, "a", func(name string) string {
+ return "a." + name
+ }, 0)
+ g.P("}")
- msgType := otherMessage
- wasClientIndex := false
-
- // generate struct fields
- n := 0
- for i, field := range msg.Fields {
- if i == 1 {
- if field.Name == clientIndexField {
- // "client_index" as the second member,
- // this might be an event message or a request
- msgType = eventMessage
- wasClientIndex = true
- } else if field.Name == contextField {
- // reply needs "context" as the second member
- msgType = replyMessage
- }
- } else if i == 2 {
- if wasClientIndex && field.Name == contextField {
- // request needs "client_index" as the second member
- // and "context" as the third member
- msgType = requestMessage
- }
- }
-
- // skip internal fields
- switch strings.ToLower(field.Name) {
- case msgIdField:
- continue
- case clientIndexField, contextField:
- if n == 0 {
- continue
- }
- }
- n++
- if n == 1 {
- fmt.Fprintln(w)
- }
-
- generateField(ctx, w, msg.Fields, i)
- }
-
- // generate end of the struct
- fmt.Fprintln(w, "}")
-
- // generate message methods
- generateMessageResetMethod(w, name)
- generateMessageNameGetter(w, name, msg.Name)
- generateCrcGetter(w, name, msg.CRC)
- generateMessageTypeGetter(w, name, msgType)
- generateMessageSize(ctx, w, name, msg.Fields)
- generateMessageMarshal(ctx, w, name, msg.Fields)
- generateMessageUnmarshal(ctx, w, name, msg.Fields)
-
- fmt.Fprintln(w)
+ // Getter
+ g.P("func (u *", structName, ") Get", field.GoName, "() (a ", getterStruct, ") {")
+ g.P(" var buf = ", govppCodecPkg.Ident("NewBuffer"), "(u.", fieldUnionData, "[:])")
+ decodeField(g, field, "a", func(name string) string {
+ return "a." + name
+ }, 0)
+ g.P(" return")
+ g.P("}")
+ g.P()
}
-func generateMessageSize(ctx *GenFile, w io.Writer, name string, fields []*Field) {
- fmt.Fprintf(w, "func (m *%[1]s) Size() int {\n", name)
-
- fmt.Fprintf(w, "\tif m == nil { return 0 }\n")
- fmt.Fprintf(w, "\tvar size int\n")
-
- encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
- t, ok := BaseTypeNames[typ]
- if !ok {
- return false
- }
+func generateField(g *GenFile, fields []*Field, i int) {
+ field := fields[i]
- var s = BaseTypeSizes[t]
- switch t {
- case STRING:
- if length > 0 {
- s = length
- fmt.Fprintf(w, "\tsize += %d\n", s)
- } else {
- s = 4
- fmt.Fprintf(w, "\tsize += %d + len(%s)\n", s, name)
- }
- default:
- if sizefrom != "" {
- //fmt.Fprintf(w, "\tsize += %d * int(%s)\n", s, sizefrom)
- fmt.Fprintf(w, "\tsize += %d * len(%s)\n", s, name)
- } else {
- if length > 0 {
- s = BaseTypeSizes[t] * length
- }
- fmt.Fprintf(w, "\tsize += %d\n", s)
- }
- }
+ logf(" gen FIELD[%d] %s (%s) - type: %q (array: %v/%v)", i, field.GoName, field.Name, field.Type, field.Array, field.Length)
- return true
+ gotype := getFieldType(g, field)
+ tags := structTags{
+ "binapi": fieldTagJSON(field),
+ "json": fieldTagBinapi(field),
}
- lvl := 0
- var sizeFields func(fields []*Field, parentName string)
- sizeFields = func(fields []*Field, parentName string) {
- lvl++
- defer func() { lvl-- }()
-
- n := 0
- for _, field := range fields {
- if field.ParentMessage != nil {
- // skip internal fields
- switch strings.ToLower(field.Name) {
- case msgIdField:
- continue
- case clientIndexField, contextField:
- if n == 0 {
- continue
- }
- }
- }
- n++
-
- fieldName := field.GoName //camelCaseName(strings.TrimPrefix(field.Name, "_"))
- name := fmt.Sprintf("%s.%s", parentName, fieldName)
- sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
- var sizeFromName string
- if sizeFrom != "" {
- sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
- }
-
- fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
-
- if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
- continue
- }
-
- char := fmt.Sprintf("s%d", lvl)
- index := fmt.Sprintf("j%d", lvl)
-
- if field.Array {
- if field.Length > 0 {
- fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
- } else if field.SizeFrom != "" {
- //fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
- fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
- }
-
- fmt.Fprintf(w, "\tvar %[1]s %[2]s\n_ = %[1]s\n", char, convertToGoType(ctx.file, field.Type))
- fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
- name = char
- }
-
- if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
- if encodeBaseType(enum.Type, name, 0, "") {
- } else {
- fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
- }
- } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
- if encodeBaseType(alias.Type, name, alias.Length, "") {
- } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
- sizeFields(typ.Fields, name)
- } else {
- fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
- }
- } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
- sizeFields(typ.Fields, name)
- } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
- maxSize := getUnionSize(ctx.file, union)
- fmt.Fprintf(w, "\tsize += %d\n", maxSize)
- } else {
- fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
- }
+ g.P(field.GoName, " ", gotype, tags)
+}
- if field.Array {
- fmt.Fprintf(w, "\t}\n")
- }
- }
+func fieldTagBinapi(field *Field) string {
+ if field.FieldSizeOf != nil {
+ return "-"
}
-
- sizeFields(fields, "m")
-
- fmt.Fprintf(w, "return size\n")
-
- fmt.Fprintf(w, "}\n")
+ return fmt.Sprintf("%s,omitempty", field.Name)
}
-func generateMessageMarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
- fmt.Fprintf(w, "func (m *%[1]s) Marshal(b []byte) ([]byte, error) {\n", name)
-
- fmt.Fprintf(w, "\to := binary.BigEndian\n")
- fmt.Fprintf(w, "\t_ = o\n")
- fmt.Fprintf(w, "\tpos := 0\n")
- fmt.Fprintf(w, "\t_ = pos\n")
-
- var buf = new(strings.Builder)
-
- encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
- t, ok := BaseTypeNames[typ]
- if !ok {
- return false
- }
-
- isArray := length > 0 || sizefrom != ""
-
- switch t {
- case I8, U8, I16, U16, I32, U32, I64, U64, F64:
- if isArray {
- if length != 0 {
- fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", length)
- } else if sizefrom != "" {
- //fmt.Fprintf(buf, "\tfor i := 0; i < int(%s); i++ {\n", sizefrom)
- fmt.Fprintf(buf, "\tfor i := 0; i < len(%s); i++ {\n", name)
- }
- }
- }
-
- switch t {
- case I8, U8:
- if isArray {
- fmt.Fprintf(buf, "\tvar x uint8\n")
- fmt.Fprintf(buf, "\tif i < len(%s) { x = uint8(%s[i]) }\n", name, name)
- name = "x"
- }
- fmt.Fprintf(buf, "\tbuf[pos] = uint8(%s)\n", name)
- fmt.Fprintf(buf, "\tpos += 1\n")
- if isArray {
- fmt.Fprintf(buf, "\t}\n")
- }
- case I16, U16:
- if isArray {
- fmt.Fprintf(buf, "\tvar x uint16\n")
- fmt.Fprintf(buf, "\tif i < len(%s) { x = uint16(%s[i]) }\n", name, name)
- name = "x"
- }
- fmt.Fprintf(buf, "\to.PutUint16(buf[pos:pos+2], uint16(%s))\n", name)
- fmt.Fprintf(buf, "\tpos += 2\n")
- if isArray {
- fmt.Fprintf(buf, "\t}\n")
- }
- case I32, U32:
- if isArray {
- fmt.Fprintf(buf, "\tvar x uint32\n")
- fmt.Fprintf(buf, "\tif i < len(%s) { x = uint32(%s[i]) }\n", name, name)
- name = "x"
- }
- fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(%s))\n", name)
- fmt.Fprintf(buf, "\tpos += 4\n")
- if isArray {
- fmt.Fprintf(buf, "\t}\n")
- }
- case I64, U64:
- if isArray {
- fmt.Fprintf(buf, "\tvar x uint64\n")
- fmt.Fprintf(buf, "\tif i < len(%s) { x = uint64(%s[i]) }\n", name, name)
- name = "x"
- }
- fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], uint64(%s))\n", name)
- fmt.Fprintf(buf, "\tpos += 8\n")
- if isArray {
- fmt.Fprintf(buf, "\t}\n")
- }
- case F64:
- if isArray {
- fmt.Fprintf(buf, "\tvar x float64\n")
- fmt.Fprintf(buf, "\tif i < len(%s) { x = float64(%s[i]) }\n", name, name)
- name = "x"
- }
- fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], math.Float64bits(float64(%s)))\n", name)
- fmt.Fprintf(buf, "\tpos += 8\n")
- if isArray {
- fmt.Fprintf(buf, "\t}\n")
- }
- case BOOL:
- fmt.Fprintf(buf, "\tif %s { buf[pos] = 1 }\n", name)
- fmt.Fprintf(buf, "\tpos += 1\n")
- case STRING:
- if length != 0 {
- fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s)\n", length, name)
- fmt.Fprintf(buf, "\tpos += %d\n", length)
- } else {
- fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(len(%s)))\n", name)
- fmt.Fprintf(buf, "\tpos += 4\n")
- fmt.Fprintf(buf, "\tcopy(buf[pos:pos+len(%s)], %s[:])\n", name, name)
- fmt.Fprintf(buf, "\tpos += len(%s)\n", name)
- }
- default:
- fmt.Fprintf(buf, "\t// ??? %s %s\n", name, typ)
- return false
+func fieldTagJSON(field *Field) string {
+ typ := fromApiType(field.Type)
+ if field.Array {
+ if field.Length > 0 {
+ typ = fmt.Sprintf("%s[%d]", typ, field.Length)
+ } else if field.SizeFrom != "" {
+ typ = fmt.Sprintf("%s[%s]", typ, field.SizeFrom)
+ } else {
+ typ = fmt.Sprintf("%s[]", typ)
}
- return true
}
-
- lvl := 0
- var encodeFields func(fields []*Field, parentName string)
- encodeFields = func(fields []*Field, parentName string) {
- lvl++
- defer func() { lvl-- }()
-
- n := 0
- for _, field := range fields {
- if field.ParentMessage != nil {
- // skip internal fields
- switch strings.ToLower(field.Name) {
- case msgIdField:
- continue
- case clientIndexField, contextField:
- if n == 0 {
- continue
- }
- }
- }
- n++
-
- getFieldName := func(name string) string {
- fieldName := camelCaseName(strings.TrimPrefix(name, "_"))
- return fmt.Sprintf("%s.%s", parentName, fieldName)
- }
-
- fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
- name := fmt.Sprintf("%s.%s", parentName, fieldName)
- sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
- var sizeFromName string
- if sizeFrom != "" {
- sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
- }
-
- fmt.Fprintf(buf, "\t// field[%d] %s\n", lvl, name)
-
- getSizeOfField := func() *Field {
- for _, f := range fields {
- if f.SizeFrom == field.Name {
- return f
- }
- }
- return nil
- }
- if f := getSizeOfField(); f != nil {
- if encodeBaseType(field.Type, fmt.Sprintf("len(%s)", getFieldName(f.Name)), field.Length, "") {
- continue
- }
- panic(fmt.Sprintf("failed to encode base type of sizefrom field: %s", field.Name))
- }
-
- if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
- continue
- }
-
- char := fmt.Sprintf("v%d", lvl)
- index := fmt.Sprintf("j%d", lvl)
-
- if field.Array {
- if field.Length > 0 {
- fmt.Fprintf(buf, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
- } else if field.SizeFrom != "" {
- //fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
- fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
- }
-
- fmt.Fprintf(buf, "\tvar %s %s\n", char, convertToGoType(ctx.file, field.Type))
- fmt.Fprintf(buf, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
- name = char
- }
-
- if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
- if encodeBaseType(enum.Type, name, 0, "") {
- } else {
- fmt.Fprintf(buf, "\t// ??? ENUM %s %s\n", name, enum.Type)
- }
- } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
- if encodeBaseType(alias.Type, name, alias.Length, "") {
- } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
- encodeFields(typ.Fields, name)
- } else {
- fmt.Fprintf(buf, "\t// ??? ALIAS %s %s\n", name, alias.Type)
- }
- } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
- encodeFields(typ.Fields, name)
- } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
- maxSize := getUnionSize(ctx.file, union)
- fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s.%s[:])\n", maxSize, name, unionDataField)
- fmt.Fprintf(buf, "\tpos += %d\n", maxSize)
- } else {
- fmt.Fprintf(buf, "\t// ??? buf[pos] = (%s)\n", name)
- }
-
- if field.Array {
- fmt.Fprintf(buf, "\t}\n")
- }
- }
+ tag := []string{
+ typ,
+ fmt.Sprintf("name=%s", field.Name),
}
-
- encodeFields(fields, "m")
-
- fmt.Fprintf(w, "\tvar buf []byte\n")
- fmt.Fprintf(w, "\tif b == nil {\n")
- fmt.Fprintf(w, "\tbuf = make([]byte, m.Size())\n")
- fmt.Fprintf(w, "\t} else {\n")
- fmt.Fprintf(w, "\tbuf = b\n")
- fmt.Fprintf(w, "\t}\n")
- fmt.Fprint(w, buf.String())
-
- fmt.Fprintf(w, "return buf, nil\n")
-
- fmt.Fprintf(w, "}\n")
-}
-
-func generateMessageUnmarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
- fmt.Fprintf(w, "func (m *%[1]s) Unmarshal(tmp []byte) error {\n", name)
-
- fmt.Fprintf(w, "\to := binary.BigEndian\n")
- fmt.Fprintf(w, "\t_ = o\n")
- fmt.Fprintf(w, "\tpos := 0\n")
- fmt.Fprintf(w, "\t_ = pos\n")
-
- decodeBaseType := func(typ, orig, name string, length int, sizefrom string, alloc bool) bool {
- t, ok := BaseTypeNames[typ]
- if !ok {
- return false
- }
-
- isArray := length > 0 || sizefrom != ""
-
- switch t {
- case I8, U8, I16, U16, I32, U32, I64, U64, F64:
- if isArray {
- if alloc {
- if length != 0 {
- fmt.Fprintf(w, "\t%s = make([]%s, %d)\n", name, orig, length)
- } else if sizefrom != "" {
- fmt.Fprintf(w, "\t%s = make([]%s, %s)\n", name, orig, sizefrom)
- }
- }
- fmt.Fprintf(w, "\tfor i := 0; i < len(%s); i++ {\n", name)
- }
- }
-
- switch t {
- case I8, U8:
- if isArray {
- fmt.Fprintf(w, "\t%s[i] = %s(tmp[pos])\n", name, convertToGoType(ctx.file, typ))
- } else {
- fmt.Fprintf(w, "\t%s = %s(tmp[pos])\n", name, orig)
- }
- fmt.Fprintf(w, "\tpos += 1\n")
- if isArray {
- fmt.Fprintf(w, "\t}\n")
- }
- case I16, U16:
- if isArray {
- fmt.Fprintf(w, "\t%s[i] = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
- } else {
- fmt.Fprintf(w, "\t%s = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
- }
- fmt.Fprintf(w, "\tpos += 2\n")
- if isArray {
- fmt.Fprintf(w, "\t}\n")
- }
- case I32, U32:
- if isArray {
- fmt.Fprintf(w, "\t%s[i] = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
- } else {
- fmt.Fprintf(w, "\t%s = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
- }
- fmt.Fprintf(w, "\tpos += 4\n")
- if isArray {
- fmt.Fprintf(w, "\t}\n")
- }
- case I64, U64:
- if isArray {
- fmt.Fprintf(w, "\t%s[i] = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
- } else {
- fmt.Fprintf(w, "\t%s = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
- }
- fmt.Fprintf(w, "\tpos += 8\n")
- if isArray {
- fmt.Fprintf(w, "\t}\n")
- }
- case F64:
- if isArray {
- fmt.Fprintf(w, "\t%s[i] = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
- } else {
- fmt.Fprintf(w, "\t%s = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
- }
- fmt.Fprintf(w, "\tpos += 8\n")
- if isArray {
- fmt.Fprintf(w, "\t}\n")
- }
- case BOOL:
- fmt.Fprintf(w, "\t%s = tmp[pos] != 0\n", name)
- fmt.Fprintf(w, "\tpos += 1\n")
- case STRING:
- if length != 0 {
- fmt.Fprintf(w, "\t{\n")
- fmt.Fprintf(w, "\tnul := bytes.Index(tmp[pos:pos+%d], []byte{0x00})\n", length)
- fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+nul])\n", name)
- fmt.Fprintf(w, "\tpos += %d\n", length)
- fmt.Fprintf(w, "\t}\n")
- } else {
- fmt.Fprintf(w, "\t{\n")
- fmt.Fprintf(w, "\tsiz := o.Uint32(tmp[pos:pos+4])\n")
- fmt.Fprintf(w, "\tpos += 4\n")
- fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+int(siz)])\n", name)
- fmt.Fprintf(w, "\tpos += len(%s)\n", name)
- fmt.Fprintf(w, "\t}\n")
- }
- default:
- fmt.Fprintf(w, "\t// ??? %s %s\n", name, typ)
- return false
- }
- return true
+ if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
+ tag = append(tag, fmt.Sprintf("limit=%s", limit))
}
-
- lvl := 0
- var decodeFields func(fields []*Field, parentName string)
- decodeFields = func(fields []*Field, parentName string) {
- lvl++
- defer func() { lvl-- }()
-
- n := 0
- for _, field := range fields {
- if field.ParentMessage != nil {
- // skip internal fields
- switch strings.ToLower(field.Name) {
- case msgIdField:
- continue
- case clientIndexField, contextField:
- if n == 0 {
- continue
- }
- }
- }
- n++
-
- fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
- name := fmt.Sprintf("%s.%s", parentName, fieldName)
- sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
- var sizeFromName string
- if sizeFrom != "" {
- sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
- }
-
- fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
-
- if decodeBaseType(field.Type, convertToGoType(ctx.file, field.Type), name, field.Length, sizeFromName, true) {
- continue
- }
-
- //char := fmt.Sprintf("v%d", lvl)
- index := fmt.Sprintf("j%d", lvl)
-
- if field.Array {
- if field.Length > 0 {
- fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
- } else if field.SizeFrom != "" {
- fieldType := getFieldType(ctx, field)
- if strings.HasPrefix(fieldType, "[]") {
- fmt.Fprintf(w, "\t%s = make(%s, int(%s.%s))\n", name, fieldType, parentName, sizeFrom)
- }
- fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
- }
-
- /*fmt.Fprintf(w, "\tvar %s %s\n", char, convertToGoType(ctx, field.Type))
- fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
- name = char*/
- name = fmt.Sprintf("%s[%s]", name, index)
- }
-
- if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
- if decodeBaseType(enum.Type, convertToGoType(ctx.file, field.Type), name, 0, "", false) {
- } else {
- fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
- }
- } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
- if decodeBaseType(alias.Type, convertToGoType(ctx.file, field.Type), name, alias.Length, "", false) {
- } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
- decodeFields(typ.Fields, name)
- } else {
- fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
- }
- } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
- decodeFields(typ.Fields, name)
- } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
- maxSize := getUnionSize(ctx.file, union)
- fmt.Fprintf(w, "\tcopy(%s.%s[:], tmp[pos:pos+%d])\n", name, unionDataField, maxSize)
- fmt.Fprintf(w, "\tpos += %d\n", maxSize)
- } else {
- fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
- }
-
- if field.Array {
- fmt.Fprintf(w, "\t}\n")
+ if def, ok := field.Meta["default"]; ok && def != nil {
+ actual := fieldActualType(field)
+ if t, ok := BaseTypesGo[actual]; ok {
+ switch t {
+ case I8, I16, I32, I64:
+ def = int(def.(float64))
+ case U8, U16, U32, U64:
+ def = uint(def.(float64))
+ case F64:
+ def = def.(float64)
}
}
+ tag = append(tag, fmt.Sprintf("default=%s", def))
}
-
- decodeFields(fields, "m")
-
- fmt.Fprintf(w, "return nil\n")
-
- fmt.Fprintf(w, "}\n")
+ return strings.Join(tag, ",")
}
-func getFieldType(ctx *GenFile, field *Field) string {
- //fieldName := strings.TrimPrefix(field.Name, "_")
- //fieldName = camelCaseName(fieldName)
- //fieldName := field.GoName
+type structTags map[string]string
- dataType := convertToGoType(ctx.file, field.Type)
- fieldType := dataType
-
- // check if it is array
- if field.Length > 0 || field.SizeFrom != "" {
- if dataType == "uint8" {
- dataType = "byte"
- }
- if dataType == "string" && field.Array {
- fieldType = "string"
- dataType = "byte"
- } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
- fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
- } else {
- fieldType = "[]" + dataType
- }
+func (tags structTags) String() string {
+ if len(tags) == 0 {
+ return ""
}
-
- return fieldType
-}
-
-func generateField(ctx *GenFile, w io.Writer, fields []*Field, i int) {
- field := fields[i]
-
- //fieldName := strings.TrimPrefix(field.Name, "_")
- //fieldName = camelCaseName(fieldName)
- fieldName := field.GoName
-
- dataType := convertToGoType(ctx.file, field.Type)
- fieldType := dataType
-
- // generate length field for strings
- if field.Type == "string" && field.Length == 0 {
- fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName)
+ var keys []string
+ for k := range tags {
+ keys = append(keys, k)
}
-
- // check if it is array
- if field.Length > 0 || field.SizeFrom != "" {
- if dataType == "uint8" {
- dataType = "byte"
- }
- if dataType == "string" && field.Array {
- fieldType = "string"
- dataType = "byte"
- } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
- fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
- } else {
- fieldType = "[]" + dataType
- }
+ sort.Strings(keys)
+ var ss []string
+ for _, key := range keys {
+ tag := tags[key]
+ ss = append(ss, fmt.Sprintf(`%s:%s`, key, strconv.Quote(tag)))
}
- fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
-
- fieldTags := map[string]string{}
+ return "`" + strings.Join(ss, " ") + "`"
+}
- if field.Length > 0 && field.Array {
- // fixed size array
- fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
- } else {
- for _, f := range fields {
- if f.SizeFrom == field.Name {
- // variable sized array
- //sizeOfName := camelCaseName(f.Name)
- fieldTags["struc"] = fmt.Sprintf("sizeof=%s", f.GoName)
- }
- }
+func genMessages(g *GenFile) {
+ if len(g.file.Messages) == 0 {
+ return
}
- if ctx.IncludeBinapiNames {
- typ := fromApiType(field.Type)
- if field.Array {
- if field.Length > 0 {
- fieldTags["binapi"] = fmt.Sprintf("%s[%d],name=%s", typ, field.Length, field.Name)
- } else if field.SizeFrom != "" {
- fieldTags["binapi"] = fmt.Sprintf("%s[%s],name=%s", typ, field.SizeFrom, field.Name)
- }
- } else {
- fieldTags["binapi"] = fmt.Sprintf("%s,name=%s", typ, field.Name)
- }
- }
- if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
- fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], limit)
- }
- if def, ok := field.Meta["default"]; ok && def != nil {
- actual := getActualType(ctx.file, fieldType)
- if t, ok := binapiTypes[actual]; ok && t != "float64" {
- defnum := int(def.(float64))
- fieldTags["binapi"] = fmt.Sprintf("%s,default=%d", fieldTags["binapi"], defnum)
- } else {
- fieldTags["binapi"] = fmt.Sprintf("%s,default=%v", fieldTags["binapi"], def)
- }
+ for _, msg := range g.file.Messages {
+ genMessage(g, msg)
}
- fieldTags["json"] = fmt.Sprintf("%s,omitempty", field.Name)
+ // generate registrations
+ initFnName := fmt.Sprintf("file_%s_binapi_init", g.file.PackageName)
- if len(fieldTags) > 0 {
- fmt.Fprintf(w, "\t`")
- var keys []string
- for k := range fieldTags {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- var n int
- for _, tt := range keys {
- t, ok := fieldTags[tt]
- if !ok {
- continue
- }
- if n > 0 {
- fmt.Fprintf(w, " ")
- }
- n++
- fmt.Fprintf(w, `%s:"%s"`, tt, t)
- }
- fmt.Fprintf(w, "`")
+ g.P("func init() { ", initFnName, "() }")
+ g.P("func ", initFnName, "() {")
+ for _, msg := range g.file.Messages {
+ id := fmt.Sprintf("%s_%s", msg.Name, msg.CRC)
+ g.P(govppApiPkg.Ident("RegisterMessage"), "((*", msg.GoIdent, ")(nil), ", strconv.Quote(id), ")")
}
+ g.P("}")
+ g.P()
- fmt.Fprintln(w)
+ // generate list of messages
+ g.P("// Messages returns list of all messages in this module.")
+ g.P("func AllMessages() []", govppApiPkg.Ident("Message"), " {")
+ g.P("return []", govppApiPkg.Ident("Message"), "{")
+ for _, msg := range g.file.Messages {
+ g.P("(*", msg.GoIdent, ")(nil),")
+ }
+ g.P("}")
+ g.P("}")
}
-func generateMessageResetMethod(w io.Writer, structName string) {
- fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
-}
+func genMessage(g *GenFile, msg *Message) {
+ logf("gen MESSAGE %s (%s) - %d fields", msg.GoName, msg.Name, len(msg.Fields))
-func generateMessageNameGetter(w io.Writer, structName, msgName string) {
- fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName)
-}
+ genTypeComment(g, msg.GoIdent.GoName, msg.Name, "message")
-func generateTypeNameGetter(w io.Writer, structName, msgName string) {
- fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
-}
-
-func generateIPAddressConversion(w io.Writer, structName string) {
- f1 := func(ipVer, ipVerExt int) string {
- return fmt.Sprintf(`address.Af = ADDRESS_IP%[1]d
- var ip%[1]daddr IP%[1]dAddress
- copy(ip%[1]daddr[:], netIP.To%[2]d())
- address.Un.SetIP%[1]d(ip%[1]daddr)`, ipVer, ipVerExt)
- }
- f2 := func(ipVer, ipVerExt int) string {
- return fmt.Sprintf("ip%[1]dAddress := a.Un.GetIP%[1]d()\nip = net.IP(ip%[1]dAddress[:]).To%[2]d().String()",
- ipVer, ipVerExt)
- }
- // IP to Address
- fmt.Fprintf(w, `func ParseAddress(ip string) (%[1]s, error) {
- var address %[1]s
- netIP := net.ParseIP(ip)
- if netIP == nil {
- return address, fmt.Errorf("invalid address: %[2]s", ip)
- }
- if ip4 := netIP.To4(); ip4 == nil {
- %[3]s
+ // generate message definition
+ if len(msg.Fields) == 0 {
+ g.P("type ", msg.GoIdent, " struct {}")
} else {
- %[4]s
- }
- return address, nil
-}
-`, structName, "%s", f1(6, 16), f1(4, 4))
- fmt.Fprintln(w)
-
- // Address to IP
- fmt.Fprintln(w)
- fmt.Fprintf(w, `func (a *%[1]s) ToString() string {
- var ip string
- if a.Af == ADDRESS_IP6 {
- %[2]s
- } else {
- %[3]s
+ g.P("type ", msg.GoIdent, " struct {")
+ for i := range msg.Fields {
+ generateField(g, msg.Fields, i)
+ }
+ g.P("}")
}
- return ip
-}`, structName, f2(6, 16), f2(4, 4))
-}
+ g.P()
-func generatePrefixConversion(w io.Writer, structName string) {
- fErr := func() string {
- return fmt.Sprintf(`if err != nil {
- return Prefix{}, fmt.Errorf("invalid IP %s: %s", ip, err)
- }`, "%s", "%v")
- }
+ generateMessageMethods(g, msg)
- // IP to Prefix
- fmt.Fprintf(w, `func ParsePrefix(ip string) (prefix %[1]s, err error) {
- hasPrefix := strings.Contains(ip, "/")
- if hasPrefix {
- netIP, network, err := net.ParseCIDR(ip)
- %[2]s
- maskSize, _ := network.Mask.Size()
- prefix.Len = byte(maskSize)
- prefix.Address, err = ParseAddress(netIP.String())
- %[2]s
- } else {
- netIP := net.ParseIP(ip)
- defaultMaskSize, _ := net.CIDRMask(32, 32).Size()
- if netIP.To4() == nil {
- defaultMaskSize, _ = net.CIDRMask(128, 128).Size()
- }
- prefix.Len = byte(defaultMaskSize)
- prefix.Address, err = ParseAddress(netIP.String())
- %[2]s
- }
- return prefix, nil
-}`, structName, fErr(), nil)
- fmt.Fprintln(w)
-
- // Prefix to IP
- fmt.Fprintln(w)
- fmt.Fprintf(w, `func (p *%[1]s) ToString() string {
- ip := p.Address.ToString()
- return ip + "/" + strconv.Itoa(int(p.Len))
- }`, structName)
-}
+ // encoding methods
+ generateMessageSize(g, msg.GoIdent.GoName, msg.Fields)
+ generateMessageMarshal(g, msg.GoIdent.GoName, msg.Fields)
+ generateMessageUnmarshal(g, msg.GoIdent.GoName, msg.Fields)
-func generateMacAddressConversion(w io.Writer, structName string) {
- // string to MAC
- fmt.Fprintf(w, `func ParseMAC(mac string) (parsed %[1]s, err error) {
- var hw net.HardwareAddr
- if hw, err = net.ParseMAC(mac); err != nil {
- return
- }
- copy(parsed[:], hw[:])
- return
-}`, structName)
- fmt.Fprintln(w)
-
- // MAC to string
- fmt.Fprintln(w)
- fmt.Fprintf(w, `func (m *%[1]s) ToString() string {
- return net.HardwareAddr(m[:]).String()
- }`, structName)
+ g.P()
}
-func generateCrcGetter(w io.Writer, structName, crc string) {
- crc = strings.TrimPrefix(crc, "0x")
- fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
-}
+func generateMessageMethods(g *GenFile, msg *Message) {
+ // Reset method
+ g.P("func (m *", msg.GoIdent.GoName, ") Reset() { *m = ", msg.GoIdent.GoName, "{} }")
-func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
- fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
- if msgType == requestMessage {
- fmt.Fprintf(w, "\treturn api.RequestMessage")
- } else if msgType == replyMessage {
- fmt.Fprintf(w, "\treturn api.ReplyMessage")
- } else if msgType == eventMessage {
- fmt.Fprintf(w, "\treturn api.EventMessage")
- } else {
- fmt.Fprintf(w, "\treturn api.OtherMessage")
- }
- fmt.Fprintln(w, "}")
- fmt.Fprintln(w)
-}
+ // GetMessageName method
+ g.P("func (*", msg.GoIdent.GoName, ") GetMessageName() string { return ", strconv.Quote(msg.Name), " }")
+
+ // GetCrcString method
+ g.P("func (*", msg.GoIdent.GoName, ") GetCrcString() string { return ", strconv.Quote(msg.CRC), " }")
+
+ // GetMessageType method
+ g.P("func (*", msg.GoIdent.GoName, ") GetMessageType() api.MessageType {")
+ g.P(" return ", apiMsgType(msg.msgType))
+ g.P("}")
-func logf(f string, v ...interface{}) {
- logrus.Debugf(f, v...)
+ g.P()
}