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.
27 "git.fd.io/govpp.git/version"
30 // generatedCodeVersion indicates a version of the generated code.
31 // It is incremented whenever an incompatibility between the generated code and
32 // GoVPP api package is introduced; the generated code references
33 // a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion).
34 const generatedCodeVersion = 2
36 // message field names
38 msgIdField = "_vl_msg_id"
39 clientIndexField = "client_index"
40 contextField = "context"
41 retvalField = "retval"
45 outputFileExt = ".ba.go" // file extension of the Go generated files
46 rpcFileSuffix = "_rpc" // file name suffix for the RPC services
48 constModuleName = "ModuleName" // module name constant
49 constAPIVersion = "APIVersion" // API version constant
50 constVersionCrc = "VersionCrc" // version CRC constant
52 unionDataField = "XXX_UnionData" // name for the union data field
54 serviceApiName = "RPCService" // name for the RPC service interface
55 serviceImplName = "serviceClient" // name for the RPC service implementation
56 serviceClientName = "ServiceClient" // name for the RPC service client
58 // TODO: register service descriptor
59 //serviceDescType = "ServiceDesc" // name for service descriptor type
60 //serviceDescName = "_ServiceRPC_serviceDesc" // name for service descriptor var
63 // MessageType represents the type of a VPP message
67 requestMessage MessageType = iota // VPP request message
68 replyMessage // VPP reply message
69 eventMessage // VPP event message
70 otherMessage // other VPP message
81 func generatePackage(ctx *GenFile, w io.Writer) {
82 logf("----------------------------")
83 logf("generating binapi package: %q", ctx.file.PackageName)
84 logf("----------------------------")
86 generateHeader(ctx, w)
87 generateImports(ctx, w)
89 // generate module desc
90 fmt.Fprintln(w, "const (")
91 fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName)
92 fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.file.Name)
94 if ctx.IncludeAPIVersion {
95 fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion)
96 fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.file.Version())
97 fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc)
98 fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.file.CRC)
104 if len(ctx.file.Enums) > 0 {
105 for _, enum := range ctx.file.Enums {
106 if imp, ok := ctx.file.imports[enum.Name]; ok {
107 generateImportedAlias(ctx, w, enum.GoName, imp)
110 generateEnum(ctx, w, enum)
115 if len(ctx.file.Aliases) > 0 {
116 for _, alias := range ctx.file.Aliases {
117 if imp, ok := ctx.file.imports[alias.Name]; ok {
118 generateImportedAlias(ctx, w, alias.GoName, imp)
121 generateAlias(ctx, w, alias)
126 if len(ctx.file.Structs) > 0 {
127 for _, typ := range ctx.file.Structs {
128 if imp, ok := ctx.file.imports[typ.Name]; ok {
129 generateImportedAlias(ctx, w, typ.GoName, imp)
132 generateStruct(ctx, w, typ)
137 if len(ctx.file.Unions) > 0 {
138 for _, union := range ctx.file.Unions {
139 if imp, ok := ctx.file.imports[union.Name]; ok {
140 generateImportedAlias(ctx, w, union.GoName, imp)
143 generateUnion(ctx, w, union)
148 if len(ctx.file.Messages) > 0 {
149 for _, msg := range ctx.file.Messages {
150 generateMessage(ctx, w, msg)
153 initFnName := fmt.Sprintf("file_%s_binapi_init", ctx.file.PackageName)
155 // generate message registrations
156 fmt.Fprintf(w, "func init() { %s() }\n", initFnName)
157 fmt.Fprintf(w, "func %s() {\n", initFnName)
158 for _, msg := range ctx.file.Messages {
159 fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n",
160 msg.GoName, ctx.file.Name+"."+msg.GoName)
165 // generate list of messages
166 fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n")
167 fmt.Fprintln(w, "func AllMessages() []api.Message {")
168 fmt.Fprintln(w, "\treturn []api.Message{")
169 for _, msg := range ctx.file.Messages {
170 fmt.Fprintf(w, "\t(*%s)(nil),\n", msg.GoName)
176 generateFooter(ctx, w)
180 func generateHeader(ctx *GenFile, w io.Writer) {
181 fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
182 fmt.Fprintln(w, "// versions:")
183 fmt.Fprintf(w, "// binapi-generator: %s\n", version.Version())
184 if ctx.IncludeVppVersion {
185 fmt.Fprintf(w, "// VPP: %s\n", ctx.VPPVersion)
187 fmt.Fprintf(w, "// source: %s\n", ctx.file.Path)
190 fmt.Fprintln(w, "/*")
191 fmt.Fprintf(w, "Package %s contains generated code for VPP binary API defined by %s.api (version %s).\n",
192 ctx.file.PackageName, ctx.file.Name, ctx.file.Version())
194 fmt.Fprintln(w, "It consists of:")
195 printObjNum := func(obj string, num int) {
198 if strings.HasSuffix(obj, "s") {
205 fmt.Fprintf(w, "\t%3d %s\n", num, obj)
208 //printObjNum("RPC", len(ctx.file.Service.RPCs))
209 printObjNum("alias", len(ctx.file.Aliases))
210 printObjNum("enum", len(ctx.file.Enums))
211 printObjNum("message", len(ctx.file.Messages))
212 printObjNum("type", len(ctx.file.Structs))
213 printObjNum("union", len(ctx.file.Unions))
214 fmt.Fprintln(w, "*/")
215 fmt.Fprintf(w, "package %s\n", ctx.file.PackageName)
219 func generateImports(ctx *GenFile, w io.Writer) {
220 fmt.Fprintln(w, "import (")
221 fmt.Fprintln(w, ` "bytes"`)
222 fmt.Fprintln(w, ` "context"`)
223 fmt.Fprintln(w, ` "encoding/binary"`)
224 fmt.Fprintln(w, ` "io"`)
225 fmt.Fprintln(w, ` "math"`)
226 fmt.Fprintln(w, ` "strconv"`)
228 fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
229 fmt.Fprintf(w, "\tcodec \"%s\"\n", "git.fd.io/govpp.git/codec")
230 fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
231 if len(ctx.file.Imports) > 0 {
233 for _, imp := range ctx.file.Imports {
234 importPath := path.Join(ctx.ImportPrefix, imp)
235 if ctx.ImportPrefix == "" {
236 importPath = getImportPath(ctx.packageDir, imp)
238 fmt.Fprintf(w, "\t%s \"%s\"\n", imp, strings.TrimSpace(importPath))
244 fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
245 fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
246 fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the")
247 fmt.Fprintln(w, "// GoVPP api package needs to be updated.")
248 fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
252 func getImportPath(outputDir string, pkg string) string {
253 absPath, err := filepath.Abs(filepath.Join(outputDir, "..", pkg))
257 cmd := exec.Command("go", "list", absPath)
258 var errbuf, outbuf bytes.Buffer
261 if err := cmd.Run(); err != nil {
262 fmt.Printf("ERR: %v\n", errbuf.String())
265 return outbuf.String()
268 func generateFooter(ctx *GenFile, w io.Writer) {
269 fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
270 fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
271 fmt.Fprintf(w, "var _ = codec.DecodeString\n")
272 fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
273 fmt.Fprintf(w, "var _ = context.Background\n")
274 fmt.Fprintf(w, "var _ = io.Copy\n")
275 fmt.Fprintf(w, "var _ = strconv.Itoa\n")
276 fmt.Fprintf(w, "var _ = struc.Pack\n")
277 fmt.Fprintf(w, "var _ = binary.BigEndian\n")
278 fmt.Fprintf(w, "var _ = math.Float32bits\n")
281 func generateComment(ctx *GenFile, w io.Writer, goName string, vppName string, objKind string) {
282 if objKind == "service" {
283 fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.file.Name)
285 fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
289 func generateEnum(ctx *GenFile, w io.Writer, enum *Enum) {
291 typ := binapiTypes[enum.Type]
293 logf(" writing ENUM %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
295 // generate enum comment
296 generateComment(ctx, w, name, enum.Name, "enum")
298 // generate enum definition
299 fmt.Fprintf(w, "type %s %s\n", name, typ)
302 // generate enum entries
303 fmt.Fprintln(w, "const (")
304 for _, entry := range enum.Entries {
305 fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value)
310 // generate enum conversion maps
311 fmt.Fprintln(w, "var (")
312 fmt.Fprintf(w, "%s_name = map[%s]string{\n", name, typ)
313 for _, entry := range enum.Entries {
314 fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name)
317 fmt.Fprintf(w, "%s_value = map[string]%s{\n", name, typ)
318 for _, entry := range enum.Entries {
319 fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value)
325 fmt.Fprintf(w, "func (x %s) String() string {\n", name)
326 fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ)
327 fmt.Fprintf(w, "\tif ok { return s }\n")
328 fmt.Fprintf(w, "\treturn \"%s(\" + strconv.Itoa(int(x)) + \")\"\n", name)
333 func generateImportedAlias(ctx *GenFile, w io.Writer, name string, imp string) {
334 fmt.Fprintf(w, "type %s = %s.%s\n", name, imp, name)
338 func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
341 logf(" writing ALIAS %q (%s), length: %d", alias.Name, name, alias.Length)
343 // generate struct comment
344 generateComment(ctx, w, name, alias.Name, "alias")
346 // generate struct definition
347 fmt.Fprintf(w, "type %s ", name)
349 if alias.Length > 0 {
350 fmt.Fprintf(w, "[%d]", alias.Length)
353 dataType := convertToGoType(ctx.file, alias.Type)
354 fmt.Fprintf(w, "%s\n", dataType)
359 func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
362 logf(" writing STRUCT %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
364 // generate struct comment
365 generateComment(ctx, w, name, typ.Name, "type")
367 // generate struct definition
368 fmt.Fprintf(w, "type %s struct {\n", name)
370 // generate struct fields
371 for i := range typ.Fields {
372 // skip internal fields
373 switch strings.ToLower(typ.Name) {
378 generateField(ctx, w, typ.Fields, i)
381 // generate end of the struct
384 // generate name getter
385 generateTypeNameGetter(w, name, typ.Name)
390 // generateUnionMethods generates methods that implement struc.Custom
391 // interface to allow having XXX_uniondata field unexported
392 // TODO: do more testing when unions are actually used in some messages
393 /*func generateUnionMethods(w io.Writer, structName string) {
394 // generate struc.Custom implementation for union
396 func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) {
397 var b = new(bytes.Buffer)
398 if err := struc.PackWithOptions(b, u.union_data, opt); err != nil {
404 func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
405 return struc.UnpackWithOptions(r, u.union_data[:], opt)
407 func (u *%[1]s) Size(opt *struc.Options) int {
408 return len(u.union_data)
410 func (u *%[1]s) String() string {
411 return string(u.union_data[:])
416 /*func generateUnionGetterSetterNew(w io.Writer, structName string, getterField, getterStruct string) {
418 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
422 func (u *%[1]s) Set%[2]s(a %[3]s) {
423 copy(u.%[4]s[:], a[:])
425 func (u *%[1]s) Get%[2]s() (a %[3]s) {
426 copy(a[:], u.%[4]s[:])
429 `, structName, getterField, getterStruct, unionDataField)
432 func generateUnion(ctx *GenFile, w io.Writer, union *Union) {
435 logf(" writing UNION %q (%s) with %d fields", union.Name, name, len(union.Fields))
437 // generate struct comment
438 generateComment(ctx, w, name, union.Name, "union")
440 // generate struct definition
441 fmt.Fprintln(w, "type", name, "struct {")
443 // maximum size for union
444 maxSize := getUnionSize(ctx.file, union)
446 // generate data field
447 fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
449 // generate end of the struct
452 // generate name getter
453 generateTypeNameGetter(w, name, union.Name)
455 // generate getters for fields
456 for _, field := range union.Fields {
457 fieldType := convertToGoType(ctx.file, field.Type)
458 generateUnionGetterSetter(w, name, field.GoName, fieldType)
461 // generate union methods
462 //generateUnionMethods(w, name)
467 func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
469 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
473 func (u *%[1]s) Set%[2]s(a %[3]s) {
474 var b = new(bytes.Buffer)
475 if err := struc.Pack(b, &a); err != nil {
478 copy(u.%[4]s[:], b.Bytes())
480 func (u *%[1]s) Get%[2]s() (a %[3]s) {
481 var b = bytes.NewReader(u.%[4]s[:])
485 `, structName, getterField, getterStruct, unionDataField)
488 func generateMessage(ctx *GenFile, w io.Writer, msg *Message) {
491 logf(" writing MESSAGE %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
493 // generate struct comment
494 generateComment(ctx, w, name, msg.Name, "message")
496 // generate struct definition
497 fmt.Fprintf(w, "type %s struct {", name)
499 msgType := otherMessage
500 wasClientIndex := false
502 // generate struct fields
504 for i, field := range msg.Fields {
506 if field.Name == clientIndexField {
507 // "client_index" as the second member,
508 // this might be an event message or a request
509 msgType = eventMessage
510 wasClientIndex = true
511 } else if field.Name == contextField {
512 // reply needs "context" as the second member
513 msgType = replyMessage
516 if wasClientIndex && field.Name == contextField {
517 // request needs "client_index" as the second member
518 // and "context" as the third member
519 msgType = requestMessage
523 // skip internal fields
524 switch strings.ToLower(field.Name) {
525 case /*crcField,*/ msgIdField:
527 case clientIndexField, contextField:
537 generateField(ctx, w, msg.Fields, i)
540 // generate end of the struct
543 // generate message methods
544 generateMessageResetMethod(w, name)
545 generateMessageNameGetter(w, name, msg.Name)
546 generateCrcGetter(w, name, msg.CRC)
547 generateMessageTypeGetter(w, name, msgType)
548 generateMessageSize(ctx, w, name, msg.Fields)
549 generateMessageMarshal(ctx, w, name, msg.Fields)
550 generateMessageUnmarshal(ctx, w, name, msg.Fields)
555 func generateMessageSize(ctx *GenFile, w io.Writer, name string, fields []*Field) {
556 fmt.Fprintf(w, "func (m *%[1]s) Size() int {\n", name)
558 fmt.Fprintf(w, "\tif m == nil { return 0 }\n")
559 fmt.Fprintf(w, "\tvar size int\n")
561 encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
562 t, ok := BaseTypeNames[typ]
567 var s = BaseTypeSizes[t]
572 fmt.Fprintf(w, "\tsize += %d\n", s)
575 fmt.Fprintf(w, "\tsize += %d + len(%s)\n", s, name)
579 //fmt.Fprintf(w, "\tsize += %d * int(%s)\n", s, sizefrom)
580 fmt.Fprintf(w, "\tsize += %d * len(%s)\n", s, name)
583 s = BaseTypeSizes[t] * length
585 fmt.Fprintf(w, "\tsize += %d\n", s)
593 var encodeFields func(fields []*Field, parentName string)
594 encodeFields = func(fields []*Field, parentName string) {
596 defer func() { lvl-- }()
599 for _, field := range fields {
600 // skip internal fields
601 switch strings.ToLower(field.Name) {
602 case /*crcField,*/ msgIdField:
604 case clientIndexField, contextField:
611 fieldName := field.GoName //camelCaseName(strings.TrimPrefix(field.Name, "_"))
612 name := fmt.Sprintf("%s.%s", parentName, fieldName)
613 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
614 var sizeFromName string
616 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
619 fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
621 if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
625 char := fmt.Sprintf("s%d", lvl)
626 index := fmt.Sprintf("j%d", lvl)
629 if field.Length > 0 {
630 fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
631 } else if field.SizeFrom != "" {
632 //fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
633 fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
636 fmt.Fprintf(w, "\tvar %[1]s %[2]s\n_ = %[1]s\n", char, convertToGoType(ctx.file, field.Type))
637 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
641 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
642 if encodeBaseType(enum.Type, name, 0, "") {
644 fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
646 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
647 if encodeBaseType(alias.Type, name, alias.Length, "") {
648 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
649 encodeFields(typ.Fields, name)
651 fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
653 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
654 encodeFields(typ.Fields, name)
655 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
656 maxSize := getUnionSize(ctx.file, union)
657 fmt.Fprintf(w, "\tsize += %d\n", maxSize)
659 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
663 fmt.Fprintf(w, "\t}\n")
668 encodeFields(fields, "m")
670 fmt.Fprintf(w, "return size\n")
672 fmt.Fprintf(w, "}\n")
675 func generateMessageMarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
676 fmt.Fprintf(w, "func (m *%[1]s) Marshal(b []byte) ([]byte, error) {\n", name)
678 fmt.Fprintf(w, "\to := binary.BigEndian\n")
679 fmt.Fprintf(w, "\t_ = o\n")
680 fmt.Fprintf(w, "\tpos := 0\n")
681 fmt.Fprintf(w, "\t_ = pos\n")
683 var buf = new(strings.Builder)
685 encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
686 t, ok := BaseTypeNames[typ]
691 isArray := length > 0 || sizefrom != ""
694 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
697 fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", length)
698 } else if sizefrom != "" {
699 //fmt.Fprintf(buf, "\tfor i := 0; i < int(%s); i++ {\n", sizefrom)
700 fmt.Fprintf(buf, "\tfor i := 0; i < len(%s); i++ {\n", name)
708 fmt.Fprintf(buf, "\tvar x uint8\n")
709 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint8(%s[i]) }\n", name, name)
712 fmt.Fprintf(buf, "\tbuf[pos] = uint8(%s)\n", name)
713 fmt.Fprintf(buf, "\tpos += 1\n")
715 fmt.Fprintf(buf, "\t}\n")
719 fmt.Fprintf(buf, "\tvar x uint16\n")
720 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint16(%s[i]) }\n", name, name)
723 fmt.Fprintf(buf, "\to.PutUint16(buf[pos:pos+2], uint16(%s))\n", name)
724 fmt.Fprintf(buf, "\tpos += 2\n")
726 fmt.Fprintf(buf, "\t}\n")
730 fmt.Fprintf(buf, "\tvar x uint32\n")
731 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint32(%s[i]) }\n", name, name)
734 fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(%s))\n", name)
735 fmt.Fprintf(buf, "\tpos += 4\n")
737 fmt.Fprintf(buf, "\t}\n")
741 fmt.Fprintf(buf, "\tvar x uint64\n")
742 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint64(%s[i]) }\n", name, name)
745 fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], uint64(%s))\n", name)
746 fmt.Fprintf(buf, "\tpos += 8\n")
748 fmt.Fprintf(buf, "\t}\n")
752 fmt.Fprintf(buf, "\tvar x float64\n")
753 fmt.Fprintf(buf, "\tif i < len(%s) { x = float64(%s[i]) }\n", name, name)
756 fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], math.Float64bits(float64(%s)))\n", name)
757 fmt.Fprintf(buf, "\tpos += 8\n")
759 fmt.Fprintf(buf, "\t}\n")
762 fmt.Fprintf(buf, "\tif %s { buf[pos] = 1 }\n", name)
763 fmt.Fprintf(buf, "\tpos += 1\n")
766 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s)\n", length, name)
767 fmt.Fprintf(buf, "\tpos += %d\n", length)
769 fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(len(%s)))\n", name)
770 fmt.Fprintf(buf, "\tpos += 4\n")
771 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+len(%s)], %s[:])\n", name, name)
772 fmt.Fprintf(buf, "\tpos += len(%s)\n", name)
775 fmt.Fprintf(buf, "\t// ??? %s %s\n", name, typ)
782 var encodeFields func(fields []*Field, parentName string)
783 encodeFields = func(fields []*Field, parentName string) {
785 defer func() { lvl-- }()
788 for _, field := range fields {
789 // skip internal fields
790 switch strings.ToLower(field.Name) {
791 case /*crcField,*/ msgIdField:
793 case clientIndexField, contextField:
800 getFieldName := func(name string) string {
801 fieldName := camelCaseName(strings.TrimPrefix(name, "_"))
802 return fmt.Sprintf("%s.%s", parentName, fieldName)
805 fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
806 name := fmt.Sprintf("%s.%s", parentName, fieldName)
807 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
808 var sizeFromName string
810 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
813 fmt.Fprintf(buf, "\t// field[%d] %s\n", lvl, name)
815 getSizeOfField := func() *Field {
816 for _, f := range fields {
817 if f.SizeFrom == field.Name {
823 if f := getSizeOfField(); f != nil {
824 if encodeBaseType(field.Type, fmt.Sprintf("len(%s)", getFieldName(f.Name)), field.Length, "") {
827 panic(fmt.Sprintf("failed to encode base type of sizefrom field: %s", field.Name))
830 if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
834 char := fmt.Sprintf("v%d", lvl)
835 index := fmt.Sprintf("j%d", lvl)
838 if field.Length > 0 {
839 fmt.Fprintf(buf, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
840 } else if field.SizeFrom != "" {
841 //fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
842 fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
845 fmt.Fprintf(buf, "\tvar %s %s\n", char, convertToGoType(ctx.file, field.Type))
846 fmt.Fprintf(buf, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
850 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
851 if encodeBaseType(enum.Type, name, 0, "") {
853 fmt.Fprintf(buf, "\t// ??? ENUM %s %s\n", name, enum.Type)
855 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
856 if encodeBaseType(alias.Type, name, alias.Length, "") {
857 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
858 encodeFields(typ.Fields, name)
860 fmt.Fprintf(buf, "\t// ??? ALIAS %s %s\n", name, alias.Type)
862 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
863 encodeFields(typ.Fields, name)
864 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
865 maxSize := getUnionSize(ctx.file, union)
866 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s.%s[:])\n", maxSize, name, unionDataField)
867 fmt.Fprintf(buf, "\tpos += %d\n", maxSize)
869 fmt.Fprintf(buf, "\t// ??? buf[pos] = (%s)\n", name)
873 fmt.Fprintf(buf, "\t}\n")
878 encodeFields(fields, "m")
880 fmt.Fprintf(w, "\tvar buf []byte\n")
881 fmt.Fprintf(w, "\tif b == nil {\n")
882 fmt.Fprintf(w, "\tbuf = make([]byte, m.Size())\n")
883 fmt.Fprintf(w, "\t} else {\n")
884 fmt.Fprintf(w, "\tbuf = b\n")
885 fmt.Fprintf(w, "\t}\n")
886 fmt.Fprint(w, buf.String())
888 fmt.Fprintf(w, "return buf, nil\n")
890 fmt.Fprintf(w, "}\n")
893 func generateMessageUnmarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
894 fmt.Fprintf(w, "func (m *%[1]s) Unmarshal(tmp []byte) error {\n", name)
896 fmt.Fprintf(w, "\to := binary.BigEndian\n")
897 fmt.Fprintf(w, "\t_ = o\n")
898 fmt.Fprintf(w, "\tpos := 0\n")
899 fmt.Fprintf(w, "\t_ = pos\n")
901 decodeBaseType := func(typ, orig, name string, length int, sizefrom string, alloc bool) bool {
902 t, ok := BaseTypeNames[typ]
907 isArray := length > 0 || sizefrom != ""
910 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
914 fmt.Fprintf(w, "\t%s = make([]%s, %d)\n", name, orig, length)
915 } else if sizefrom != "" {
916 fmt.Fprintf(w, "\t%s = make([]%s, %s)\n", name, orig, sizefrom)
919 fmt.Fprintf(w, "\tfor i := 0; i < len(%s); i++ {\n", name)
926 fmt.Fprintf(w, "\t%s[i] = %s(tmp[pos])\n", name, convertToGoType(ctx.file, typ))
928 fmt.Fprintf(w, "\t%s = %s(tmp[pos])\n", name, orig)
930 fmt.Fprintf(w, "\tpos += 1\n")
932 fmt.Fprintf(w, "\t}\n")
936 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
938 fmt.Fprintf(w, "\t%s = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
940 fmt.Fprintf(w, "\tpos += 2\n")
942 fmt.Fprintf(w, "\t}\n")
946 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
948 fmt.Fprintf(w, "\t%s = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
950 fmt.Fprintf(w, "\tpos += 4\n")
952 fmt.Fprintf(w, "\t}\n")
956 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
958 fmt.Fprintf(w, "\t%s = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
960 fmt.Fprintf(w, "\tpos += 8\n")
962 fmt.Fprintf(w, "\t}\n")
966 fmt.Fprintf(w, "\t%s[i] = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
968 fmt.Fprintf(w, "\t%s = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
970 fmt.Fprintf(w, "\tpos += 8\n")
972 fmt.Fprintf(w, "\t}\n")
975 fmt.Fprintf(w, "\t%s = tmp[pos] != 0\n", name)
976 fmt.Fprintf(w, "\tpos += 1\n")
979 fmt.Fprintf(w, "\t{\n")
980 fmt.Fprintf(w, "\tnul := bytes.Index(tmp[pos:pos+%d], []byte{0x00})\n", length)
981 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+nul])\n", name)
982 fmt.Fprintf(w, "\tpos += %d\n", length)
983 fmt.Fprintf(w, "\t}\n")
985 fmt.Fprintf(w, "\t{\n")
986 fmt.Fprintf(w, "\tsiz := o.Uint32(tmp[pos:pos+4])\n")
987 fmt.Fprintf(w, "\tpos += 4\n")
988 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+int(siz)])\n", name)
989 fmt.Fprintf(w, "\tpos += len(%s)\n", name)
990 fmt.Fprintf(w, "\t}\n")
993 fmt.Fprintf(w, "\t// ??? %s %s\n", name, typ)
1000 var decodeFields func(fields []*Field, parentName string)
1001 decodeFields = func(fields []*Field, parentName string) {
1003 defer func() { lvl-- }()
1006 for _, field := range fields {
1007 // skip internal fields
1008 switch strings.ToLower(field.Name) {
1009 case /*crcField,*/ msgIdField:
1011 case clientIndexField, contextField:
1018 fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
1019 name := fmt.Sprintf("%s.%s", parentName, fieldName)
1020 sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
1021 var sizeFromName string
1023 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
1026 fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
1028 if decodeBaseType(field.Type, convertToGoType(ctx.file, field.Type), name, field.Length, sizeFromName, true) {
1032 //char := fmt.Sprintf("v%d", lvl)
1033 index := fmt.Sprintf("j%d", lvl)
1036 if field.Length > 0 {
1037 fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
1038 } else if field.SizeFrom != "" {
1039 fieldType := getFieldType(ctx, field)
1040 if strings.HasPrefix(fieldType, "[]") {
1041 fmt.Fprintf(w, "\t%s = make(%s, int(%s.%s))\n", name, fieldType, parentName, sizeFrom)
1043 fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
1046 /*fmt.Fprintf(w, "\tvar %s %s\n", char, convertToGoType(ctx, field.Type))
1047 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
1049 name = fmt.Sprintf("%s[%s]", name, index)
1052 if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
1053 if decodeBaseType(enum.Type, convertToGoType(ctx.file, field.Type), name, 0, "", false) {
1055 fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
1057 } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
1058 if decodeBaseType(alias.Type, convertToGoType(ctx.file, field.Type), name, alias.Length, "", false) {
1059 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
1060 decodeFields(typ.Fields, name)
1062 fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
1064 } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
1065 decodeFields(typ.Fields, name)
1066 } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
1067 maxSize := getUnionSize(ctx.file, union)
1068 fmt.Fprintf(w, "\tcopy(%s.%s[:], tmp[pos:pos+%d])\n", name, unionDataField, maxSize)
1069 fmt.Fprintf(w, "\tpos += %d\n", maxSize)
1071 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
1075 fmt.Fprintf(w, "\t}\n")
1080 decodeFields(fields, "m")
1082 fmt.Fprintf(w, "return nil\n")
1084 fmt.Fprintf(w, "}\n")
1087 func getFieldType(ctx *GenFile, field *Field) string {
1088 //fieldName := strings.TrimPrefix(field.Name, "_")
1089 //fieldName = camelCaseName(fieldName)
1090 //fieldName := field.GoName
1092 dataType := convertToGoType(ctx.file, field.Type)
1093 fieldType := dataType
1095 // check if it is array
1096 if field.Length > 0 || field.SizeFrom != "" {
1097 if dataType == "uint8" {
1100 if dataType == "string" && field.Array {
1101 fieldType = "string"
1103 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1104 fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1106 fieldType = "[]" + dataType
1113 func generateField(ctx *GenFile, w io.Writer, fields []*Field, i int) {
1116 //fieldName := strings.TrimPrefix(field.Name, "_")
1117 //fieldName = camelCaseName(fieldName)
1118 fieldName := field.GoName
1120 dataType := convertToGoType(ctx.file, field.Type)
1121 fieldType := dataType
1123 // generate length field for strings
1124 if field.Type == "string" && field.Length == 0 {
1125 fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName)
1128 // check if it is array
1129 if field.Length > 0 || field.SizeFrom != "" {
1130 if dataType == "uint8" {
1133 if dataType == "string" && field.Array {
1134 fieldType = "string"
1136 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1137 fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1139 fieldType = "[]" + dataType
1142 fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
1144 fieldTags := map[string]string{}
1146 if field.Length > 0 && field.Array {
1148 fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
1150 for _, f := range fields {
1151 if f.SizeFrom == field.Name {
1152 // variable sized array
1153 //sizeOfName := camelCaseName(f.Name)
1154 fieldTags["struc"] = fmt.Sprintf("sizeof=%s", f.GoName)
1159 if ctx.IncludeBinapiNames {
1160 typ := fromApiType(field.Type)
1162 if field.Length > 0 {
1163 fieldTags["binapi"] = fmt.Sprintf("%s[%d],name=%s", typ, field.Length, field.Name)
1164 } else if field.SizeFrom != "" {
1165 fieldTags["binapi"] = fmt.Sprintf("%s[%s],name=%s", typ, field.SizeFrom, field.Name)
1168 fieldTags["binapi"] = fmt.Sprintf("%s,name=%s", typ, field.Name)
1171 if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
1172 fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], limit)
1174 if def, ok := field.Meta["default"]; ok && def != nil {
1175 actual := getActualType(ctx.file, fieldType)
1176 if t, ok := binapiTypes[actual]; ok && t != "float64" {
1177 defnum := int(def.(float64))
1178 fieldTags["binapi"] = fmt.Sprintf("%s,default=%d", fieldTags["binapi"], defnum)
1180 fieldTags["binapi"] = fmt.Sprintf("%s,default=%v", fieldTags["binapi"], def)
1184 fieldTags["json"] = fmt.Sprintf("%s,omitempty", field.Name)
1186 if len(fieldTags) > 0 {
1187 fmt.Fprintf(w, "\t`")
1189 for k := range fieldTags {
1190 keys = append(keys, k)
1194 for _, tt := range keys {
1195 t, ok := fieldTags[tt]
1203 fmt.Fprintf(w, `%s:"%s"`, tt, t)
1211 func generateMessageResetMethod(w io.Writer, structName string) {
1212 fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
1215 func generateMessageNameGetter(w io.Writer, structName, msgName string) {
1216 fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName)
1219 func generateTypeNameGetter(w io.Writer, structName, msgName string) {
1220 fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
1223 func generateCrcGetter(w io.Writer, structName, crc string) {
1224 crc = strings.TrimPrefix(crc, "0x")
1225 fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
1228 func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
1229 fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
1230 if msgType == requestMessage {
1231 fmt.Fprintf(w, "\treturn api.RequestMessage")
1232 } else if msgType == replyMessage {
1233 fmt.Fprintf(w, "\treturn api.ReplyMessage")
1234 } else if msgType == eventMessage {
1235 fmt.Fprintf(w, "\treturn api.EventMessage")
1237 fmt.Fprintf(w, "\treturn api.OtherMessage")
1239 fmt.Fprintln(w, "}")