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, ` "io"`)
122 fmt.Fprintln(w, ` "math"`)
123 fmt.Fprintln(w, ` "strconv"`)
125 fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
126 fmt.Fprintf(w, "\tcodec \"%s\"\n", "git.fd.io/govpp.git/codec")
127 fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
128 imports := listImports(ctx)
129 if len(imports) > 0 {
131 for imp, importPath := range imports {
132 fmt.Fprintf(w, "\t%s \"%s\"\n", imp, importPath)
138 fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
139 fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
140 fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the")
141 fmt.Fprintln(w, "// GoVPP api package needs to be updated.")
142 fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
146 func generateApiInfo(ctx *GenFile, w io.Writer) {
147 // generate module desc
148 fmt.Fprintln(w, "const (")
149 fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName)
150 fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.file.Name)
152 if ctx.IncludeAPIVersion {
153 fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion)
154 fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.file.Version())
155 fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc)
156 fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.file.CRC)
162 func generateTypes(ctx *GenFile, w io.Writer) {
164 if len(ctx.file.Enums) > 0 {
165 for _, enum := range ctx.file.Enums {
166 if imp, ok := ctx.file.imports[enum.Name]; ok {
167 generateImportedAlias(ctx, w, enum.GoName, imp)
170 generateEnum(ctx, w, enum)
175 if len(ctx.file.Aliases) > 0 {
176 for _, alias := range ctx.file.Aliases {
177 if imp, ok := ctx.file.imports[alias.Name]; ok {
178 generateImportedAlias(ctx, w, alias.GoName, imp)
181 generateAlias(ctx, w, alias)
186 if len(ctx.file.Structs) > 0 {
187 for _, typ := range ctx.file.Structs {
188 if imp, ok := ctx.file.imports[typ.Name]; ok {
189 generateImportedAlias(ctx, w, typ.GoName, imp)
192 generateStruct(ctx, w, typ)
197 if len(ctx.file.Unions) > 0 {
198 for _, union := range ctx.file.Unions {
199 if imp, ok := ctx.file.imports[union.Name]; ok {
200 generateImportedAlias(ctx, w, union.GoName, imp)
203 generateUnion(ctx, w, union)
208 func generateMessages(ctx *GenFile, w io.Writer) {
209 if len(ctx.file.Messages) == 0 {
213 for _, msg := range ctx.file.Messages {
214 generateMessage(ctx, w, msg)
217 // generate message registrations
218 initFnName := fmt.Sprintf("file_%s_binapi_init", ctx.file.PackageName)
220 fmt.Fprintf(w, "func init() { %s() }\n", initFnName)
221 fmt.Fprintf(w, "func %s() {\n", initFnName)
222 for _, msg := range ctx.file.Messages {
223 fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n",
224 msg.GoName, ctx.file.Name+"."+msg.GoName)
229 // generate list of messages
230 fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n")
231 fmt.Fprintln(w, "func AllMessages() []api.Message {")
232 fmt.Fprintln(w, "\treturn []api.Message{")
233 for _, msg := range ctx.file.Messages {
234 fmt.Fprintf(w, "\t(*%s)(nil),\n", msg.GoName)
240 func generateImportRefs(ctx *GenFile, w io.Writer) {
241 fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
242 fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
243 fmt.Fprintf(w, "var _ = codec.DecodeString\n")
244 fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
245 fmt.Fprintf(w, "var _ = context.Background\n")
246 fmt.Fprintf(w, "var _ = io.Copy\n")
247 fmt.Fprintf(w, "var _ = strconv.Itoa\n")
248 fmt.Fprintf(w, "var _ = struc.Pack\n")
249 fmt.Fprintf(w, "var _ = binary.BigEndian\n")
250 fmt.Fprintf(w, "var _ = math.Float32bits\n")
253 func generateComment(ctx *GenFile, w io.Writer, goName string, vppName string, objKind string) {
254 if objKind == "service" {
255 fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.file.Name)
257 fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
261 func generateEnum(ctx *GenFile, w io.Writer, enum *Enum) {
263 typ := binapiTypes[enum.Type]
265 logf(" writing ENUM %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
267 // generate enum comment
268 generateComment(ctx, w, name, enum.Name, "enum")
270 // generate enum definition
271 fmt.Fprintf(w, "type %s %s\n", name, typ)
274 // generate enum entries
275 fmt.Fprintln(w, "const (")
276 for _, entry := range enum.Entries {
277 fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value)
282 // generate enum conversion maps
283 fmt.Fprintln(w, "var (")
284 fmt.Fprintf(w, "%s_name = map[%s]string{\n", name, typ)
285 for _, entry := range enum.Entries {
286 fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name)
289 fmt.Fprintf(w, "%s_value = map[string]%s{\n", name, typ)
290 for _, entry := range enum.Entries {
291 fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value)
297 fmt.Fprintf(w, "func (x %s) String() string {\n", name)
298 fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ)
299 fmt.Fprintf(w, "\tif ok { return s }\n")
300 fmt.Fprintf(w, "\treturn \"%s(\" + strconv.Itoa(int(x)) + \")\"\n", name)
305 func generateImportedAlias(ctx *GenFile, w io.Writer, name string, imp string) {
306 fmt.Fprintf(w, "type %s = %s.%s\n", name, imp, name)
310 func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
313 logf(" writing ALIAS %q (%s), length: %d", alias.Name, name, alias.Length)
315 // generate struct comment
316 generateComment(ctx, w, name, alias.Name, "alias")
318 // generate struct definition
319 fmt.Fprintf(w, "type %s ", name)
321 if alias.Length > 0 {
322 fmt.Fprintf(w, "[%d]", alias.Length)
325 dataType := convertToGoType(ctx.file, alias.Type)
326 fmt.Fprintf(w, "%s\n", dataType)
331 func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
334 logf(" writing STRUCT %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
336 // generate struct comment
337 generateComment(ctx, w, name, typ.Name, "type")
339 // generate struct definition
340 fmt.Fprintf(w, "type %s struct {\n", name)
342 // generate struct fields
343 for i := range typ.Fields {
344 // skip internal fields
345 switch strings.ToLower(typ.Name) {
350 generateField(ctx, w, typ.Fields, i)
353 // generate end of the struct
356 // generate name getter
357 generateTypeNameGetter(w, name, typ.Name)
362 // generateUnionMethods generates methods that implement struc.Custom
363 // interface to allow having XXX_uniondata field unexported
364 // TODO: do more testing when unions are actually used in some messages
365 /*func generateUnionMethods(w io.Writer, structName string) {
366 // generate struc.Custom implementation for union
368 func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) {
369 var b = new(bytes.Buffer)
370 if err := struc.PackWithOptions(b, u.union_data, opt); err != nil {
376 func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
377 return struc.UnpackWithOptions(r, u.union_data[:], opt)
379 func (u *%[1]s) Size(opt *struc.Options) int {
380 return len(u.union_data)
382 func (u *%[1]s) String() string {
383 return string(u.union_data[:])
388 /*func generateUnionGetterSetterNew(w io.Writer, structName string, getterField, getterStruct string) {
390 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
394 func (u *%[1]s) Set%[2]s(a %[3]s) {
395 copy(u.%[4]s[:], a[:])
397 func (u *%[1]s) Get%[2]s() (a %[3]s) {
398 copy(a[:], u.%[4]s[:])
401 `, structName, getterField, getterStruct, unionDataField)
404 func generateUnion(ctx *GenFile, w io.Writer, union *Union) {
407 logf(" writing UNION %q (%s) with %d fields", union.Name, name, len(union.Fields))
409 // generate struct comment
410 generateComment(ctx, w, name, union.Name, "union")
412 // generate struct definition
413 fmt.Fprintln(w, "type", name, "struct {")
415 // maximum size for union
416 maxSize := getUnionSize(ctx.file, union)
418 // generate data field
419 fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
421 // generate end of the struct
424 // generate name getter
425 generateTypeNameGetter(w, name, union.Name)
427 // generate getters for fields
428 for _, field := range union.Fields {
429 fieldType := convertToGoType(ctx.file, field.Type)
430 generateUnionGetterSetter(w, name, field.GoName, fieldType)
433 // generate union methods
434 //generateUnionMethods(w, name)
439 func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
441 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
445 func (u *%[1]s) Set%[2]s(a %[3]s) {
446 var b = new(bytes.Buffer)
447 if err := struc.Pack(b, &a); err != nil {
450 copy(u.%[4]s[:], b.Bytes())
452 func (u *%[1]s) Get%[2]s() (a %[3]s) {
453 var b = bytes.NewReader(u.%[4]s[:])
457 `, structName, getterField, getterStruct, unionDataField)
460 func generateMessage(ctx *GenFile, w io.Writer, msg *Message) {
463 logf(" writing MESSAGE %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
465 // generate struct comment
466 generateComment(ctx, w, name, msg.Name, "message")
468 // generate struct definition
469 fmt.Fprintf(w, "type %s struct {", name)
471 msgType := otherMessage
472 wasClientIndex := false
474 // generate struct fields
476 for i, field := range msg.Fields {
478 if field.Name == clientIndexField {
479 // "client_index" as the second member,
480 // this might be an event message or a request
481 msgType = eventMessage
482 wasClientIndex = true
483 } else if field.Name == contextField {
484 // reply needs "context" as the second member
485 msgType = replyMessage
488 if wasClientIndex && field.Name == contextField {
489 // request needs "client_index" as the second member
490 // and "context" as the third member
491 msgType = requestMessage
495 // skip internal fields
496 switch strings.ToLower(field.Name) {
499 case clientIndexField, contextField:
509 generateField(ctx, w, msg.Fields, i)
512 // generate end of the struct
515 // generate message methods
516 generateMessageResetMethod(w, name)
517 generateMessageNameGetter(w, name, msg.Name)
518 generateCrcGetter(w, name, msg.CRC)
519 generateMessageTypeGetter(w, name, msgType)
520 generateMessageSize(ctx, w, name, msg.Fields)
521 generateMessageMarshal(ctx, w, name, msg.Fields)
522 generateMessageUnmarshal(ctx, w, name, msg.Fields)
527 func generateMessageSize(ctx *GenFile, w io.Writer, name string, fields []*Field) {
528 fmt.Fprintf(w, "func (m *%[1]s) Size() int {\n", name)
530 fmt.Fprintf(w, "\tif m == nil { return 0 }\n")
531 fmt.Fprintf(w, "\tvar size int\n")
533 encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
534 t, ok := BaseTypeNames[typ]
539 var s = BaseTypeSizes[t]
544 fmt.Fprintf(w, "\tsize += %d\n", s)
547 fmt.Fprintf(w, "\tsize += %d + len(%s)\n", s, name)
551 //fmt.Fprintf(w, "\tsize += %d * int(%s)\n", s, sizefrom)
552 fmt.Fprintf(w, "\tsize += %d * len(%s)\n", s, name)
555 s = BaseTypeSizes[t] * length
557 fmt.Fprintf(w, "\tsize += %d\n", s)
565 var sizeFields func(fields []*Field, parentName string)
566 sizeFields = func(fields []*Field, parentName string) {
568 defer func() { lvl-- }()
571 for _, field := range fields {
572 if field.ParentMessage != nil {
573 // skip internal fields
574 switch strings.ToLower(field.Name) {
577 case clientIndexField, contextField:
585 fieldName := field.GoName //camelCaseName(strings.TrimPrefix(field.Name, "_"))
586 name := fmt.Sprintf("%s.%s", parentName, fieldName)
587 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
588 var sizeFromName string
590 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
593 fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
595 if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
599 char := fmt.Sprintf("s%d", lvl)
600 index := fmt.Sprintf("j%d", lvl)
603 if field.Length > 0 {
604 fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
605 } else if field.SizeFrom != "" {
606 //fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
607 fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
610 fmt.Fprintf(w, "\tvar %[1]s %[2]s\n_ = %[1]s\n", char, convertToGoType(ctx.file, field.Type))
611 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
615 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
616 if encodeBaseType(enum.Type, name, 0, "") {
618 fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
620 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
621 if encodeBaseType(alias.Type, name, alias.Length, "") {
622 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
623 sizeFields(typ.Fields, name)
625 fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
627 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
628 sizeFields(typ.Fields, name)
629 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
630 maxSize := getUnionSize(ctx.file, union)
631 fmt.Fprintf(w, "\tsize += %d\n", maxSize)
633 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
637 fmt.Fprintf(w, "\t}\n")
642 sizeFields(fields, "m")
644 fmt.Fprintf(w, "return size\n")
646 fmt.Fprintf(w, "}\n")
649 func generateMessageMarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
650 fmt.Fprintf(w, "func (m *%[1]s) Marshal(b []byte) ([]byte, error) {\n", name)
652 fmt.Fprintf(w, "\to := binary.BigEndian\n")
653 fmt.Fprintf(w, "\t_ = o\n")
654 fmt.Fprintf(w, "\tpos := 0\n")
655 fmt.Fprintf(w, "\t_ = pos\n")
657 var buf = new(strings.Builder)
659 encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
660 t, ok := BaseTypeNames[typ]
665 isArray := length > 0 || sizefrom != ""
668 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
671 fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", length)
672 } else if sizefrom != "" {
673 //fmt.Fprintf(buf, "\tfor i := 0; i < int(%s); i++ {\n", sizefrom)
674 fmt.Fprintf(buf, "\tfor i := 0; i < len(%s); i++ {\n", name)
682 fmt.Fprintf(buf, "\tvar x uint8\n")
683 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint8(%s[i]) }\n", name, name)
686 fmt.Fprintf(buf, "\tbuf[pos] = uint8(%s)\n", name)
687 fmt.Fprintf(buf, "\tpos += 1\n")
689 fmt.Fprintf(buf, "\t}\n")
693 fmt.Fprintf(buf, "\tvar x uint16\n")
694 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint16(%s[i]) }\n", name, name)
697 fmt.Fprintf(buf, "\to.PutUint16(buf[pos:pos+2], uint16(%s))\n", name)
698 fmt.Fprintf(buf, "\tpos += 2\n")
700 fmt.Fprintf(buf, "\t}\n")
704 fmt.Fprintf(buf, "\tvar x uint32\n")
705 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint32(%s[i]) }\n", name, name)
708 fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(%s))\n", name)
709 fmt.Fprintf(buf, "\tpos += 4\n")
711 fmt.Fprintf(buf, "\t}\n")
715 fmt.Fprintf(buf, "\tvar x uint64\n")
716 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint64(%s[i]) }\n", name, name)
719 fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], uint64(%s))\n", name)
720 fmt.Fprintf(buf, "\tpos += 8\n")
722 fmt.Fprintf(buf, "\t}\n")
726 fmt.Fprintf(buf, "\tvar x float64\n")
727 fmt.Fprintf(buf, "\tif i < len(%s) { x = float64(%s[i]) }\n", name, name)
730 fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], math.Float64bits(float64(%s)))\n", name)
731 fmt.Fprintf(buf, "\tpos += 8\n")
733 fmt.Fprintf(buf, "\t}\n")
736 fmt.Fprintf(buf, "\tif %s { buf[pos] = 1 }\n", name)
737 fmt.Fprintf(buf, "\tpos += 1\n")
740 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s)\n", length, name)
741 fmt.Fprintf(buf, "\tpos += %d\n", length)
743 fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(len(%s)))\n", name)
744 fmt.Fprintf(buf, "\tpos += 4\n")
745 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+len(%s)], %s[:])\n", name, name)
746 fmt.Fprintf(buf, "\tpos += len(%s)\n", name)
749 fmt.Fprintf(buf, "\t// ??? %s %s\n", name, typ)
756 var encodeFields func(fields []*Field, parentName string)
757 encodeFields = func(fields []*Field, parentName string) {
759 defer func() { lvl-- }()
762 for _, field := range fields {
763 if field.ParentMessage != nil {
764 // skip internal fields
765 switch strings.ToLower(field.Name) {
768 case clientIndexField, contextField:
776 getFieldName := func(name string) string {
777 fieldName := camelCaseName(strings.TrimPrefix(name, "_"))
778 return fmt.Sprintf("%s.%s", parentName, fieldName)
781 fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
782 name := fmt.Sprintf("%s.%s", parentName, fieldName)
783 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
784 var sizeFromName string
786 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
789 fmt.Fprintf(buf, "\t// field[%d] %s\n", lvl, name)
791 getSizeOfField := func() *Field {
792 for _, f := range fields {
793 if f.SizeFrom == field.Name {
799 if f := getSizeOfField(); f != nil {
800 if encodeBaseType(field.Type, fmt.Sprintf("len(%s)", getFieldName(f.Name)), field.Length, "") {
803 panic(fmt.Sprintf("failed to encode base type of sizefrom field: %s", field.Name))
806 if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
810 char := fmt.Sprintf("v%d", lvl)
811 index := fmt.Sprintf("j%d", lvl)
814 if field.Length > 0 {
815 fmt.Fprintf(buf, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
816 } else if field.SizeFrom != "" {
817 //fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
818 fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
821 fmt.Fprintf(buf, "\tvar %s %s\n", char, convertToGoType(ctx.file, field.Type))
822 fmt.Fprintf(buf, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
826 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
827 if encodeBaseType(enum.Type, name, 0, "") {
829 fmt.Fprintf(buf, "\t// ??? ENUM %s %s\n", name, enum.Type)
831 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
832 if encodeBaseType(alias.Type, name, alias.Length, "") {
833 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
834 encodeFields(typ.Fields, name)
836 fmt.Fprintf(buf, "\t// ??? ALIAS %s %s\n", name, alias.Type)
838 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
839 encodeFields(typ.Fields, name)
840 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
841 maxSize := getUnionSize(ctx.file, union)
842 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s.%s[:])\n", maxSize, name, unionDataField)
843 fmt.Fprintf(buf, "\tpos += %d\n", maxSize)
845 fmt.Fprintf(buf, "\t// ??? buf[pos] = (%s)\n", name)
849 fmt.Fprintf(buf, "\t}\n")
854 encodeFields(fields, "m")
856 fmt.Fprintf(w, "\tvar buf []byte\n")
857 fmt.Fprintf(w, "\tif b == nil {\n")
858 fmt.Fprintf(w, "\tbuf = make([]byte, m.Size())\n")
859 fmt.Fprintf(w, "\t} else {\n")
860 fmt.Fprintf(w, "\tbuf = b\n")
861 fmt.Fprintf(w, "\t}\n")
862 fmt.Fprint(w, buf.String())
864 fmt.Fprintf(w, "return buf, nil\n")
866 fmt.Fprintf(w, "}\n")
869 func generateMessageUnmarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
870 fmt.Fprintf(w, "func (m *%[1]s) Unmarshal(tmp []byte) error {\n", name)
872 fmt.Fprintf(w, "\to := binary.BigEndian\n")
873 fmt.Fprintf(w, "\t_ = o\n")
874 fmt.Fprintf(w, "\tpos := 0\n")
875 fmt.Fprintf(w, "\t_ = pos\n")
877 decodeBaseType := func(typ, orig, name string, length int, sizefrom string, alloc bool) bool {
878 t, ok := BaseTypeNames[typ]
883 isArray := length > 0 || sizefrom != ""
886 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
890 fmt.Fprintf(w, "\t%s = make([]%s, %d)\n", name, orig, length)
891 } else if sizefrom != "" {
892 fmt.Fprintf(w, "\t%s = make([]%s, %s)\n", name, orig, sizefrom)
895 fmt.Fprintf(w, "\tfor i := 0; i < len(%s); i++ {\n", name)
902 fmt.Fprintf(w, "\t%s[i] = %s(tmp[pos])\n", name, convertToGoType(ctx.file, typ))
904 fmt.Fprintf(w, "\t%s = %s(tmp[pos])\n", name, orig)
906 fmt.Fprintf(w, "\tpos += 1\n")
908 fmt.Fprintf(w, "\t}\n")
912 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
914 fmt.Fprintf(w, "\t%s = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
916 fmt.Fprintf(w, "\tpos += 2\n")
918 fmt.Fprintf(w, "\t}\n")
922 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
924 fmt.Fprintf(w, "\t%s = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
926 fmt.Fprintf(w, "\tpos += 4\n")
928 fmt.Fprintf(w, "\t}\n")
932 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
934 fmt.Fprintf(w, "\t%s = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
936 fmt.Fprintf(w, "\tpos += 8\n")
938 fmt.Fprintf(w, "\t}\n")
942 fmt.Fprintf(w, "\t%s[i] = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
944 fmt.Fprintf(w, "\t%s = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
946 fmt.Fprintf(w, "\tpos += 8\n")
948 fmt.Fprintf(w, "\t}\n")
951 fmt.Fprintf(w, "\t%s = tmp[pos] != 0\n", name)
952 fmt.Fprintf(w, "\tpos += 1\n")
955 fmt.Fprintf(w, "\t{\n")
956 fmt.Fprintf(w, "\tnul := bytes.Index(tmp[pos:pos+%d], []byte{0x00})\n", length)
957 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+nul])\n", name)
958 fmt.Fprintf(w, "\tpos += %d\n", length)
959 fmt.Fprintf(w, "\t}\n")
961 fmt.Fprintf(w, "\t{\n")
962 fmt.Fprintf(w, "\tsiz := o.Uint32(tmp[pos:pos+4])\n")
963 fmt.Fprintf(w, "\tpos += 4\n")
964 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+int(siz)])\n", name)
965 fmt.Fprintf(w, "\tpos += len(%s)\n", name)
966 fmt.Fprintf(w, "\t}\n")
969 fmt.Fprintf(w, "\t// ??? %s %s\n", name, typ)
976 var decodeFields func(fields []*Field, parentName string)
977 decodeFields = func(fields []*Field, parentName string) {
979 defer func() { lvl-- }()
982 for _, field := range fields {
983 if field.ParentMessage != nil {
984 // skip internal fields
985 switch strings.ToLower(field.Name) {
988 case clientIndexField, contextField:
996 fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
997 name := fmt.Sprintf("%s.%s", parentName, fieldName)
998 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
999 var sizeFromName string
1001 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
1004 fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
1006 if decodeBaseType(field.Type, convertToGoType(ctx.file, field.Type), name, field.Length, sizeFromName, true) {
1010 //char := fmt.Sprintf("v%d", lvl)
1011 index := fmt.Sprintf("j%d", lvl)
1014 if field.Length > 0 {
1015 fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
1016 } else if field.SizeFrom != "" {
1017 fieldType := getFieldType(ctx, field)
1018 if strings.HasPrefix(fieldType, "[]") {
1019 fmt.Fprintf(w, "\t%s = make(%s, int(%s.%s))\n", name, fieldType, parentName, sizeFrom)
1021 fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
1024 /*fmt.Fprintf(w, "\tvar %s %s\n", char, convertToGoType(ctx, field.Type))
1025 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
1027 name = fmt.Sprintf("%s[%s]", name, index)
1030 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
1031 if decodeBaseType(enum.Type, convertToGoType(ctx.file, field.Type), name, 0, "", false) {
1033 fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
1035 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
1036 if decodeBaseType(alias.Type, convertToGoType(ctx.file, field.Type), name, alias.Length, "", false) {
1037 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
1038 decodeFields(typ.Fields, name)
1040 fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
1042 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
1043 decodeFields(typ.Fields, name)
1044 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
1045 maxSize := getUnionSize(ctx.file, union)
1046 fmt.Fprintf(w, "\tcopy(%s.%s[:], tmp[pos:pos+%d])\n", name, unionDataField, maxSize)
1047 fmt.Fprintf(w, "\tpos += %d\n", maxSize)
1049 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
1053 fmt.Fprintf(w, "\t}\n")
1058 decodeFields(fields, "m")
1060 fmt.Fprintf(w, "return nil\n")
1062 fmt.Fprintf(w, "}\n")
1065 func getFieldType(ctx *GenFile, field *Field) string {
1066 //fieldName := strings.TrimPrefix(field.Name, "_")
1067 //fieldName = camelCaseName(fieldName)
1068 //fieldName := field.GoName
1070 dataType := convertToGoType(ctx.file, field.Type)
1071 fieldType := dataType
1073 // check if it is array
1074 if field.Length > 0 || field.SizeFrom != "" {
1075 if dataType == "uint8" {
1078 if dataType == "string" && field.Array {
1079 fieldType = "string"
1081 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1082 fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1084 fieldType = "[]" + dataType
1091 func generateField(ctx *GenFile, w io.Writer, fields []*Field, i int) {
1094 //fieldName := strings.TrimPrefix(field.Name, "_")
1095 //fieldName = camelCaseName(fieldName)
1096 fieldName := field.GoName
1098 dataType := convertToGoType(ctx.file, field.Type)
1099 fieldType := dataType
1101 // generate length field for strings
1102 if field.Type == "string" && field.Length == 0 {
1103 fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName)
1106 // check if it is array
1107 if field.Length > 0 || field.SizeFrom != "" {
1108 if dataType == "uint8" {
1111 if dataType == "string" && field.Array {
1112 fieldType = "string"
1114 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1115 fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1117 fieldType = "[]" + dataType
1120 fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
1122 fieldTags := map[string]string{}
1124 if field.Length > 0 && field.Array {
1126 fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
1128 for _, f := range fields {
1129 if f.SizeFrom == field.Name {
1130 // variable sized array
1131 //sizeOfName := camelCaseName(f.Name)
1132 fieldTags["struc"] = fmt.Sprintf("sizeof=%s", f.GoName)
1137 if ctx.IncludeBinapiNames {
1138 typ := fromApiType(field.Type)
1140 if field.Length > 0 {
1141 fieldTags["binapi"] = fmt.Sprintf("%s[%d],name=%s", typ, field.Length, field.Name)
1142 } else if field.SizeFrom != "" {
1143 fieldTags["binapi"] = fmt.Sprintf("%s[%s],name=%s", typ, field.SizeFrom, field.Name)
1146 fieldTags["binapi"] = fmt.Sprintf("%s,name=%s", typ, field.Name)
1149 if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
1150 fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], limit)
1152 if def, ok := field.Meta["default"]; ok && def != nil {
1153 actual := getActualType(ctx.file, fieldType)
1154 if t, ok := binapiTypes[actual]; ok && t != "float64" {
1155 defnum := int(def.(float64))
1156 fieldTags["binapi"] = fmt.Sprintf("%s,default=%d", fieldTags["binapi"], defnum)
1158 fieldTags["binapi"] = fmt.Sprintf("%s,default=%v", fieldTags["binapi"], def)
1162 fieldTags["json"] = fmt.Sprintf("%s,omitempty", field.Name)
1164 if len(fieldTags) > 0 {
1165 fmt.Fprintf(w, "\t`")
1167 for k := range fieldTags {
1168 keys = append(keys, k)
1172 for _, tt := range keys {
1173 t, ok := fieldTags[tt]
1181 fmt.Fprintf(w, `%s:"%s"`, tt, t)
1189 func generateMessageResetMethod(w io.Writer, structName string) {
1190 fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
1193 func generateMessageNameGetter(w io.Writer, structName, msgName string) {
1194 fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName)
1197 func generateTypeNameGetter(w io.Writer, structName, msgName string) {
1198 fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
1201 func generateCrcGetter(w io.Writer, structName, crc string) {
1202 crc = strings.TrimPrefix(crc, "0x")
1203 fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
1206 func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
1207 fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
1208 if msgType == requestMessage {
1209 fmt.Fprintf(w, "\treturn api.RequestMessage")
1210 } else if msgType == replyMessage {
1211 fmt.Fprintf(w, "\treturn api.ReplyMessage")
1212 } else if msgType == eventMessage {
1213 fmt.Fprintf(w, "\treturn api.EventMessage")
1215 fmt.Fprintf(w, "\treturn api.OtherMessage")
1217 fmt.Fprintln(w, "}")
1221 func logf(f string, v ...interface{}) {
1222 logrus.Debugf(f, v...)