From fa21c9d726ebb807895a8571af9a16dab5cd8d6e Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Fri, 8 Feb 2019 01:16:32 +0100 Subject: Generator improvements and cleanup - generator now supports include-comments flag (as opt-in) - minor code cleanup in binapi-generator - remove obsolete unit tests - flatten examples from examples/cmd folder - introduce constant for checking compatibility in future versions Change-Id: I3545f2ba4b869a3b51d6d0de7e742f3f1e1be392 Signed-off-by: Ondrej Fabry --- cmd/binapi-generator/definitions.go | 42 ++----- cmd/binapi-generator/generate.go | 61 ++++++---- cmd/binapi-generator/main.go | 38 ++++--- cmd/binapi-generator/objects.go | 52 ++------- cmd/binapi-generator/parse.go | 218 ++++++++++++++---------------------- cmd/binapi-generator/types.go | 129 +++++++++++++++++++++ 6 files changed, 292 insertions(+), 248 deletions(-) create mode 100644 cmd/binapi-generator/types.go (limited to 'cmd') diff --git a/cmd/binapi-generator/definitions.go b/cmd/binapi-generator/definitions.go index 3ad782f..9bf9e53 100644 --- a/cmd/binapi-generator/definitions.go +++ b/cmd/binapi-generator/definitions.go @@ -15,43 +15,10 @@ package main import ( - "strconv" "strings" "unicode" ) -func getBinapiTypeSize(binapiType string) int { - if _, ok := binapiTypes[binapiType]; ok { - b, err := strconv.Atoi(strings.TrimLeft(binapiType, "uif")) - if err == nil { - return b / 8 - } - } - return -1 -} - -// binapiTypes is a set of types used VPP binary API for translation to Go types -var binapiTypes = map[string]string{ - "u8": "uint8", - "i8": "int8", - "u16": "uint16", - "i16": "int16", - "u32": "uint32", - "i32": "int32", - "u64": "uint64", - "i64": "int64", - "f64": "float64", -} - -func usesInitialism(s string) string { - if u := strings.ToUpper(s); commonInitialisms[u] { - return u - } else if su, ok := specialInitialisms[u]; ok { - return su - } - return "" -} - // commonInitialisms is a set of common initialisms that need to stay in upper case. var commonInitialisms = map[string]bool{ "ACL": true, @@ -105,6 +72,15 @@ var specialInitialisms = map[string]string{ //"IPV6": "IPv6", } +func usesInitialism(s string) string { + if u := strings.ToUpper(s); commonInitialisms[u] { + return u + } else if su, ok := specialInitialisms[u]; ok { + return su + } + return "" +} + // camelCaseName returns correct name identifier (camelCase). func camelCaseName(name string) (should string) { name = strings.Title(name) diff --git a/cmd/binapi-generator/generate.go b/cmd/binapi-generator/generate.go index 48c3a41..f2da08a 100644 --- a/cmd/binapi-generator/generate.go +++ b/cmd/binapi-generator/generate.go @@ -25,9 +25,11 @@ import ( ) const ( + inputFileExt = ".api.json" // file extension of the VPP API files + outputFileExt = ".ba.go" // file extension of the Go generated files + govppApiImportPath = "git.fd.io/govpp.git/api" // import path of the govpp API package - inputFileExt = ".api.json" // file extension of the VPP binary API files - outputFileExt = ".ba.go" // file extension of the Go generated files + constAPIVersionCrc = "APIVersionCrc" // name for the API version CRC constant ) // context is a structure storing data for code generation @@ -37,6 +39,9 @@ type context struct { inputData []byte // contents of the input file + includeAPIVersionCrc bool // include constant with API version CRC string + includeComments bool // include parts of original source in comments + moduleName string // name of the source VPP module packageName string // name of the Go package being generated @@ -83,10 +88,9 @@ func generatePackage(ctx *context, w *bufio.Writer) error { generateHeader(ctx, w) generateImports(ctx, w) - if *includeAPIVer { - const APIVerConstName = "VlAPIVersion" - fmt.Fprintf(w, "// %s represents version of the binary API module.\n", APIVerConstName) - fmt.Fprintf(w, "const %s = %v\n", APIVerConstName, ctx.packageData.APIVersion) + if ctx.includeAPIVersionCrc { + fmt.Fprintf(w, "// %s defines API version CRC of the VPP binary API module.\n", constAPIVersionCrc) + fmt.Fprintf(w, "const %s = %v\n", constAPIVersionCrc, ctx.packageData.APIVersion) fmt.Fprintln(w) } @@ -194,9 +198,9 @@ func generateHeader(ctx *context, w io.Writer) { // generateImports writes generated package imports into w func generateImports(ctx *context, w io.Writer) { - fmt.Fprintf(w, "import \"%s\"\n", govppApiImportPath) - fmt.Fprintf(w, "import \"%s\"\n", "github.com/lunixbochs/struc") - fmt.Fprintf(w, "import \"%s\"\n", "bytes") + fmt.Fprintf(w, "import api \"%s\"\n", govppApiImportPath) + fmt.Fprintf(w, "import struc \"%s\"\n", "github.com/lunixbochs/struc") + fmt.Fprintf(w, "import bytes \"%s\"\n", "bytes") fmt.Fprintln(w) fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n") @@ -204,6 +208,13 @@ func generateImports(ctx *context, w io.Writer) { fmt.Fprintf(w, "var _ = struc.Pack\n") fmt.Fprintf(w, "var _ = bytes.NewBuffer\n") 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.Fprintln(w, "const _ = api.GoVppAPIPackageIsVersion1 // please upgrade the GoVPP api package") + fmt.Fprintln(w)*/ } // generateComment writes generated comment for the object into w @@ -214,6 +225,10 @@ func generateComment(ctx *context, w io.Writer, goName string, vppName string, o fmt.Fprintf(w, "// %s represents VPP binary API %s '%s':\n", goName, objKind, vppName) } + if !ctx.includeComments { + return + } + var isNotSpace = func(r rune) bool { return !unicode.IsSpace(r) } @@ -271,7 +286,7 @@ func generateServices(ctx *context, w *bufio.Writer, services []Service) { // generate interface fmt.Fprintf(w, "type %s interface {\n", "Services") - for _, svc := range ctx.packageData.Services { + for _, svc := range services { generateService(ctx, w, &svc) } fmt.Fprintln(w, "}") @@ -284,7 +299,14 @@ func generateService(ctx *context, w io.Writer, svc *Service) { reqTyp := camelCaseName(svc.RequestType) // method name is same as parameter type name by default - method := svc.MethodName() + method := reqTyp + if svc.Stream { + // use Dump as prefix instead of suffix for stream services + if m := strings.TrimSuffix(method, "Dump"); method != m { + method = "Dump" + m + } + } + params := fmt.Sprintf("*%s", reqTyp) returns := "error" if replyType := camelCaseName(svc.ReplyType); replyType != "" { @@ -450,7 +472,7 @@ func generateType(ctx *context, w io.Writer, typ *Type) { for i, field := range typ.Fields { // skip internal fields switch strings.ToLower(field.Name) { - case "crc", "_vl_msg_id": + case crcField, msgIdField: continue } @@ -488,17 +510,17 @@ func generateMessage(ctx *context, w io.Writer, msg *Message) { n := 0 for i, field := range msg.Fields { if i == 1 { - if field.Name == "client_index" { + 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 == "context" { + } else if field.Name == contextField { // reply needs "context" as the second member msgType = replyMessage } } else if i == 2 { - if wasClientIndex && field.Name == "context" { + if wasClientIndex && field.Name == contextField { // request needs "client_index" as the second member // and "context" as the third member msgType = requestMessage @@ -507,9 +529,9 @@ func generateMessage(ctx *context, w io.Writer, msg *Message) { // skip internal fields switch strings.ToLower(field.Name) { - case "crc", "_vl_msg_id": + case crcField, msgIdField: continue - case "client_index", "context": + case clientIndexField, contextField: if n == 0 { continue } @@ -550,9 +572,10 @@ func generateField(ctx *context, w io.Writer, fields []Field, i int) { } dataType := convertToGoType(ctx, field.Type) - fieldType := dataType - if field.IsArray() { + + // check if it is array + if field.Length > 0 || field.SizeFrom != "" { if dataType == "uint8" { dataType = "byte" } diff --git a/cmd/binapi-generator/main.go b/cmd/binapi-generator/main.go index b73a699..b3a131c 100644 --- a/cmd/binapi-generator/main.go +++ b/cmd/binapi-generator/main.go @@ -30,27 +30,15 @@ import ( ) var ( - inputFile = flag.String("input-file", "", "Input JSON file.") - inputDir = flag.String("input-dir", ".", "Input directory with JSON files.") + inputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.") + inputDir = flag.String("input-dir", ".", "Input directory with VPP API files in JSON format.") outputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.") - includeAPIVer = flag.Bool("include-apiver", false, "Whether to include VlAPIVersion in generated file.") - debug = flag.Bool("debug", false, "Turn on debug mode.") - continueOnError = flag.Bool("continue-onerror", false, "Wheter to continue with next file on error.") + includeAPIVer = flag.Bool("include-apiver", false, "Include APIVersion constant for each module.") + includeComments = flag.Bool("include-comments", false, "Include JSON API source in comments for each object.") + continueOnError = flag.Bool("continue-onerror", false, "Continue with next file on error.") + debug = flag.Bool("debug", false, "Enable debug mode.") ) -func init() { - flag.Parse() - if *debug { - logrus.SetLevel(logrus.DebugLevel) - } -} - -func logf(f string, v ...interface{}) { - if *debug { - logrus.Debugf(f, v...) - } -} - var log = logrus.Logger{ Level: logrus.InfoLevel, Formatter: &logrus.TextFormatter{}, @@ -58,6 +46,11 @@ var log = logrus.Logger{ } func main() { + flag.Parse() + if *debug { + logrus.SetLevel(logrus.DebugLevel) + } + if *inputFile == "" && *inputDir == "" { fmt.Fprintln(os.Stderr, "ERROR: input-file or input-dir must be specified") os.Exit(1) @@ -112,6 +105,9 @@ func generateFromFile(inputFile, outputDir string) error { return err } + ctx.includeAPIVersionCrc = *includeAPIVer + ctx.includeComments = *includeComments + // read input file contents ctx.inputData, err = readFile(inputFile) if err != nil { @@ -182,3 +178,9 @@ func parseJSON(inputData []byte) (*jsongo.JSONNode, error) { return &root, nil } + +func logf(f string, v ...interface{}) { + if *debug { + logrus.Debugf(f, v...) + } +} diff --git a/cmd/binapi-generator/objects.go b/cmd/binapi-generator/objects.go index 4b424f5..75c7581 100644 --- a/cmd/binapi-generator/objects.go +++ b/cmd/binapi-generator/objects.go @@ -1,9 +1,5 @@ package main -import ( - "strings" -) - // Package represents collection of objects parsed from VPP binary API JSON data type Package struct { APIVersion string @@ -52,6 +48,14 @@ type Type struct { Fields []Field } +// Field represents VPP binary API object field +type Field struct { + Name string + Type string + Length int + SizeFrom string +} + // Union represents VPP binary API union type Union struct { Name string @@ -75,43 +79,3 @@ const ( eventMessage // VPP event message otherMessage // other VPP message ) - -// Field represents VPP binary API object field -type Field struct { - Name string - Type string - Length int - SizeFrom string -} - -func (f *Field) IsArray() bool { - return f.Length > 0 || f.SizeFrom != "" -} - -func (s Service) MethodName() string { - reqTyp := camelCaseName(s.RequestType) - - // method name is same as parameter type name by default - method := reqTyp - if s.Stream { - // use Dump as prefix instead of suffix for stream services - if m := strings.TrimSuffix(method, "Dump"); method != m { - method = "Dump" + m - } - } - - return method -} - -func (s Service) IsDumpService() bool { - return s.Stream -} - -func (s Service) IsEventService() bool { - return len(s.Events) > 0 -} - -func (s Service) IsRequestService() bool { - // some binapi messages might have `null` reply (for example: memclnt) - return s.ReplyType != "" && s.ReplyType != "null" // not null -} diff --git a/cmd/binapi-generator/parse.go b/cmd/binapi-generator/parse.go index 07abebd..4138ac6 100644 --- a/cmd/binapi-generator/parse.go +++ b/cmd/binapi-generator/parse.go @@ -23,26 +23,62 @@ import ( "github.com/bennyscetbun/jsongo" ) +// top level objects +const ( + objTypes = "types" + objMessages = "messages" + objUnions = "unions" + objEnums = "enums" + objServices = "services" + objAliases = "aliases" + vlAPIVersion = "vl_api_version" +) + +// various object fields +const ( + crcField = "crc" + msgIdField = "_vl_msg_id" + + clientIndexField = "client_index" + contextField = "context" + + aliasLengthField = "length" + aliasTypeField = "type" + + replyField = "reply" + streamField = "stream" + eventsField = "events" +) + +// service name parts +const ( + serviceEventPrefix = "want_" + serviceDumpSuffix = "_dump" + serviceDetailsSuffix = "_details" + serviceReplySuffix = "_reply" + serviceNoReply = "null" +) + // parsePackage parses provided JSON data into objects prepared for code generation func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { - logf(" %s contains: %d services, %d messages, %d types, %d enums, %d unions, %d aliases (version: %s)", + logf(" %s (version: %s) contains: %d services, %d messages, %d types, %d enums, %d unions, %d aliases", ctx.packageName, - jsonRoot.Map("services").Len(), - jsonRoot.Map("messages").Len(), - jsonRoot.Map("types").Len(), - jsonRoot.Map("enums").Len(), - jsonRoot.Map("unions").Len(), - jsonRoot.Map("aliases").Len(), - jsonRoot.Map("vl_api_version").Get(), + jsonRoot.Map(vlAPIVersion).Get(), + jsonRoot.Map(objServices).Len(), + jsonRoot.Map(objMessages).Len(), + jsonRoot.Map(objTypes).Len(), + jsonRoot.Map(objEnums).Len(), + jsonRoot.Map(objUnions).Len(), + jsonRoot.Map(objAliases).Len(), ) pkg := Package{ - APIVersion: jsonRoot.Map("vl_api_version").Get().(string), + APIVersion: jsonRoot.Map(vlAPIVersion).Get().(string), RefMap: make(map[string]string), } // parse enums - enums := jsonRoot.Map("enums") + enums := jsonRoot.Map(objEnums) pkg.Enums = make([]Enum, enums.Len()) for i := 0; i < enums.Len(); i++ { enumNode := enums.At(i) @@ -60,7 +96,7 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { }) // parse aliases - aliases := jsonRoot.Map("aliases") + aliases := jsonRoot.Map(objAliases) if aliases.GetType() == jsongo.TypeMap { pkg.Aliases = make([]Alias, aliases.Len()) for i, key := range aliases.GetKeys() { @@ -80,7 +116,7 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { }) // parse types - types := jsonRoot.Map("types") + types := jsonRoot.Map(objTypes) pkg.Types = make([]Type, types.Len()) for i := 0; i < types.Len(); i++ { typNode := types.At(i) @@ -98,7 +134,7 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { }) // parse unions - unions := jsonRoot.Map("unions") + unions := jsonRoot.Map(objUnions) pkg.Unions = make([]Union, unions.Len()) for i := 0; i < unions.Len(); i++ { unionNode := unions.At(i) @@ -116,7 +152,7 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { }) // parse messages - messages := jsonRoot.Map("messages") + messages := jsonRoot.Map(objMessages) pkg.Messages = make([]Message, messages.Len()) for i := 0; i < messages.Len(); i++ { msgNode := messages.At(i) @@ -133,7 +169,7 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { }) // parse services - services := jsonRoot.Map("services") + services := jsonRoot.Map(objServices) if services.GetType() == jsongo.TypeMap { pkg.Services = make([]Service, services.Len()) for i, key := range services.GetKeys() { @@ -255,7 +291,7 @@ func parseUnion(ctx *context, unionNode *jsongo.JSONNode) (*Union, error) { if !ok { return nil, fmt.Errorf("union name is %T, not a string", unionNode.At(0).Get()) } - unionCRC, ok := unionNode.At(unionNode.Len() - 1).At("crc").Get().(string) + unionCRC, ok := unionNode.At(unionNode.Len() - 1).At(crcField).Get().(string) if !ok { return nil, fmt.Errorf("union crc invalid or missing") } @@ -292,7 +328,7 @@ func parseType(ctx *context, typeNode *jsongo.JSONNode) (*Type, error) { if !ok { return nil, fmt.Errorf("type name is %T, not a string", typeNode.At(0).Get()) } - typeCRC, ok := typeNode.At(typeNode.Len() - 1).At("crc").Get().(string) + typeCRC, ok := typeNode.At(typeNode.Len() - 1).At(crcField).Get().(string) if !ok { return nil, fmt.Errorf("type crc invalid or missing") } @@ -319,14 +355,9 @@ func parseType(ctx *context, typeNode *jsongo.JSONNode) (*Type, error) { return &typ, nil } -const ( - aliasesLength = "length" - aliasesType = "type" -) - // parseAlias parses VPP binary API alias object from JSON node func parseAlias(ctx *context, aliasName string, aliasNode *jsongo.JSONNode) (*Alias, error) { - if aliasNode.Len() == 0 || aliasNode.At(aliasesType).GetType() != jsongo.TypeValue { + if aliasNode.Len() == 0 || aliasNode.At(aliasTypeField).GetType() != jsongo.TypeValue { return nil, errors.New("invalid JSON for alias specified") } @@ -334,7 +365,7 @@ func parseAlias(ctx *context, aliasName string, aliasNode *jsongo.JSONNode) (*Al Name: aliasName, } - if typeNode := aliasNode.At(aliasesType); typeNode.GetType() == jsongo.TypeValue { + if typeNode := aliasNode.At(aliasTypeField); typeNode.GetType() == jsongo.TypeValue { typ, ok := typeNode.Get().(string) if !ok { return nil, fmt.Errorf("alias type is %T, not a string", typeNode.Get()) @@ -344,7 +375,7 @@ func parseAlias(ctx *context, aliasName string, aliasNode *jsongo.JSONNode) (*Al } } - if lengthNode := aliasNode.At(aliasesLength); lengthNode.GetType() == jsongo.TypeValue { + if lengthNode := aliasNode.At(aliasLengthField); lengthNode.GetType() == jsongo.TypeValue { length, ok := lengthNode.Get().(float64) if !ok { return nil, fmt.Errorf("alias length is %T, not a float64", lengthNode.Get()) @@ -365,7 +396,7 @@ func parseMessage(ctx *context, msgNode *jsongo.JSONNode) (*Message, error) { if !ok { return nil, fmt.Errorf("message name is %T, not a string", msgNode.At(0).Get()) } - msgCRC, ok := msgNode.At(msgNode.Len() - 1).At("crc").Get().(string) + msgCRC, ok := msgNode.At(msgNode.Len() - 1).At(crcField).Get().(string) if !ok { return nil, fmt.Errorf("message crc invalid or missing") @@ -432,7 +463,7 @@ func parseField(ctx *context, field *jsongo.JSONNode) (*Field, error) { // parseService parses VPP binary API service object from JSON node func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Service, error) { - if svcNode.Len() == 0 || svcNode.At("reply").GetType() != jsongo.TypeValue { + if svcNode.Len() == 0 || svcNode.At(replyField).GetType() != jsongo.TypeValue { return nil, errors.New("invalid JSON for service specified") } @@ -441,18 +472,18 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv RequestType: svcName, } - if replyNode := svcNode.At("reply"); replyNode.GetType() == jsongo.TypeValue { + if replyNode := svcNode.At(replyField); replyNode.GetType() == jsongo.TypeValue { reply, ok := replyNode.Get().(string) if !ok { return nil, fmt.Errorf("service reply is %T, not a string", replyNode.Get()) } - if reply != "null" { + if reply != serviceNoReply { svc.ReplyType = reply } } // stream service (dumps) - if streamNode := svcNode.At("stream"); streamNode.GetType() == jsongo.TypeValue { + if streamNode := svcNode.At(streamField); streamNode.GetType() == jsongo.TypeValue { var ok bool svc.Stream, ok = streamNode.Get().(bool) if !ok { @@ -461,7 +492,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv } // events service (event subscription) - if eventsNode := svcNode.At("events"); eventsNode.GetType() == jsongo.TypeArray { + if eventsNode := svcNode.At(eventsField); eventsNode.GetType() == jsongo.TypeArray { for j := 0; j < eventsNode.Len(); j++ { event := eventsNode.At(j).Get().(string) svc.Events = append(svc.Events, event) @@ -469,111 +500,30 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv } // validate service - if svc.IsEventService() { - if !strings.HasPrefix(svc.RequestType, "want_") { - log.Debugf("Unusual EVENTS SERVICE: %+v\n"+ - "- events service %q does not have 'want_' prefix in request.", - svc, svc.Name) - } - } else if svc.IsDumpService() { - if !strings.HasSuffix(svc.RequestType, "_dump") || - !strings.HasSuffix(svc.ReplyType, "_details") { - log.Debugf("Unusual STREAM SERVICE: %+v\n"+ - "- stream service %q does not have '_dump' suffix in request or reply does not have '_details' suffix.", - svc, svc.Name) - } - } else if svc.IsRequestService() { - if !strings.HasSuffix(svc.ReplyType, "_reply") { - log.Debugf("Unusual REQUEST SERVICE: %+v\n"+ - "- service %q does not have '_reply' suffix in reply.", - svc, svc.Name) + if len(svc.Events) > 0 { + // EVENT service + if !strings.HasPrefix(svc.RequestType, serviceEventPrefix) { + log.Debugf("unusual EVENTS service: %+v\n"+ + "- events service %q does not have %q prefix in request.", + svc, svc.Name, serviceEventPrefix) + } + } else if svc.Stream { + // STREAM service + if !strings.HasSuffix(svc.RequestType, serviceDumpSuffix) || + !strings.HasSuffix(svc.ReplyType, serviceDetailsSuffix) { + log.Debugf("unusual STREAM service: %+v\n"+ + "- stream service %q does not have %q suffix in request or reply does not have %q suffix.", + svc, svc.Name, serviceDumpSuffix, serviceDetailsSuffix) + } + } else if svc.ReplyType != "" && svc.ReplyType != serviceNoReply { + // REQUEST service + // some messages might have `null` reply (for example: memclnt) + if !strings.HasSuffix(svc.ReplyType, serviceReplySuffix) { + log.Debugf("unusual REQUEST service: %+v\n"+ + "- service %q does not have %q suffix in reply.", + svc, svc.Name, serviceReplySuffix) } } return &svc, nil } - -// toApiType returns name that is used as type reference in VPP binary API -func toApiType(name string) string { - return fmt.Sprintf("vl_api_%s_t", name) -} - -// convertToGoType translates the VPP binary API type into Go type -func convertToGoType(ctx *context, binapiType string) (typ string) { - if t, ok := binapiTypes[binapiType]; ok { - // basic types - typ = t - } else if r, ok := ctx.packageData.RefMap[binapiType]; ok { - // specific types (enums/types/unions) - typ = camelCaseName(r) - } else { - switch binapiType { - case "bool", "string": - typ = binapiType - default: - // fallback type - log.Warnf("found unknown VPP binary API type %q, using byte", binapiType) - typ = "byte" - } - } - return typ -} - -func getSizeOfType(typ *Type) (size int) { - for _, field := range typ.Fields { - size += getSizeOfBinapiTypeLength(field.Type, field.Length) - } - return size -} - -func getSizeOfBinapiTypeLength(typ string, length int) (size int) { - if n := getBinapiTypeSize(typ); n > 0 { - if length > 0 { - return n * length - } else { - return n - } - } - return -} - -func getTypeByRef(ctx *context, ref string) *Type { - for _, typ := range ctx.packageData.Types { - if ref == toApiType(typ.Name) { - return &typ - } - } - return nil -} - -func getAliasByRef(ctx *context, ref string) *Alias { - for _, alias := range ctx.packageData.Aliases { - if ref == toApiType(alias.Name) { - return &alias - } - } - return nil -} - -func getUnionSize(ctx *context, union *Union) (maxSize int) { - for _, field := range union.Fields { - typ := getTypeByRef(ctx, field.Type) - if typ != nil { - if size := getSizeOfType(typ); size > maxSize { - maxSize = size - } - continue - } - alias := getAliasByRef(ctx, field.Type) - if alias != nil { - if size := getSizeOfBinapiTypeLength(alias.Type, alias.Length); size > maxSize { - maxSize = size - } - continue - } else { - logf("no type or alias found for union %s field type %q", union.Name, field.Type) - continue - } - } - return -} diff --git a/cmd/binapi-generator/types.go b/cmd/binapi-generator/types.go new file mode 100644 index 0000000..3aa9819 --- /dev/null +++ b/cmd/binapi-generator/types.go @@ -0,0 +1,129 @@ +// Copyright (c) 2019 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "strconv" + "strings" +) + +// toApiType returns name that is used as type reference in VPP binary API +func toApiType(name string) string { + return fmt.Sprintf("vl_api_%s_t", name) +} + +// binapiTypes is a set of types used VPP binary API for translation to Go types +var binapiTypes = map[string]string{ + "u8": "uint8", + "i8": "int8", + "u16": "uint16", + "i16": "int16", + "u32": "uint32", + "i32": "int32", + "u64": "uint64", + "i64": "int64", + "f64": "float64", +} + +func getBinapiTypeSize(binapiType string) int { + if _, ok := binapiTypes[binapiType]; ok { + b, err := strconv.Atoi(strings.TrimLeft(binapiType, "uif")) + if err == nil { + return b / 8 + } + } + return -1 +} + +// convertToGoType translates the VPP binary API type into Go type +func convertToGoType(ctx *context, binapiType string) (typ string) { + if t, ok := binapiTypes[binapiType]; ok { + // basic types + typ = t + } else if r, ok := ctx.packageData.RefMap[binapiType]; ok { + // specific types (enums/types/unions) + typ = camelCaseName(r) + } else { + switch binapiType { + case "bool", "string": + typ = binapiType + default: + // fallback type + log.Warnf("found unknown VPP binary API type %q, using byte", binapiType) + typ = "byte" + } + } + return typ +} + +func getSizeOfType(typ *Type) (size int) { + for _, field := range typ.Fields { + size += getSizeOfBinapiTypeLength(field.Type, field.Length) + } + return size +} + +func getSizeOfBinapiTypeLength(typ string, length int) (size int) { + if n := getBinapiTypeSize(typ); n > 0 { + if length > 0 { + return n * length + } else { + return n + } + } + return +} + +func getTypeByRef(ctx *context, ref string) *Type { + for _, typ := range ctx.packageData.Types { + if ref == toApiType(typ.Name) { + return &typ + } + } + return nil +} + +func getAliasByRef(ctx *context, ref string) *Alias { + for _, alias := range ctx.packageData.Aliases { + if ref == toApiType(alias.Name) { + return &alias + } + } + return nil +} + +func getUnionSize(ctx *context, union *Union) (maxSize int) { + for _, field := range union.Fields { + typ := getTypeByRef(ctx, field.Type) + if typ != nil { + if size := getSizeOfType(typ); size > maxSize { + maxSize = size + } + continue + } + alias := getAliasByRef(ctx, field.Type) + if alias != nil { + if size := getSizeOfBinapiTypeLength(alias.Type, alias.Length); size > maxSize { + maxSize = size + } + continue + } else { + logf("no type or alias found for union %s field type %q", union.Name, field.Type) + continue + } + } + return +} -- cgit 1.2.3-korg