1 // Copyright (c) 2020 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
23 "git.fd.io/govpp.git/version"
24 "github.com/sirupsen/logrus"
27 // generatedCodeVersion indicates a version of the generated code.
28 // It is incremented whenever an incompatibility between the generated code and
29 // GoVPP api package is introduced; the generated code references
30 // a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion).
31 const generatedCodeVersion = 2
33 // common message fields
35 msgIdField = "_vl_msg_id"
36 clientIndexField = "client_index"
37 contextField = "context"
38 retvalField = "retval"
43 constModuleName = "ModuleName" // module name constant
44 constAPIVersion = "APIVersion" // API version constant
45 constVersionCrc = "VersionCrc" // version CRC constant
50 unionDataField = "XXX_UnionData" // name for the union data field
53 // MessageType represents the type of a VPP message
57 requestMessage MessageType = iota // VPP request message
58 replyMessage // VPP reply message
59 eventMessage // VPP event message
60 otherMessage // other VPP message
63 func generateFileBinapi(ctx *GenFile, w io.Writer) {
64 logf("----------------------------")
65 logf("generating BINAPI file package: %q", ctx.file.PackageName)
66 logf("----------------------------")
68 // generate file header
69 fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
70 fmt.Fprintln(w, "// versions:")
71 fmt.Fprintf(w, "// binapi-generator: %s\n", version.Version())
72 if ctx.IncludeVppVersion {
73 fmt.Fprintf(w, "// VPP: %s\n", ctx.VPPVersion)
75 fmt.Fprintf(w, "// source: %s\n", ctx.file.Path)
78 generatePackageHeader(ctx, w)
79 generateImports(ctx, w)
81 generateApiInfo(ctx, w)
83 generateMessages(ctx, w)
85 generateImportRefs(ctx, w)
88 func generatePackageHeader(ctx *GenFile, w io.Writer) {
90 fmt.Fprintf(w, "Package %s contains generated code for VPP API file %s.api (%s).\n",
91 ctx.file.PackageName, ctx.file.Name, ctx.file.Version())
93 fmt.Fprintln(w, "It consists of:")
94 printObjNum := func(obj string, num int) {
97 if strings.HasSuffix(obj, "s") {
103 fmt.Fprintf(w, "\t%3d %s\n", num, obj)
106 printObjNum("alias", len(ctx.file.Aliases))
107 printObjNum("enum", len(ctx.file.Enums))
108 printObjNum("message", len(ctx.file.Messages))
109 printObjNum("type", len(ctx.file.Structs))
110 printObjNum("union", len(ctx.file.Unions))
111 fmt.Fprintln(w, "*/")
112 fmt.Fprintf(w, "package %s\n", ctx.file.PackageName)
116 func generateImports(ctx *GenFile, w io.Writer) {
117 fmt.Fprintln(w, "import (")
118 fmt.Fprintln(w, ` "bytes"`)
119 fmt.Fprintln(w, ` "context"`)
120 fmt.Fprintln(w, ` "encoding/binary"`)
121 fmt.Fprintln(w, ` "fmt"`)
122 fmt.Fprintln(w, ` "io"`)
123 fmt.Fprintln(w, ` "math"`)
124 fmt.Fprintln(w, ` "net"`)
125 fmt.Fprintln(w, ` "strconv"`)
126 fmt.Fprintln(w, ` "strings"`)
128 fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
129 fmt.Fprintf(w, "\tcodec \"%s\"\n", "git.fd.io/govpp.git/codec")
130 fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
131 imports := listImports(ctx)
132 if len(imports) > 0 {
134 for imp, importPath := range imports {
135 fmt.Fprintf(w, "\t%s \"%s\"\n", imp, importPath)
141 fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
142 fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
143 fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the")
144 fmt.Fprintln(w, "// GoVPP api package needs to be updated.")
145 fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
149 func generateApiInfo(ctx *GenFile, w io.Writer) {
150 // generate module desc
151 fmt.Fprintln(w, "const (")
152 fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName)
153 fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.file.Name)
155 if ctx.IncludeAPIVersion {
156 fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion)
157 fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.file.Version())
158 fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc)
159 fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.file.CRC)
165 func generateTypes(ctx *GenFile, w io.Writer) {
167 if len(ctx.file.Enums) > 0 {
168 for _, enum := range ctx.file.Enums {
169 if imp, ok := ctx.file.imports[enum.Name]; ok {
170 if strings.HasSuffix(ctx.file.Name, "_types") {
171 generateImportedAlias(ctx, w, enum.GoName, imp)
175 generateEnum(ctx, w, enum)
180 if len(ctx.file.Aliases) > 0 {
181 for _, alias := range ctx.file.Aliases {
182 if imp, ok := ctx.file.imports[alias.Name]; ok {
183 if strings.HasSuffix(ctx.file.Name, "_types") {
184 generateImportedAlias(ctx, w, alias.GoName, imp)
188 generateAlias(ctx, w, alias)
193 if len(ctx.file.Structs) > 0 {
194 for _, typ := range ctx.file.Structs {
195 if imp, ok := ctx.file.imports[typ.Name]; ok {
196 if strings.HasSuffix(ctx.file.Name, "_types") {
197 generateImportedAlias(ctx, w, typ.GoName, imp)
201 generateStruct(ctx, w, typ)
206 if len(ctx.file.Unions) > 0 {
207 for _, union := range ctx.file.Unions {
208 if imp, ok := ctx.file.imports[union.Name]; ok {
209 if strings.HasSuffix(ctx.file.Name, "_types") {
210 generateImportedAlias(ctx, w, union.GoName, imp)
214 generateUnion(ctx, w, union)
219 func generateMessages(ctx *GenFile, w io.Writer) {
220 if len(ctx.file.Messages) == 0 {
224 for _, msg := range ctx.file.Messages {
225 generateMessage(ctx, w, msg)
228 // generate message registrations
229 initFnName := fmt.Sprintf("file_%s_binapi_init", ctx.file.PackageName)
231 fmt.Fprintf(w, "func init() { %s() }\n", initFnName)
232 fmt.Fprintf(w, "func %s() {\n", initFnName)
233 for _, msg := range ctx.file.Messages {
234 fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n",
235 msg.GoName, ctx.file.Name+"."+msg.GoName)
240 // generate list of messages
241 fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n")
242 fmt.Fprintln(w, "func AllMessages() []api.Message {")
243 fmt.Fprintln(w, "\treturn []api.Message{")
244 for _, msg := range ctx.file.Messages {
245 fmt.Fprintf(w, "\t(*%s)(nil),\n", msg.GoName)
251 func generateImportRefs(ctx *GenFile, w io.Writer) {
252 fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
253 fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
254 fmt.Fprintf(w, "var _ = codec.DecodeString\n")
255 fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
256 fmt.Fprintf(w, "var _ = context.Background\n")
257 fmt.Fprintf(w, "var _ = io.Copy\n")
258 fmt.Fprintf(w, "var _ = strconv.Itoa\n")
259 fmt.Fprintf(w, "var _ = strings.Contains\n")
260 fmt.Fprintf(w, "var _ = struc.Pack\n")
261 fmt.Fprintf(w, "var _ = binary.BigEndian\n")
262 fmt.Fprintf(w, "var _ = math.Float32bits\n")
263 fmt.Fprintf(w, "var _ = net.ParseIP\n")
264 fmt.Fprintf(w, "var _ = fmt.Errorf\n")
267 func generateComment(ctx *GenFile, w io.Writer, goName string, vppName string, objKind string) {
268 if objKind == "service" {
269 fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.file.Name)
271 fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
275 func generateEnum(ctx *GenFile, w io.Writer, enum *Enum) {
277 typ := binapiTypes[enum.Type]
279 logf(" writing ENUM %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
281 // generate enum comment
282 generateComment(ctx, w, name, enum.Name, "enum")
284 // generate enum definition
285 fmt.Fprintf(w, "type %s %s\n", name, typ)
288 // generate enum entries
289 fmt.Fprintln(w, "const (")
290 for _, entry := range enum.Entries {
291 fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value)
296 // generate enum conversion maps
297 fmt.Fprintln(w, "var (")
298 fmt.Fprintf(w, "%s_name = map[%s]string{\n", name, typ)
299 for _, entry := range enum.Entries {
300 fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name)
303 fmt.Fprintf(w, "%s_value = map[string]%s{\n", name, typ)
304 for _, entry := range enum.Entries {
305 fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value)
311 fmt.Fprintf(w, "func (x %s) String() string {\n", name)
312 fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ)
313 fmt.Fprintf(w, "\tif ok { return s }\n")
314 fmt.Fprintf(w, "\treturn \"%s(\" + strconv.Itoa(int(x)) + \")\"\n", name)
319 func generateImportedAlias(ctx *GenFile, w io.Writer, name string, imp string) {
320 fmt.Fprintf(w, "type %s = %s.%s\n", name, imp, name)
324 func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
327 logf(" writing ALIAS %q (%s), length: %d", alias.Name, name, alias.Length)
329 // generate struct comment
330 generateComment(ctx, w, name, alias.Name, "alias")
332 // generate struct definition
333 fmt.Fprintf(w, "type %s ", name)
335 if alias.Length > 0 {
336 fmt.Fprintf(w, "[%d]", alias.Length)
339 dataType := convertToGoType(ctx.file, alias.Type)
340 fmt.Fprintf(w, "%s\n", dataType)
342 // generate alias-specific methods
346 generateMacAddressConversion(w, name)
352 func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
355 logf(" writing STRUCT %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
357 // generate struct comment
358 generateComment(ctx, w, name, typ.Name, "type")
360 // generate struct definition
361 fmt.Fprintf(w, "type %s struct {\n", name)
363 // generate struct fields
364 for i := range typ.Fields {
365 // skip internal fields
366 switch strings.ToLower(typ.Name) {
371 generateField(ctx, w, typ.Fields, i)
374 // generate end of the struct
377 // generate name getter
378 generateTypeNameGetter(w, name, typ.Name)
380 // generate type-specific methods
384 generateIPAddressConversion(w, name)
387 generatePrefixConversion(w, name)
393 // generateUnionMethods generates methods that implement struc.Custom
394 // interface to allow having XXX_uniondata field unexported
395 // TODO: do more testing when unions are actually used in some messages
396 /*func generateUnionMethods(w io.Writer, structName string) {
397 // generate struc.Custom implementation for union
399 func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) {
400 var b = new(bytes.Buffer)
401 if err := struc.PackWithOptions(b, u.union_data, opt); err != nil {
407 func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
408 return struc.UnpackWithOptions(r, u.union_data[:], opt)
410 func (u *%[1]s) Size(opt *struc.Options) int {
411 return len(u.union_data)
413 func (u *%[1]s) String() string {
414 return string(u.union_data[:])
419 /*func generateUnionGetterSetterNew(w io.Writer, structName string, getterField, getterStruct string) {
421 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
425 func (u *%[1]s) Set%[2]s(a %[3]s) {
426 copy(u.%[4]s[:], a[:])
428 func (u *%[1]s) Get%[2]s() (a %[3]s) {
429 copy(a[:], u.%[4]s[:])
432 `, structName, getterField, getterStruct, unionDataField)
435 func generateUnion(ctx *GenFile, w io.Writer, union *Union) {
438 logf(" writing UNION %q (%s) with %d fields", union.Name, name, len(union.Fields))
440 // generate struct comment
441 generateComment(ctx, w, name, union.Name, "union")
443 // generate struct definition
444 fmt.Fprintln(w, "type", name, "struct {")
446 // maximum size for union
447 maxSize := getUnionSize(ctx.file, union)
449 // generate data field
450 fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
452 // generate end of the struct
455 // generate name getter
456 generateTypeNameGetter(w, name, union.Name)
458 // generate getters for fields
459 for _, field := range union.Fields {
460 fieldType := convertToGoType(ctx.file, field.Type)
461 generateUnionGetterSetter(w, name, field.GoName, fieldType)
464 // generate union methods
465 //generateUnionMethods(w, name)
470 func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
472 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
476 func (u *%[1]s) Set%[2]s(a %[3]s) {
477 var b = new(bytes.Buffer)
478 if err := struc.Pack(b, &a); err != nil {
481 copy(u.%[4]s[:], b.Bytes())
483 func (u *%[1]s) Get%[2]s() (a %[3]s) {
484 var b = bytes.NewReader(u.%[4]s[:])
488 `, structName, getterField, getterStruct, unionDataField)
491 func generateMessage(ctx *GenFile, w io.Writer, msg *Message) {
494 logf(" writing MESSAGE %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
496 // generate struct comment
497 generateComment(ctx, w, name, msg.Name, "message")
499 // generate struct definition
500 fmt.Fprintf(w, "type %s struct {", name)
502 msgType := otherMessage
503 wasClientIndex := false
505 // generate struct fields
507 for i, field := range msg.Fields {
509 if field.Name == clientIndexField {
510 // "client_index" as the second member,
511 // this might be an event message or a request
512 msgType = eventMessage
513 wasClientIndex = true
514 } else if field.Name == contextField {
515 // reply needs "context" as the second member
516 msgType = replyMessage
519 if wasClientIndex && field.Name == contextField {
520 // request needs "client_index" as the second member
521 // and "context" as the third member
522 msgType = requestMessage
526 // skip internal fields
527 switch strings.ToLower(field.Name) {
530 case clientIndexField, contextField:
540 generateField(ctx, w, msg.Fields, i)
543 // generate end of the struct
546 // generate message methods
547 generateMessageResetMethod(w, name)
548 generateMessageNameGetter(w, name, msg.Name)
549 generateCrcGetter(w, name, msg.CRC)
550 generateMessageTypeGetter(w, name, msgType)
551 generateMessageSize(ctx, w, name, msg.Fields)
552 generateMessageMarshal(ctx, w, name, msg.Fields)
553 generateMessageUnmarshal(ctx, w, name, msg.Fields)
558 func generateMessageSize(ctx *GenFile, w io.Writer, name string, fields []*Field) {
559 fmt.Fprintf(w, "func (m *%[1]s) Size() int {\n", name)
561 fmt.Fprintf(w, "\tif m == nil { return 0 }\n")
562 fmt.Fprintf(w, "\tvar size int\n")
564 encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
565 t, ok := BaseTypeNames[typ]
570 var s = BaseTypeSizes[t]
575 fmt.Fprintf(w, "\tsize += %d\n", s)
578 fmt.Fprintf(w, "\tsize += %d + len(%s)\n", s, name)
582 //fmt.Fprintf(w, "\tsize += %d * int(%s)\n", s, sizefrom)
583 fmt.Fprintf(w, "\tsize += %d * len(%s)\n", s, name)
586 s = BaseTypeSizes[t] * length
588 fmt.Fprintf(w, "\tsize += %d\n", s)
596 var sizeFields func(fields []*Field, parentName string)
597 sizeFields = func(fields []*Field, parentName string) {
599 defer func() { lvl-- }()
602 for _, field := range fields {
603 if field.ParentMessage != nil {
604 // skip internal fields
605 switch strings.ToLower(field.Name) {
608 case clientIndexField, contextField:
616 fieldName := field.GoName //camelCaseName(strings.TrimPrefix(field.Name, "_"))
617 name := fmt.Sprintf("%s.%s", parentName, fieldName)
618 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
619 var sizeFromName string
621 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
624 fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
626 if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
630 char := fmt.Sprintf("s%d", lvl)
631 index := fmt.Sprintf("j%d", lvl)
634 if field.Length > 0 {
635 fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
636 } else if field.SizeFrom != "" {
637 //fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
638 fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
641 fmt.Fprintf(w, "\tvar %[1]s %[2]s\n_ = %[1]s\n", char, convertToGoType(ctx.file, field.Type))
642 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
646 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
647 if encodeBaseType(enum.Type, name, 0, "") {
649 fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
651 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
652 if encodeBaseType(alias.Type, name, alias.Length, "") {
653 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
654 sizeFields(typ.Fields, name)
656 fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
658 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
659 sizeFields(typ.Fields, name)
660 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
661 maxSize := getUnionSize(ctx.file, union)
662 fmt.Fprintf(w, "\tsize += %d\n", maxSize)
664 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
668 fmt.Fprintf(w, "\t}\n")
673 sizeFields(fields, "m")
675 fmt.Fprintf(w, "return size\n")
677 fmt.Fprintf(w, "}\n")
680 func generateMessageMarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
681 fmt.Fprintf(w, "func (m *%[1]s) Marshal(b []byte) ([]byte, error) {\n", name)
683 fmt.Fprintf(w, "\to := binary.BigEndian\n")
684 fmt.Fprintf(w, "\t_ = o\n")
685 fmt.Fprintf(w, "\tpos := 0\n")
686 fmt.Fprintf(w, "\t_ = pos\n")
688 var buf = new(strings.Builder)
690 encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
691 t, ok := BaseTypeNames[typ]
696 isArray := length > 0 || sizefrom != ""
699 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
702 fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", length)
703 } else if sizefrom != "" {
704 //fmt.Fprintf(buf, "\tfor i := 0; i < int(%s); i++ {\n", sizefrom)
705 fmt.Fprintf(buf, "\tfor i := 0; i < len(%s); i++ {\n", name)
713 fmt.Fprintf(buf, "\tvar x uint8\n")
714 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint8(%s[i]) }\n", name, name)
717 fmt.Fprintf(buf, "\tbuf[pos] = uint8(%s)\n", name)
718 fmt.Fprintf(buf, "\tpos += 1\n")
720 fmt.Fprintf(buf, "\t}\n")
724 fmt.Fprintf(buf, "\tvar x uint16\n")
725 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint16(%s[i]) }\n", name, name)
728 fmt.Fprintf(buf, "\to.PutUint16(buf[pos:pos+2], uint16(%s))\n", name)
729 fmt.Fprintf(buf, "\tpos += 2\n")
731 fmt.Fprintf(buf, "\t}\n")
735 fmt.Fprintf(buf, "\tvar x uint32\n")
736 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint32(%s[i]) }\n", name, name)
739 fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(%s))\n", name)
740 fmt.Fprintf(buf, "\tpos += 4\n")
742 fmt.Fprintf(buf, "\t}\n")
746 fmt.Fprintf(buf, "\tvar x uint64\n")
747 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint64(%s[i]) }\n", name, name)
750 fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], uint64(%s))\n", name)
751 fmt.Fprintf(buf, "\tpos += 8\n")
753 fmt.Fprintf(buf, "\t}\n")
757 fmt.Fprintf(buf, "\tvar x float64\n")
758 fmt.Fprintf(buf, "\tif i < len(%s) { x = float64(%s[i]) }\n", name, name)
761 fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], math.Float64bits(float64(%s)))\n", name)
762 fmt.Fprintf(buf, "\tpos += 8\n")
764 fmt.Fprintf(buf, "\t}\n")
767 fmt.Fprintf(buf, "\tif %s { buf[pos] = 1 }\n", name)
768 fmt.Fprintf(buf, "\tpos += 1\n")
771 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s)\n", length, name)
772 fmt.Fprintf(buf, "\tpos += %d\n", length)
774 fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(len(%s)))\n", name)
775 fmt.Fprintf(buf, "\tpos += 4\n")
776 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+len(%s)], %s[:])\n", name, name)
777 fmt.Fprintf(buf, "\tpos += len(%s)\n", name)
780 fmt.Fprintf(buf, "\t// ??? %s %s\n", name, typ)
787 var encodeFields func(fields []*Field, parentName string)
788 encodeFields = func(fields []*Field, parentName string) {
790 defer func() { lvl-- }()
793 for _, field := range fields {
794 if field.ParentMessage != nil {
795 // skip internal fields
796 switch strings.ToLower(field.Name) {
799 case clientIndexField, contextField:
807 getFieldName := func(name string) string {
808 fieldName := camelCaseName(strings.TrimPrefix(name, "_"))
809 return fmt.Sprintf("%s.%s", parentName, fieldName)
812 fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
813 name := fmt.Sprintf("%s.%s", parentName, fieldName)
814 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
815 var sizeFromName string
817 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
820 fmt.Fprintf(buf, "\t// field[%d] %s\n", lvl, name)
822 getSizeOfField := func() *Field {
823 for _, f := range fields {
824 if f.SizeFrom == field.Name {
830 if f := getSizeOfField(); f != nil {
831 if encodeBaseType(field.Type, fmt.Sprintf("len(%s)", getFieldName(f.Name)), field.Length, "") {
834 panic(fmt.Sprintf("failed to encode base type of sizefrom field: %s", field.Name))
837 if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
841 char := fmt.Sprintf("v%d", lvl)
842 index := fmt.Sprintf("j%d", lvl)
845 if field.Length > 0 {
846 fmt.Fprintf(buf, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
847 } else if field.SizeFrom != "" {
848 //fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
849 fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
852 fmt.Fprintf(buf, "\tvar %s %s\n", char, convertToGoType(ctx.file, field.Type))
853 fmt.Fprintf(buf, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
857 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
858 if encodeBaseType(enum.Type, name, 0, "") {
860 fmt.Fprintf(buf, "\t// ??? ENUM %s %s\n", name, enum.Type)
862 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
863 if encodeBaseType(alias.Type, name, alias.Length, "") {
864 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
865 encodeFields(typ.Fields, name)
867 fmt.Fprintf(buf, "\t// ??? ALIAS %s %s\n", name, alias.Type)
869 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
870 encodeFields(typ.Fields, name)
871 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
872 maxSize := getUnionSize(ctx.file, union)
873 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s.%s[:])\n", maxSize, name, unionDataField)
874 fmt.Fprintf(buf, "\tpos += %d\n", maxSize)
876 fmt.Fprintf(buf, "\t// ??? buf[pos] = (%s)\n", name)
880 fmt.Fprintf(buf, "\t}\n")
885 encodeFields(fields, "m")
887 fmt.Fprintf(w, "\tvar buf []byte\n")
888 fmt.Fprintf(w, "\tif b == nil {\n")
889 fmt.Fprintf(w, "\tbuf = make([]byte, m.Size())\n")
890 fmt.Fprintf(w, "\t} else {\n")
891 fmt.Fprintf(w, "\tbuf = b\n")
892 fmt.Fprintf(w, "\t}\n")
893 fmt.Fprint(w, buf.String())
895 fmt.Fprintf(w, "return buf, nil\n")
897 fmt.Fprintf(w, "}\n")
900 func generateMessageUnmarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
901 fmt.Fprintf(w, "func (m *%[1]s) Unmarshal(tmp []byte) error {\n", name)
903 fmt.Fprintf(w, "\to := binary.BigEndian\n")
904 fmt.Fprintf(w, "\t_ = o\n")
905 fmt.Fprintf(w, "\tpos := 0\n")
906 fmt.Fprintf(w, "\t_ = pos\n")
908 decodeBaseType := func(typ, orig, name string, length int, sizefrom string, alloc bool) bool {
909 t, ok := BaseTypeNames[typ]
914 isArray := length > 0 || sizefrom != ""
917 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
921 fmt.Fprintf(w, "\t%s = make([]%s, %d)\n", name, orig, length)
922 } else if sizefrom != "" {
923 fmt.Fprintf(w, "\t%s = make([]%s, %s)\n", name, orig, sizefrom)
926 fmt.Fprintf(w, "\tfor i := 0; i < len(%s); i++ {\n", name)
933 fmt.Fprintf(w, "\t%s[i] = %s(tmp[pos])\n", name, convertToGoType(ctx.file, typ))
935 fmt.Fprintf(w, "\t%s = %s(tmp[pos])\n", name, orig)
937 fmt.Fprintf(w, "\tpos += 1\n")
939 fmt.Fprintf(w, "\t}\n")
943 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
945 fmt.Fprintf(w, "\t%s = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
947 fmt.Fprintf(w, "\tpos += 2\n")
949 fmt.Fprintf(w, "\t}\n")
953 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
955 fmt.Fprintf(w, "\t%s = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
957 fmt.Fprintf(w, "\tpos += 4\n")
959 fmt.Fprintf(w, "\t}\n")
963 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
965 fmt.Fprintf(w, "\t%s = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
967 fmt.Fprintf(w, "\tpos += 8\n")
969 fmt.Fprintf(w, "\t}\n")
973 fmt.Fprintf(w, "\t%s[i] = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
975 fmt.Fprintf(w, "\t%s = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
977 fmt.Fprintf(w, "\tpos += 8\n")
979 fmt.Fprintf(w, "\t}\n")
982 fmt.Fprintf(w, "\t%s = tmp[pos] != 0\n", name)
983 fmt.Fprintf(w, "\tpos += 1\n")
986 fmt.Fprintf(w, "\t{\n")
987 fmt.Fprintf(w, "\tnul := bytes.Index(tmp[pos:pos+%d], []byte{0x00})\n", length)
988 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+nul])\n", name)
989 fmt.Fprintf(w, "\tpos += %d\n", length)
990 fmt.Fprintf(w, "\t}\n")
992 fmt.Fprintf(w, "\t{\n")
993 fmt.Fprintf(w, "\tsiz := o.Uint32(tmp[pos:pos+4])\n")
994 fmt.Fprintf(w, "\tpos += 4\n")
995 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+int(siz)])\n", name)
996 fmt.Fprintf(w, "\tpos += len(%s)\n", name)
997 fmt.Fprintf(w, "\t}\n")
1000 fmt.Fprintf(w, "\t// ??? %s %s\n", name, typ)
1007 var decodeFields func(fields []*Field, parentName string)
1008 decodeFields = func(fields []*Field, parentName string) {
1010 defer func() { lvl-- }()
1013 for _, field := range fields {
1014 if field.ParentMessage != nil {
1015 // skip internal fields
1016 switch strings.ToLower(field.Name) {
1019 case clientIndexField, contextField:
1027 fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
1028 name := fmt.Sprintf("%s.%s", parentName, fieldName)
1029 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
1030 var sizeFromName string
1032 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
1035 fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
1037 if decodeBaseType(field.Type, convertToGoType(ctx.file, field.Type), name, field.Length, sizeFromName, true) {
1041 //char := fmt.Sprintf("v%d", lvl)
1042 index := fmt.Sprintf("j%d", lvl)
1045 if field.Length > 0 {
1046 fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
1047 } else if field.SizeFrom != "" {
1048 fieldType := getFieldType(ctx, field)
1049 if strings.HasPrefix(fieldType, "[]") {
1050 fmt.Fprintf(w, "\t%s = make(%s, int(%s.%s))\n", name, fieldType, parentName, sizeFrom)
1052 fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
1055 /*fmt.Fprintf(w, "\tvar %s %s\n", char, convertToGoType(ctx, field.Type))
1056 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
1058 name = fmt.Sprintf("%s[%s]", name, index)
1061 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
1062 if decodeBaseType(enum.Type, convertToGoType(ctx.file, field.Type), name, 0, "", false) {
1064 fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
1066 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
1067 if decodeBaseType(alias.Type, convertToGoType(ctx.file, field.Type), name, alias.Length, "", false) {
1068 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
1069 decodeFields(typ.Fields, name)
1071 fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
1073 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
1074 decodeFields(typ.Fields, name)
1075 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
1076 maxSize := getUnionSize(ctx.file, union)
1077 fmt.Fprintf(w, "\tcopy(%s.%s[:], tmp[pos:pos+%d])\n", name, unionDataField, maxSize)
1078 fmt.Fprintf(w, "\tpos += %d\n", maxSize)
1080 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
1084 fmt.Fprintf(w, "\t}\n")
1089 decodeFields(fields, "m")
1091 fmt.Fprintf(w, "return nil\n")
1093 fmt.Fprintf(w, "}\n")
1096 func getFieldType(ctx *GenFile, field *Field) string {
1097 //fieldName := strings.TrimPrefix(field.Name, "_")
1098 //fieldName = camelCaseName(fieldName)
1099 //fieldName := field.GoName
1101 dataType := convertToGoType(ctx.file, field.Type)
1102 fieldType := dataType
1104 // check if it is array
1105 if field.Length > 0 || field.SizeFrom != "" {
1106 if dataType == "uint8" {
1109 if dataType == "string" && field.Array {
1110 fieldType = "string"
1112 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1113 fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1115 fieldType = "[]" + dataType
1122 func generateField(ctx *GenFile, w io.Writer, fields []*Field, i int) {
1125 //fieldName := strings.TrimPrefix(field.Name, "_")
1126 //fieldName = camelCaseName(fieldName)
1127 fieldName := field.GoName
1129 dataType := convertToGoType(ctx.file, field.Type)
1130 fieldType := dataType
1132 // generate length field for strings
1133 if field.Type == "string" && field.Length == 0 {
1134 fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName)
1137 // check if it is array
1138 if field.Length > 0 || field.SizeFrom != "" {
1139 if dataType == "uint8" {
1142 if dataType == "string" && field.Array {
1143 fieldType = "string"
1145 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1146 fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1148 fieldType = "[]" + dataType
1151 fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
1153 fieldTags := map[string]string{}
1155 if field.Length > 0 && field.Array {
1157 fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
1159 for _, f := range fields {
1160 if f.SizeFrom == field.Name {
1161 // variable sized array
1162 //sizeOfName := camelCaseName(f.Name)
1163 fieldTags["struc"] = fmt.Sprintf("sizeof=%s", f.GoName)
1168 if ctx.IncludeBinapiNames {
1169 typ := fromApiType(field.Type)
1171 if field.Length > 0 {
1172 fieldTags["binapi"] = fmt.Sprintf("%s[%d],name=%s", typ, field.Length, field.Name)
1173 } else if field.SizeFrom != "" {
1174 fieldTags["binapi"] = fmt.Sprintf("%s[%s],name=%s", typ, field.SizeFrom, field.Name)
1177 fieldTags["binapi"] = fmt.Sprintf("%s,name=%s", typ, field.Name)
1180 if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
1181 fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], limit)
1183 if def, ok := field.Meta["default"]; ok && def != nil {
1184 actual := getActualType(ctx.file, fieldType)
1185 if t, ok := binapiTypes[actual]; ok && t != "float64" {
1186 defnum := int(def.(float64))
1187 fieldTags["binapi"] = fmt.Sprintf("%s,default=%d", fieldTags["binapi"], defnum)
1189 fieldTags["binapi"] = fmt.Sprintf("%s,default=%v", fieldTags["binapi"], def)
1193 fieldTags["json"] = fmt.Sprintf("%s,omitempty", field.Name)
1195 if len(fieldTags) > 0 {
1196 fmt.Fprintf(w, "\t`")
1198 for k := range fieldTags {
1199 keys = append(keys, k)
1203 for _, tt := range keys {
1204 t, ok := fieldTags[tt]
1212 fmt.Fprintf(w, `%s:"%s"`, tt, t)
1220 func generateMessageResetMethod(w io.Writer, structName string) {
1221 fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
1224 func generateMessageNameGetter(w io.Writer, structName, msgName string) {
1225 fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName)
1228 func generateTypeNameGetter(w io.Writer, structName, msgName string) {
1229 fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
1232 func generateIPAddressConversion(w io.Writer, structName string) {
1233 f1 := func(ipVer, ipVerExt int) string {
1234 return fmt.Sprintf(`address.Af = ADDRESS_IP%[1]d
1235 var ip%[1]daddr IP%[1]dAddress
1236 copy(ip%[1]daddr[:], netIP.To%[2]d())
1237 address.Un.SetIP%[1]d(ip%[1]daddr)`, ipVer, ipVerExt)
1239 f2 := func(ipVer, ipVerExt int) string {
1240 return fmt.Sprintf("ip%[1]dAddress := a.Un.GetIP%[1]d()\nip = net.IP(ip%[1]dAddress[:]).To%[2]d().String()",
1244 fmt.Fprintf(w, `func ParseAddress(ip string) (%[1]s, error) {
1246 netIP := net.ParseIP(ip)
1248 return address, fmt.Errorf("invalid address: %[2]s", ip)
1250 if ip4 := netIP.To4(); ip4 == nil {
1257 `, structName, "%s", f1(6, 16), f1(4, 4))
1262 fmt.Fprintf(w, `func (a *%[1]s) ToString() string {
1264 if a.Af == ADDRESS_IP6 {
1270 }`, structName, f2(6, 16), f2(4, 4))
1273 func generatePrefixConversion(w io.Writer, structName string) {
1274 fErr := func() string {
1275 return fmt.Sprintf(`if err != nil {
1276 return Prefix{}, fmt.Errorf("invalid IP %s: %s", ip, err)
1281 fmt.Fprintf(w, `func ParsePrefix(ip string) (prefix %[1]s, err error) {
1282 hasPrefix := strings.Contains(ip, "/")
1284 netIP, network, err := net.ParseCIDR(ip)
1286 maskSize, _ := network.Mask.Size()
1287 prefix.Len = byte(maskSize)
1288 prefix.Address, err = ParseAddress(netIP.String())
1291 netIP := net.ParseIP(ip)
1292 defaultMaskSize, _ := net.CIDRMask(32, 32).Size()
1293 if netIP.To4() == nil {
1294 defaultMaskSize, _ = net.CIDRMask(128, 128).Size()
1296 prefix.Len = byte(defaultMaskSize)
1297 prefix.Address, err = ParseAddress(netIP.String())
1301 }`, structName, fErr(), nil)
1306 fmt.Fprintf(w, `func (p *%[1]s) ToString() string {
1307 ip := p.Address.ToString()
1308 return ip + "/" + strconv.Itoa(int(p.Len))
1312 func generateMacAddressConversion(w io.Writer, structName string) {
1314 fmt.Fprintf(w, `func ParseMAC(mac string) (parsed %[1]s, err error) {
1315 var hw net.HardwareAddr
1316 if hw, err = net.ParseMAC(mac); err != nil {
1319 copy(parsed[:], hw[:])
1326 fmt.Fprintf(w, `func (m *%[1]s) ToString() string {
1327 return net.HardwareAddr(m[:]).String()
1331 func generateCrcGetter(w io.Writer, structName, crc string) {
1332 crc = strings.TrimPrefix(crc, "0x")
1333 fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
1336 func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
1337 fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
1338 if msgType == requestMessage {
1339 fmt.Fprintf(w, "\treturn api.RequestMessage")
1340 } else if msgType == replyMessage {
1341 fmt.Fprintf(w, "\treturn api.ReplyMessage")
1342 } else if msgType == eventMessage {
1343 fmt.Fprintf(w, "\treturn api.EventMessage")
1345 fmt.Fprintf(w, "\treturn api.OtherMessage")
1347 fmt.Fprintln(w, "}")
1351 func logf(f string, v ...interface{}) {
1352 logrus.Debugf(f, v...)