Introduce Stream - experimental API for low-level access to VPP API
[govpp.git] / binapigen / generate.go
1 //  Copyright (c) 2020 Cisco and/or its affiliates.
2 //
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:
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15 package binapigen
16
17 import (
18         "fmt"
19         "io"
20         "sort"
21         "strings"
22
23         "git.fd.io/govpp.git/version"
24         "github.com/sirupsen/logrus"
25 )
26
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
32
33 // common message fields
34 const (
35         msgIdField       = "_vl_msg_id"
36         clientIndexField = "client_index"
37         contextField     = "context"
38         retvalField      = "retval"
39 )
40
41 // global API info
42 const (
43         constModuleName = "ModuleName" // module name constant
44         constAPIVersion = "APIVersion" // API version constant
45         constVersionCrc = "VersionCrc" // version CRC constant
46 )
47
48 // generated fiels
49 const (
50         unionDataField = "XXX_UnionData" // name for the union data field
51 )
52
53 // MessageType represents the type of a VPP message
54 type MessageType int
55
56 const (
57         requestMessage MessageType = iota // VPP request message
58         replyMessage                      // VPP reply message
59         eventMessage                      // VPP event message
60         otherMessage                      // other VPP message
61 )
62
63 func generateFileBinapi(ctx *GenFile, w io.Writer) {
64         logf("----------------------------")
65         logf("generating BINAPI file package: %q", ctx.file.PackageName)
66         logf("----------------------------")
67
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)
74         }
75         fmt.Fprintf(w, "// source: %s\n", ctx.file.Path)
76         fmt.Fprintln(w)
77
78         generatePackageHeader(ctx, w)
79         generateImports(ctx, w)
80
81         generateApiInfo(ctx, w)
82         generateTypes(ctx, w)
83         generateMessages(ctx, w)
84
85         generateImportRefs(ctx, w)
86 }
87
88 func generatePackageHeader(ctx *GenFile, w io.Writer) {
89         fmt.Fprintln(w, "/*")
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())
92         fmt.Fprintln(w)
93         fmt.Fprintln(w, "It consists of:")
94         printObjNum := func(obj string, num int) {
95                 if num > 0 {
96                         if num > 1 {
97                                 if strings.HasSuffix(obj, "s") {
98                                         obj += "es"
99                                 } else {
100                                         obj += "s"
101                                 }
102                         }
103                         fmt.Fprintf(w, "\t%3d %s\n", num, obj)
104                 }
105         }
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)
113         fmt.Fprintln(w)
114 }
115
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"`)
124         fmt.Fprintln(w)
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 {
130                 fmt.Fprintln(w)
131                 for imp, importPath := range imports {
132                         fmt.Fprintf(w, "\t%s \"%s\"\n", imp, importPath)
133                 }
134         }
135         fmt.Fprintln(w, ")")
136         fmt.Fprintln(w)
137
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)
143         fmt.Fprintln(w)
144 }
145
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)
151
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)
157         }
158         fmt.Fprintln(w, ")")
159         fmt.Fprintln(w)
160 }
161
162 func generateTypes(ctx *GenFile, w io.Writer) {
163         // generate enums
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)
168                                 continue
169                         }
170                         generateEnum(ctx, w, enum)
171                 }
172         }
173
174         // generate aliases
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)
179                                 continue
180                         }
181                         generateAlias(ctx, w, alias)
182                 }
183         }
184
185         // generate types
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)
190                                 continue
191                         }
192                         generateStruct(ctx, w, typ)
193                 }
194         }
195
196         // generate unions
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)
201                                 continue
202                         }
203                         generateUnion(ctx, w, union)
204                 }
205         }
206 }
207
208 func generateMessages(ctx *GenFile, w io.Writer) {
209         if len(ctx.file.Messages) == 0 {
210                 return
211         }
212
213         for _, msg := range ctx.file.Messages {
214                 generateMessage(ctx, w, msg)
215         }
216
217         // generate message registrations
218         initFnName := fmt.Sprintf("file_%s_binapi_init", ctx.file.PackageName)
219
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)
225         }
226         fmt.Fprintln(w, "}")
227         fmt.Fprintln(w)
228
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)
235         }
236         fmt.Fprintln(w, "}")
237         fmt.Fprintln(w, "}")
238 }
239
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")
251 }
252
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)
256         } else {
257                 fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
258         }
259 }
260
261 func generateEnum(ctx *GenFile, w io.Writer, enum *Enum) {
262         name := enum.GoName
263         typ := binapiTypes[enum.Type]
264
265         logf(" writing ENUM %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
266
267         // generate enum comment
268         generateComment(ctx, w, name, enum.Name, "enum")
269
270         // generate enum definition
271         fmt.Fprintf(w, "type %s %s\n", name, typ)
272         fmt.Fprintln(w)
273
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)
278         }
279         fmt.Fprintln(w, ")")
280         fmt.Fprintln(w)
281
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)
287         }
288         fmt.Fprintln(w, "}")
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)
292         }
293         fmt.Fprintln(w, "}")
294         fmt.Fprintln(w, ")")
295         fmt.Fprintln(w)
296
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)
301         fmt.Fprintln(w, "}")
302         fmt.Fprintln(w)
303 }
304
305 func generateImportedAlias(ctx *GenFile, w io.Writer, name string, imp string) {
306         fmt.Fprintf(w, "type %s = %s.%s\n", name, imp, name)
307         fmt.Fprintln(w)
308 }
309
310 func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
311         name := alias.GoName
312
313         logf(" writing ALIAS %q (%s), length: %d", alias.Name, name, alias.Length)
314
315         // generate struct comment
316         generateComment(ctx, w, name, alias.Name, "alias")
317
318         // generate struct definition
319         fmt.Fprintf(w, "type %s ", name)
320
321         if alias.Length > 0 {
322                 fmt.Fprintf(w, "[%d]", alias.Length)
323         }
324
325         dataType := convertToGoType(ctx.file, alias.Type)
326         fmt.Fprintf(w, "%s\n", dataType)
327
328         fmt.Fprintln(w)
329 }
330
331 func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
332         name := typ.GoName
333
334         logf(" writing STRUCT %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
335
336         // generate struct comment
337         generateComment(ctx, w, name, typ.Name, "type")
338
339         // generate struct definition
340         fmt.Fprintf(w, "type %s struct {\n", name)
341
342         // generate struct fields
343         for i := range typ.Fields {
344                 // skip internal fields
345                 switch strings.ToLower(typ.Name) {
346                 case msgIdField:
347                         continue
348                 }
349
350                 generateField(ctx, w, typ.Fields, i)
351         }
352
353         // generate end of the struct
354         fmt.Fprintln(w, "}")
355
356         // generate name getter
357         generateTypeNameGetter(w, name, typ.Name)
358
359         fmt.Fprintln(w)
360 }
361
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
367         fmt.Fprintf(w, `
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 {
371                 return 0, err
372         }
373         copy(p, b.Bytes())
374         return b.Len(), nil
375 }
376 func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
377         return struc.UnpackWithOptions(r, u.union_data[:], opt)
378 }
379 func (u *%[1]s) Size(opt *struc.Options) int {
380         return len(u.union_data)
381 }
382 func (u *%[1]s) String() string {
383         return string(u.union_data[:])
384 }
385 `, structName)
386 }*/
387
388 /*func generateUnionGetterSetterNew(w io.Writer, structName string, getterField, getterStruct string) {
389         fmt.Fprintf(w, `
390 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
391         u.Set%[2]s(a)
392         return
393 }
394 func (u *%[1]s) Set%[2]s(a %[3]s) {
395         copy(u.%[4]s[:], a[:])
396 }
397 func (u *%[1]s) Get%[2]s() (a %[3]s) {
398         copy(a[:], u.%[4]s[:])
399         return
400 }
401 `, structName, getterField, getterStruct, unionDataField)
402 }*/
403
404 func generateUnion(ctx *GenFile, w io.Writer, union *Union) {
405         name := union.GoName
406
407         logf(" writing UNION %q (%s) with %d fields", union.Name, name, len(union.Fields))
408
409         // generate struct comment
410         generateComment(ctx, w, name, union.Name, "union")
411
412         // generate struct definition
413         fmt.Fprintln(w, "type", name, "struct {")
414
415         // maximum size for union
416         maxSize := getUnionSize(ctx.file, union)
417
418         // generate data field
419         fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
420
421         // generate end of the struct
422         fmt.Fprintln(w, "}")
423
424         // generate name getter
425         generateTypeNameGetter(w, name, union.Name)
426
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)
431         }
432
433         // generate union methods
434         //generateUnionMethods(w, name)
435
436         fmt.Fprintln(w)
437 }
438
439 func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
440         fmt.Fprintf(w, `
441 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
442         u.Set%[2]s(a)
443         return
444 }
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 {
448                 return
449         }
450         copy(u.%[4]s[:], b.Bytes())
451 }
452 func (u *%[1]s) Get%[2]s() (a %[3]s) {
453         var b = bytes.NewReader(u.%[4]s[:])
454         struc.Unpack(b, &a)
455         return
456 }
457 `, structName, getterField, getterStruct, unionDataField)
458 }
459
460 func generateMessage(ctx *GenFile, w io.Writer, msg *Message) {
461         name := msg.GoName
462
463         logf(" writing MESSAGE %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
464
465         // generate struct comment
466         generateComment(ctx, w, name, msg.Name, "message")
467
468         // generate struct definition
469         fmt.Fprintf(w, "type %s struct {", name)
470
471         msgType := otherMessage
472         wasClientIndex := false
473
474         // generate struct fields
475         n := 0
476         for i, field := range msg.Fields {
477                 if i == 1 {
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
486                         }
487                 } else if i == 2 {
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
492                         }
493                 }
494
495                 // skip internal fields
496                 switch strings.ToLower(field.Name) {
497                 case msgIdField:
498                         continue
499                 case clientIndexField, contextField:
500                         if n == 0 {
501                                 continue
502                         }
503                 }
504                 n++
505                 if n == 1 {
506                         fmt.Fprintln(w)
507                 }
508
509                 generateField(ctx, w, msg.Fields, i)
510         }
511
512         // generate end of the struct
513         fmt.Fprintln(w, "}")
514
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)
523
524         fmt.Fprintln(w)
525 }
526
527 func generateMessageSize(ctx *GenFile, w io.Writer, name string, fields []*Field) {
528         fmt.Fprintf(w, "func (m *%[1]s) Size() int {\n", name)
529
530         fmt.Fprintf(w, "\tif m == nil { return 0 }\n")
531         fmt.Fprintf(w, "\tvar size int\n")
532
533         encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
534                 t, ok := BaseTypeNames[typ]
535                 if !ok {
536                         return false
537                 }
538
539                 var s = BaseTypeSizes[t]
540                 switch t {
541                 case STRING:
542                         if length > 0 {
543                                 s = length
544                                 fmt.Fprintf(w, "\tsize += %d\n", s)
545                         } else {
546                                 s = 4
547                                 fmt.Fprintf(w, "\tsize += %d + len(%s)\n", s, name)
548                         }
549                 default:
550                         if sizefrom != "" {
551                                 //fmt.Fprintf(w, "\tsize += %d * int(%s)\n", s, sizefrom)
552                                 fmt.Fprintf(w, "\tsize += %d * len(%s)\n", s, name)
553                         } else {
554                                 if length > 0 {
555                                         s = BaseTypeSizes[t] * length
556                                 }
557                                 fmt.Fprintf(w, "\tsize += %d\n", s)
558                         }
559                 }
560
561                 return true
562         }
563
564         lvl := 0
565         var sizeFields func(fields []*Field, parentName string)
566         sizeFields = func(fields []*Field, parentName string) {
567                 lvl++
568                 defer func() { lvl-- }()
569
570                 n := 0
571                 for _, field := range fields {
572                         if field.ParentMessage != nil {
573                                 // skip internal fields
574                                 switch strings.ToLower(field.Name) {
575                                 case msgIdField:
576                                         continue
577                                 case clientIndexField, contextField:
578                                         if n == 0 {
579                                                 continue
580                                         }
581                                 }
582                         }
583                         n++
584
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
589                         if sizeFrom != "" {
590                                 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
591                         }
592
593                         fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
594
595                         if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
596                                 continue
597                         }
598
599                         char := fmt.Sprintf("s%d", lvl)
600                         index := fmt.Sprintf("j%d", lvl)
601
602                         if field.Array {
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)
608                                 }
609
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)
612                                 name = char
613                         }
614
615                         if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
616                                 if encodeBaseType(enum.Type, name, 0, "") {
617                                 } else {
618                                         fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
619                                 }
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)
624                                 } else {
625                                         fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
626                                 }
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)
632                         } else {
633                                 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
634                         }
635
636                         if field.Array {
637                                 fmt.Fprintf(w, "\t}\n")
638                         }
639                 }
640         }
641
642         sizeFields(fields, "m")
643
644         fmt.Fprintf(w, "return size\n")
645
646         fmt.Fprintf(w, "}\n")
647 }
648
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)
651
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")
656
657         var buf = new(strings.Builder)
658
659         encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
660                 t, ok := BaseTypeNames[typ]
661                 if !ok {
662                         return false
663                 }
664
665                 isArray := length > 0 || sizefrom != ""
666
667                 switch t {
668                 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
669                         if isArray {
670                                 if length != 0 {
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)
675                                 }
676                         }
677                 }
678
679                 switch t {
680                 case I8, U8:
681                         if isArray {
682                                 fmt.Fprintf(buf, "\tvar x uint8\n")
683                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint8(%s[i]) }\n", name, name)
684                                 name = "x"
685                         }
686                         fmt.Fprintf(buf, "\tbuf[pos] = uint8(%s)\n", name)
687                         fmt.Fprintf(buf, "\tpos += 1\n")
688                         if isArray {
689                                 fmt.Fprintf(buf, "\t}\n")
690                         }
691                 case I16, U16:
692                         if isArray {
693                                 fmt.Fprintf(buf, "\tvar x uint16\n")
694                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint16(%s[i]) }\n", name, name)
695                                 name = "x"
696                         }
697                         fmt.Fprintf(buf, "\to.PutUint16(buf[pos:pos+2], uint16(%s))\n", name)
698                         fmt.Fprintf(buf, "\tpos += 2\n")
699                         if isArray {
700                                 fmt.Fprintf(buf, "\t}\n")
701                         }
702                 case I32, U32:
703                         if isArray {
704                                 fmt.Fprintf(buf, "\tvar x uint32\n")
705                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint32(%s[i]) }\n", name, name)
706                                 name = "x"
707                         }
708                         fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(%s))\n", name)
709                         fmt.Fprintf(buf, "\tpos += 4\n")
710                         if isArray {
711                                 fmt.Fprintf(buf, "\t}\n")
712                         }
713                 case I64, U64:
714                         if isArray {
715                                 fmt.Fprintf(buf, "\tvar x uint64\n")
716                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint64(%s[i]) }\n", name, name)
717                                 name = "x"
718                         }
719                         fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], uint64(%s))\n", name)
720                         fmt.Fprintf(buf, "\tpos += 8\n")
721                         if isArray {
722                                 fmt.Fprintf(buf, "\t}\n")
723                         }
724                 case F64:
725                         if isArray {
726                                 fmt.Fprintf(buf, "\tvar x float64\n")
727                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = float64(%s[i]) }\n", name, name)
728                                 name = "x"
729                         }
730                         fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], math.Float64bits(float64(%s)))\n", name)
731                         fmt.Fprintf(buf, "\tpos += 8\n")
732                         if isArray {
733                                 fmt.Fprintf(buf, "\t}\n")
734                         }
735                 case BOOL:
736                         fmt.Fprintf(buf, "\tif %s { buf[pos] = 1 }\n", name)
737                         fmt.Fprintf(buf, "\tpos += 1\n")
738                 case STRING:
739                         if length != 0 {
740                                 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s)\n", length, name)
741                                 fmt.Fprintf(buf, "\tpos += %d\n", length)
742                         } else {
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)
747                         }
748                 default:
749                         fmt.Fprintf(buf, "\t// ??? %s %s\n", name, typ)
750                         return false
751                 }
752                 return true
753         }
754
755         lvl := 0
756         var encodeFields func(fields []*Field, parentName string)
757         encodeFields = func(fields []*Field, parentName string) {
758                 lvl++
759                 defer func() { lvl-- }()
760
761                 n := 0
762                 for _, field := range fields {
763                         if field.ParentMessage != nil {
764                                 // skip internal fields
765                                 switch strings.ToLower(field.Name) {
766                                 case msgIdField:
767                                         continue
768                                 case clientIndexField, contextField:
769                                         if n == 0 {
770                                                 continue
771                                         }
772                                 }
773                         }
774                         n++
775
776                         getFieldName := func(name string) string {
777                                 fieldName := camelCaseName(strings.TrimPrefix(name, "_"))
778                                 return fmt.Sprintf("%s.%s", parentName, fieldName)
779                         }
780
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
785                         if sizeFrom != "" {
786                                 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
787                         }
788
789                         fmt.Fprintf(buf, "\t// field[%d] %s\n", lvl, name)
790
791                         getSizeOfField := func() *Field {
792                                 for _, f := range fields {
793                                         if f.SizeFrom == field.Name {
794                                                 return f
795                                         }
796                                 }
797                                 return nil
798                         }
799                         if f := getSizeOfField(); f != nil {
800                                 if encodeBaseType(field.Type, fmt.Sprintf("len(%s)", getFieldName(f.Name)), field.Length, "") {
801                                         continue
802                                 }
803                                 panic(fmt.Sprintf("failed to encode base type of sizefrom field: %s", field.Name))
804                         }
805
806                         if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
807                                 continue
808                         }
809
810                         char := fmt.Sprintf("v%d", lvl)
811                         index := fmt.Sprintf("j%d", lvl)
812
813                         if field.Array {
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)
819                                 }
820
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)
823                                 name = char
824                         }
825
826                         if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
827                                 if encodeBaseType(enum.Type, name, 0, "") {
828                                 } else {
829                                         fmt.Fprintf(buf, "\t// ??? ENUM %s %s\n", name, enum.Type)
830                                 }
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)
835                                 } else {
836                                         fmt.Fprintf(buf, "\t// ??? ALIAS %s %s\n", name, alias.Type)
837                                 }
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)
844                         } else {
845                                 fmt.Fprintf(buf, "\t// ??? buf[pos] = (%s)\n", name)
846                         }
847
848                         if field.Array {
849                                 fmt.Fprintf(buf, "\t}\n")
850                         }
851                 }
852         }
853
854         encodeFields(fields, "m")
855
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())
863
864         fmt.Fprintf(w, "return buf, nil\n")
865
866         fmt.Fprintf(w, "}\n")
867 }
868
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)
871
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")
876
877         decodeBaseType := func(typ, orig, name string, length int, sizefrom string, alloc bool) bool {
878                 t, ok := BaseTypeNames[typ]
879                 if !ok {
880                         return false
881                 }
882
883                 isArray := length > 0 || sizefrom != ""
884
885                 switch t {
886                 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
887                         if isArray {
888                                 if alloc {
889                                         if length != 0 {
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)
893                                         }
894                                 }
895                                 fmt.Fprintf(w, "\tfor i := 0; i < len(%s); i++ {\n", name)
896                         }
897                 }
898
899                 switch t {
900                 case I8, U8:
901                         if isArray {
902                                 fmt.Fprintf(w, "\t%s[i] = %s(tmp[pos])\n", name, convertToGoType(ctx.file, typ))
903                         } else {
904                                 fmt.Fprintf(w, "\t%s = %s(tmp[pos])\n", name, orig)
905                         }
906                         fmt.Fprintf(w, "\tpos += 1\n")
907                         if isArray {
908                                 fmt.Fprintf(w, "\t}\n")
909                         }
910                 case I16, U16:
911                         if isArray {
912                                 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
913                         } else {
914                                 fmt.Fprintf(w, "\t%s = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
915                         }
916                         fmt.Fprintf(w, "\tpos += 2\n")
917                         if isArray {
918                                 fmt.Fprintf(w, "\t}\n")
919                         }
920                 case I32, U32:
921                         if isArray {
922                                 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
923                         } else {
924                                 fmt.Fprintf(w, "\t%s = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
925                         }
926                         fmt.Fprintf(w, "\tpos += 4\n")
927                         if isArray {
928                                 fmt.Fprintf(w, "\t}\n")
929                         }
930                 case I64, U64:
931                         if isArray {
932                                 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
933                         } else {
934                                 fmt.Fprintf(w, "\t%s = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
935                         }
936                         fmt.Fprintf(w, "\tpos += 8\n")
937                         if isArray {
938                                 fmt.Fprintf(w, "\t}\n")
939                         }
940                 case F64:
941                         if isArray {
942                                 fmt.Fprintf(w, "\t%s[i] = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
943                         } else {
944                                 fmt.Fprintf(w, "\t%s = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
945                         }
946                         fmt.Fprintf(w, "\tpos += 8\n")
947                         if isArray {
948                                 fmt.Fprintf(w, "\t}\n")
949                         }
950                 case BOOL:
951                         fmt.Fprintf(w, "\t%s = tmp[pos] != 0\n", name)
952                         fmt.Fprintf(w, "\tpos += 1\n")
953                 case STRING:
954                         if length != 0 {
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")
960                         } else {
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")
967                         }
968                 default:
969                         fmt.Fprintf(w, "\t// ??? %s %s\n", name, typ)
970                         return false
971                 }
972                 return true
973         }
974
975         lvl := 0
976         var decodeFields func(fields []*Field, parentName string)
977         decodeFields = func(fields []*Field, parentName string) {
978                 lvl++
979                 defer func() { lvl-- }()
980
981                 n := 0
982                 for _, field := range fields {
983                         if field.ParentMessage != nil {
984                                 // skip internal fields
985                                 switch strings.ToLower(field.Name) {
986                                 case msgIdField:
987                                         continue
988                                 case clientIndexField, contextField:
989                                         if n == 0 {
990                                                 continue
991                                         }
992                                 }
993                         }
994                         n++
995
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
1000                         if sizeFrom != "" {
1001                                 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
1002                         }
1003
1004                         fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
1005
1006                         if decodeBaseType(field.Type, convertToGoType(ctx.file, field.Type), name, field.Length, sizeFromName, true) {
1007                                 continue
1008                         }
1009
1010                         //char := fmt.Sprintf("v%d", lvl)
1011                         index := fmt.Sprintf("j%d", lvl)
1012
1013                         if field.Array {
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)
1020                                         }
1021                                         fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
1022                                 }
1023
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)
1026                                 name = char*/
1027                                 name = fmt.Sprintf("%s[%s]", name, index)
1028                         }
1029
1030                         if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
1031                                 if decodeBaseType(enum.Type, convertToGoType(ctx.file, field.Type), name, 0, "", false) {
1032                                 } else {
1033                                         fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
1034                                 }
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)
1039                                 } else {
1040                                         fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
1041                                 }
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)
1048                         } else {
1049                                 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
1050                         }
1051
1052                         if field.Array {
1053                                 fmt.Fprintf(w, "\t}\n")
1054                         }
1055                 }
1056         }
1057
1058         decodeFields(fields, "m")
1059
1060         fmt.Fprintf(w, "return nil\n")
1061
1062         fmt.Fprintf(w, "}\n")
1063 }
1064
1065 func getFieldType(ctx *GenFile, field *Field) string {
1066         //fieldName := strings.TrimPrefix(field.Name, "_")
1067         //fieldName = camelCaseName(fieldName)
1068         //fieldName := field.GoName
1069
1070         dataType := convertToGoType(ctx.file, field.Type)
1071         fieldType := dataType
1072
1073         // check if it is array
1074         if field.Length > 0 || field.SizeFrom != "" {
1075                 if dataType == "uint8" {
1076                         dataType = "byte"
1077                 }
1078                 if dataType == "string" && field.Array {
1079                         fieldType = "string"
1080                         dataType = "byte"
1081                 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1082                         fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1083                 } else {
1084                         fieldType = "[]" + dataType
1085                 }
1086         }
1087
1088         return fieldType
1089 }
1090
1091 func generateField(ctx *GenFile, w io.Writer, fields []*Field, i int) {
1092         field := fields[i]
1093
1094         //fieldName := strings.TrimPrefix(field.Name, "_")
1095         //fieldName = camelCaseName(fieldName)
1096         fieldName := field.GoName
1097
1098         dataType := convertToGoType(ctx.file, field.Type)
1099         fieldType := dataType
1100
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)
1104         }
1105
1106         // check if it is array
1107         if field.Length > 0 || field.SizeFrom != "" {
1108                 if dataType == "uint8" {
1109                         dataType = "byte"
1110                 }
1111                 if dataType == "string" && field.Array {
1112                         fieldType = "string"
1113                         dataType = "byte"
1114                 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1115                         fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1116                 } else {
1117                         fieldType = "[]" + dataType
1118                 }
1119         }
1120         fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
1121
1122         fieldTags := map[string]string{}
1123
1124         if field.Length > 0 && field.Array {
1125                 // fixed size array
1126                 fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
1127         } else {
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)
1133                         }
1134                 }
1135         }
1136
1137         if ctx.IncludeBinapiNames {
1138                 typ := fromApiType(field.Type)
1139                 if field.Array {
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)
1144                         }
1145                 } else {
1146                         fieldTags["binapi"] = fmt.Sprintf("%s,name=%s", typ, field.Name)
1147                 }
1148         }
1149         if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
1150                 fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], limit)
1151         }
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)
1157                 } else {
1158                         fieldTags["binapi"] = fmt.Sprintf("%s,default=%v", fieldTags["binapi"], def)
1159                 }
1160         }
1161
1162         fieldTags["json"] = fmt.Sprintf("%s,omitempty", field.Name)
1163
1164         if len(fieldTags) > 0 {
1165                 fmt.Fprintf(w, "\t`")
1166                 var keys []string
1167                 for k := range fieldTags {
1168                         keys = append(keys, k)
1169                 }
1170                 sort.Strings(keys)
1171                 var n int
1172                 for _, tt := range keys {
1173                         t, ok := fieldTags[tt]
1174                         if !ok {
1175                                 continue
1176                         }
1177                         if n > 0 {
1178                                 fmt.Fprintf(w, " ")
1179                         }
1180                         n++
1181                         fmt.Fprintf(w, `%s:"%s"`, tt, t)
1182                 }
1183                 fmt.Fprintf(w, "`")
1184         }
1185
1186         fmt.Fprintln(w)
1187 }
1188
1189 func generateMessageResetMethod(w io.Writer, structName string) {
1190         fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
1191 }
1192
1193 func generateMessageNameGetter(w io.Writer, structName, msgName string) {
1194         fmt.Fprintf(w, "func (*%s) GetMessageName() string {    return %q }\n", structName, msgName)
1195 }
1196
1197 func generateTypeNameGetter(w io.Writer, structName, msgName string) {
1198         fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
1199 }
1200
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)
1204 }
1205
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")
1214         } else {
1215                 fmt.Fprintf(w, "\treturn api.OtherMessage")
1216         }
1217         fmt.Fprintln(w, "}")
1218         fmt.Fprintln(w)
1219 }
1220
1221 func logf(f string, v ...interface{}) {
1222         logrus.Debugf(f, v...)
1223 }