d35427ff1c0a9331f2b4527d8bbd785a719296b4
[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, `       "fmt"`)
122         fmt.Fprintln(w, `       "io"`)
123         fmt.Fprintln(w, `       "math"`)
124         fmt.Fprintln(w, `       "net"`)
125         fmt.Fprintln(w, `       "strconv"`)
126         fmt.Fprintln(w, `       "strings"`)
127         fmt.Fprintln(w)
128         fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
129         fmt.Fprintf(w, "\tcodec \"%s\"\n", "git.fd.io/govpp.git/codec")
130         fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
131         imports := listImports(ctx)
132         if len(imports) > 0 {
133                 fmt.Fprintln(w)
134                 for imp, importPath := range imports {
135                         fmt.Fprintf(w, "\t%s \"%s\"\n", imp, importPath)
136                 }
137         }
138         fmt.Fprintln(w, ")")
139         fmt.Fprintln(w)
140
141         fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
142         fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
143         fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the")
144         fmt.Fprintln(w, "// GoVPP api package needs to be updated.")
145         fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion)
146         fmt.Fprintln(w)
147 }
148
149 func generateApiInfo(ctx *GenFile, w io.Writer) {
150         // generate module desc
151         fmt.Fprintln(w, "const (")
152         fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName)
153         fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.file.Name)
154
155         if ctx.IncludeAPIVersion {
156                 fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion)
157                 fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.file.Version())
158                 fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc)
159                 fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.file.CRC)
160         }
161         fmt.Fprintln(w, ")")
162         fmt.Fprintln(w)
163 }
164
165 func generateTypes(ctx *GenFile, w io.Writer) {
166         // generate enums
167         if len(ctx.file.Enums) > 0 {
168                 for _, enum := range ctx.file.Enums {
169                         if imp, ok := ctx.file.imports[enum.Name]; ok {
170                                 if strings.HasSuffix(ctx.file.Name, "_types") {
171                                         generateImportedAlias(ctx, w, enum.GoName, imp)
172                                 }
173                                 continue
174                         }
175                         generateEnum(ctx, w, enum)
176                 }
177         }
178
179         // generate aliases
180         if len(ctx.file.Aliases) > 0 {
181                 for _, alias := range ctx.file.Aliases {
182                         if imp, ok := ctx.file.imports[alias.Name]; ok {
183                                 if strings.HasSuffix(ctx.file.Name, "_types") {
184                                         generateImportedAlias(ctx, w, alias.GoName, imp)
185                                 }
186                                 continue
187                         }
188                         generateAlias(ctx, w, alias)
189                 }
190         }
191
192         // generate types
193         if len(ctx.file.Structs) > 0 {
194                 for _, typ := range ctx.file.Structs {
195                         if imp, ok := ctx.file.imports[typ.Name]; ok {
196                                 if strings.HasSuffix(ctx.file.Name, "_types") {
197                                         generateImportedAlias(ctx, w, typ.GoName, imp)
198                                 }
199                                 continue
200                         }
201                         generateStruct(ctx, w, typ)
202                 }
203         }
204
205         // generate unions
206         if len(ctx.file.Unions) > 0 {
207                 for _, union := range ctx.file.Unions {
208                         if imp, ok := ctx.file.imports[union.Name]; ok {
209                                 if strings.HasSuffix(ctx.file.Name, "_types") {
210                                         generateImportedAlias(ctx, w, union.GoName, imp)
211                                 }
212                                 continue
213                         }
214                         generateUnion(ctx, w, union)
215                 }
216         }
217 }
218
219 func generateMessages(ctx *GenFile, w io.Writer) {
220         if len(ctx.file.Messages) == 0 {
221                 return
222         }
223
224         for _, msg := range ctx.file.Messages {
225                 generateMessage(ctx, w, msg)
226         }
227
228         // generate message registrations
229         initFnName := fmt.Sprintf("file_%s_binapi_init", ctx.file.PackageName)
230
231         fmt.Fprintf(w, "func init() { %s() }\n", initFnName)
232         fmt.Fprintf(w, "func %s() {\n", initFnName)
233         for _, msg := range ctx.file.Messages {
234                 fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n",
235                         msg.GoName, ctx.file.Name+"."+msg.GoName)
236         }
237         fmt.Fprintln(w, "}")
238         fmt.Fprintln(w)
239
240         // generate list of messages
241         fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n")
242         fmt.Fprintln(w, "func AllMessages() []api.Message {")
243         fmt.Fprintln(w, "\treturn []api.Message{")
244         for _, msg := range ctx.file.Messages {
245                 fmt.Fprintf(w, "\t(*%s)(nil),\n", msg.GoName)
246         }
247         fmt.Fprintln(w, "}")
248         fmt.Fprintln(w, "}")
249 }
250
251 func generateImportRefs(ctx *GenFile, w io.Writer) {
252         fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
253         fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
254         fmt.Fprintf(w, "var _ = codec.DecodeString\n")
255         fmt.Fprintf(w, "var _ = bytes.NewBuffer\n")
256         fmt.Fprintf(w, "var _ = context.Background\n")
257         fmt.Fprintf(w, "var _ = io.Copy\n")
258         fmt.Fprintf(w, "var _ = strconv.Itoa\n")
259         fmt.Fprintf(w, "var _ = strings.Contains\n")
260         fmt.Fprintf(w, "var _ = struc.Pack\n")
261         fmt.Fprintf(w, "var _ = binary.BigEndian\n")
262         fmt.Fprintf(w, "var _ = math.Float32bits\n")
263         fmt.Fprintf(w, "var _ = net.ParseIP\n")
264         fmt.Fprintf(w, "var _ = fmt.Errorf\n")
265 }
266
267 func generateComment(ctx *GenFile, w io.Writer, goName string, vppName string, objKind string) {
268         if objKind == "service" {
269                 fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.file.Name)
270         } else {
271                 fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName)
272         }
273 }
274
275 func generateEnum(ctx *GenFile, w io.Writer, enum *Enum) {
276         name := enum.GoName
277         typ := binapiTypes[enum.Type]
278
279         logf(" writing ENUM %q (%s) with %d entries", enum.Name, name, len(enum.Entries))
280
281         // generate enum comment
282         generateComment(ctx, w, name, enum.Name, "enum")
283
284         // generate enum definition
285         fmt.Fprintf(w, "type %s %s\n", name, typ)
286         fmt.Fprintln(w)
287
288         // generate enum entries
289         fmt.Fprintln(w, "const (")
290         for _, entry := range enum.Entries {
291                 fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value)
292         }
293         fmt.Fprintln(w, ")")
294         fmt.Fprintln(w)
295
296         // generate enum conversion maps
297         fmt.Fprintln(w, "var (")
298         fmt.Fprintf(w, "%s_name = map[%s]string{\n", name, typ)
299         for _, entry := range enum.Entries {
300                 fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name)
301         }
302         fmt.Fprintln(w, "}")
303         fmt.Fprintf(w, "%s_value = map[string]%s{\n", name, typ)
304         for _, entry := range enum.Entries {
305                 fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value)
306         }
307         fmt.Fprintln(w, "}")
308         fmt.Fprintln(w, ")")
309         fmt.Fprintln(w)
310
311         fmt.Fprintf(w, "func (x %s) String() string {\n", name)
312         fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ)
313         fmt.Fprintf(w, "\tif ok { return s }\n")
314         fmt.Fprintf(w, "\treturn \"%s(\" + strconv.Itoa(int(x)) + \")\"\n", name)
315         fmt.Fprintln(w, "}")
316         fmt.Fprintln(w)
317 }
318
319 func generateImportedAlias(ctx *GenFile, w io.Writer, name string, imp string) {
320         fmt.Fprintf(w, "type %s = %s.%s\n", name, imp, name)
321         fmt.Fprintln(w)
322 }
323
324 func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
325         name := alias.GoName
326
327         logf(" writing ALIAS %q (%s), length: %d", alias.Name, name, alias.Length)
328
329         // generate struct comment
330         generateComment(ctx, w, name, alias.Name, "alias")
331
332         // generate struct definition
333         fmt.Fprintf(w, "type %s ", name)
334
335         if alias.Length > 0 {
336                 fmt.Fprintf(w, "[%d]", alias.Length)
337         }
338
339         dataType := convertToGoType(ctx.file, alias.Type)
340         fmt.Fprintf(w, "%s\n", dataType)
341
342         // generate alias-specific methods
343         switch alias.Name {
344         case "mac_address":
345                 fmt.Fprintln(w)
346                 generateMacAddressConversion(w, name)
347         }
348
349         fmt.Fprintln(w)
350 }
351
352 func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
353         name := typ.GoName
354
355         logf(" writing STRUCT %q (%s) with %d fields", typ.Name, name, len(typ.Fields))
356
357         // generate struct comment
358         generateComment(ctx, w, name, typ.Name, "type")
359
360         // generate struct definition
361         fmt.Fprintf(w, "type %s struct {\n", name)
362
363         // generate struct fields
364         for i := range typ.Fields {
365                 // skip internal fields
366                 switch strings.ToLower(typ.Name) {
367                 case msgIdField:
368                         continue
369                 }
370
371                 generateField(ctx, w, typ.Fields, i)
372         }
373
374         // generate end of the struct
375         fmt.Fprintln(w, "}")
376
377         // generate name getter
378         generateTypeNameGetter(w, name, typ.Name)
379
380         // generate type-specific methods
381         switch typ.Name {
382         case "address":
383                 fmt.Fprintln(w)
384                 generateIPAddressConversion(w, name)
385         case "prefix":
386                 fmt.Fprintln(w)
387                 generatePrefixConversion(w, name)
388         }
389
390         fmt.Fprintln(w)
391 }
392
393 // generateUnionMethods generates methods that implement struc.Custom
394 // interface to allow having XXX_uniondata field unexported
395 // TODO: do more testing when unions are actually used in some messages
396 /*func generateUnionMethods(w io.Writer, structName string) {
397         // generate struc.Custom implementation for union
398         fmt.Fprintf(w, `
399 func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) {
400         var b = new(bytes.Buffer)
401         if err := struc.PackWithOptions(b, u.union_data, opt); err != nil {
402                 return 0, err
403         }
404         copy(p, b.Bytes())
405         return b.Len(), nil
406 }
407 func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error {
408         return struc.UnpackWithOptions(r, u.union_data[:], opt)
409 }
410 func (u *%[1]s) Size(opt *struc.Options) int {
411         return len(u.union_data)
412 }
413 func (u *%[1]s) String() string {
414         return string(u.union_data[:])
415 }
416 `, structName)
417 }*/
418
419 /*func generateUnionGetterSetterNew(w io.Writer, structName string, getterField, getterStruct string) {
420         fmt.Fprintf(w, `
421 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
422         u.Set%[2]s(a)
423         return
424 }
425 func (u *%[1]s) Set%[2]s(a %[3]s) {
426         copy(u.%[4]s[:], a[:])
427 }
428 func (u *%[1]s) Get%[2]s() (a %[3]s) {
429         copy(a[:], u.%[4]s[:])
430         return
431 }
432 `, structName, getterField, getterStruct, unionDataField)
433 }*/
434
435 func generateUnion(ctx *GenFile, w io.Writer, union *Union) {
436         name := union.GoName
437
438         logf(" writing UNION %q (%s) with %d fields", union.Name, name, len(union.Fields))
439
440         // generate struct comment
441         generateComment(ctx, w, name, union.Name, "union")
442
443         // generate struct definition
444         fmt.Fprintln(w, "type", name, "struct {")
445
446         // maximum size for union
447         maxSize := getUnionSize(ctx.file, union)
448
449         // generate data field
450         fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize)
451
452         // generate end of the struct
453         fmt.Fprintln(w, "}")
454
455         // generate name getter
456         generateTypeNameGetter(w, name, union.Name)
457
458         // generate getters for fields
459         for _, field := range union.Fields {
460                 fieldType := convertToGoType(ctx.file, field.Type)
461                 generateUnionGetterSetter(w, name, field.GoName, fieldType)
462         }
463
464         // generate union methods
465         //generateUnionMethods(w, name)
466
467         fmt.Fprintln(w)
468 }
469
470 func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) {
471         fmt.Fprintf(w, `
472 func %[1]s%[2]s(a %[3]s) (u %[1]s) {
473         u.Set%[2]s(a)
474         return
475 }
476 func (u *%[1]s) Set%[2]s(a %[3]s) {
477         var b = new(bytes.Buffer)
478         if err := struc.Pack(b, &a); err != nil {
479                 return
480         }
481         copy(u.%[4]s[:], b.Bytes())
482 }
483 func (u *%[1]s) Get%[2]s() (a %[3]s) {
484         var b = bytes.NewReader(u.%[4]s[:])
485         struc.Unpack(b, &a)
486         return
487 }
488 `, structName, getterField, getterStruct, unionDataField)
489 }
490
491 func generateMessage(ctx *GenFile, w io.Writer, msg *Message) {
492         name := msg.GoName
493
494         logf(" writing MESSAGE %q (%s) with %d fields", msg.Name, name, len(msg.Fields))
495
496         // generate struct comment
497         generateComment(ctx, w, name, msg.Name, "message")
498
499         // generate struct definition
500         fmt.Fprintf(w, "type %s struct {", name)
501
502         msgType := otherMessage
503         wasClientIndex := false
504
505         // generate struct fields
506         n := 0
507         for i, field := range msg.Fields {
508                 if i == 1 {
509                         if field.Name == clientIndexField {
510                                 // "client_index" as the second member,
511                                 // this might be an event message or a request
512                                 msgType = eventMessage
513                                 wasClientIndex = true
514                         } else if field.Name == contextField {
515                                 // reply needs "context" as the second member
516                                 msgType = replyMessage
517                         }
518                 } else if i == 2 {
519                         if wasClientIndex && field.Name == contextField {
520                                 // request needs "client_index" as the second member
521                                 // and "context" as the third member
522                                 msgType = requestMessage
523                         }
524                 }
525
526                 // skip internal fields
527                 switch strings.ToLower(field.Name) {
528                 case msgIdField:
529                         continue
530                 case clientIndexField, contextField:
531                         if n == 0 {
532                                 continue
533                         }
534                 }
535                 n++
536                 if n == 1 {
537                         fmt.Fprintln(w)
538                 }
539
540                 generateField(ctx, w, msg.Fields, i)
541         }
542
543         // generate end of the struct
544         fmt.Fprintln(w, "}")
545
546         // generate message methods
547         generateMessageResetMethod(w, name)
548         generateMessageNameGetter(w, name, msg.Name)
549         generateCrcGetter(w, name, msg.CRC)
550         generateMessageTypeGetter(w, name, msgType)
551         generateMessageSize(ctx, w, name, msg.Fields)
552         generateMessageMarshal(ctx, w, name, msg.Fields)
553         generateMessageUnmarshal(ctx, w, name, msg.Fields)
554
555         fmt.Fprintln(w)
556 }
557
558 func generateMessageSize(ctx *GenFile, w io.Writer, name string, fields []*Field) {
559         fmt.Fprintf(w, "func (m *%[1]s) Size() int {\n", name)
560
561         fmt.Fprintf(w, "\tif m == nil { return 0 }\n")
562         fmt.Fprintf(w, "\tvar size int\n")
563
564         encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
565                 t, ok := BaseTypeNames[typ]
566                 if !ok {
567                         return false
568                 }
569
570                 var s = BaseTypeSizes[t]
571                 switch t {
572                 case STRING:
573                         if length > 0 {
574                                 s = length
575                                 fmt.Fprintf(w, "\tsize += %d\n", s)
576                         } else {
577                                 s = 4
578                                 fmt.Fprintf(w, "\tsize += %d + len(%s)\n", s, name)
579                         }
580                 default:
581                         if sizefrom != "" {
582                                 //fmt.Fprintf(w, "\tsize += %d * int(%s)\n", s, sizefrom)
583                                 fmt.Fprintf(w, "\tsize += %d * len(%s)\n", s, name)
584                         } else {
585                                 if length > 0 {
586                                         s = BaseTypeSizes[t] * length
587                                 }
588                                 fmt.Fprintf(w, "\tsize += %d\n", s)
589                         }
590                 }
591
592                 return true
593         }
594
595         lvl := 0
596         var sizeFields func(fields []*Field, parentName string)
597         sizeFields = func(fields []*Field, parentName string) {
598                 lvl++
599                 defer func() { lvl-- }()
600
601                 n := 0
602                 for _, field := range fields {
603                         if field.ParentMessage != nil {
604                                 // skip internal fields
605                                 switch strings.ToLower(field.Name) {
606                                 case msgIdField:
607                                         continue
608                                 case clientIndexField, contextField:
609                                         if n == 0 {
610                                                 continue
611                                         }
612                                 }
613                         }
614                         n++
615
616                         fieldName := field.GoName //camelCaseName(strings.TrimPrefix(field.Name, "_"))
617                         name := fmt.Sprintf("%s.%s", parentName, fieldName)
618                         sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
619                         var sizeFromName string
620                         if sizeFrom != "" {
621                                 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
622                         }
623
624                         fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
625
626                         if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
627                                 continue
628                         }
629
630                         char := fmt.Sprintf("s%d", lvl)
631                         index := fmt.Sprintf("j%d", lvl)
632
633                         if field.Array {
634                                 if field.Length > 0 {
635                                         fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
636                                 } else if field.SizeFrom != "" {
637                                         //fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
638                                         fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
639                                 }
640
641                                 fmt.Fprintf(w, "\tvar %[1]s %[2]s\n_ = %[1]s\n", char, convertToGoType(ctx.file, field.Type))
642                                 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
643                                 name = char
644                         }
645
646                         if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
647                                 if encodeBaseType(enum.Type, name, 0, "") {
648                                 } else {
649                                         fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
650                                 }
651                         } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
652                                 if encodeBaseType(alias.Type, name, alias.Length, "") {
653                                 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
654                                         sizeFields(typ.Fields, name)
655                                 } else {
656                                         fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
657                                 }
658                         } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
659                                 sizeFields(typ.Fields, name)
660                         } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
661                                 maxSize := getUnionSize(ctx.file, union)
662                                 fmt.Fprintf(w, "\tsize += %d\n", maxSize)
663                         } else {
664                                 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
665                         }
666
667                         if field.Array {
668                                 fmt.Fprintf(w, "\t}\n")
669                         }
670                 }
671         }
672
673         sizeFields(fields, "m")
674
675         fmt.Fprintf(w, "return size\n")
676
677         fmt.Fprintf(w, "}\n")
678 }
679
680 func generateMessageMarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
681         fmt.Fprintf(w, "func (m *%[1]s) Marshal(b []byte) ([]byte, error) {\n", name)
682
683         fmt.Fprintf(w, "\to := binary.BigEndian\n")
684         fmt.Fprintf(w, "\t_ = o\n")
685         fmt.Fprintf(w, "\tpos := 0\n")
686         fmt.Fprintf(w, "\t_ = pos\n")
687
688         var buf = new(strings.Builder)
689
690         encodeBaseType := func(typ, name string, length int, sizefrom string) bool {
691                 t, ok := BaseTypeNames[typ]
692                 if !ok {
693                         return false
694                 }
695
696                 isArray := length > 0 || sizefrom != ""
697
698                 switch t {
699                 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
700                         if isArray {
701                                 if length != 0 {
702                                         fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", length)
703                                 } else if sizefrom != "" {
704                                         //fmt.Fprintf(buf, "\tfor i := 0; i < int(%s); i++ {\n", sizefrom)
705                                         fmt.Fprintf(buf, "\tfor i := 0; i < len(%s); i++ {\n", name)
706                                 }
707                         }
708                 }
709
710                 switch t {
711                 case I8, U8:
712                         if isArray {
713                                 fmt.Fprintf(buf, "\tvar x uint8\n")
714                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint8(%s[i]) }\n", name, name)
715                                 name = "x"
716                         }
717                         fmt.Fprintf(buf, "\tbuf[pos] = uint8(%s)\n", name)
718                         fmt.Fprintf(buf, "\tpos += 1\n")
719                         if isArray {
720                                 fmt.Fprintf(buf, "\t}\n")
721                         }
722                 case I16, U16:
723                         if isArray {
724                                 fmt.Fprintf(buf, "\tvar x uint16\n")
725                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint16(%s[i]) }\n", name, name)
726                                 name = "x"
727                         }
728                         fmt.Fprintf(buf, "\to.PutUint16(buf[pos:pos+2], uint16(%s))\n", name)
729                         fmt.Fprintf(buf, "\tpos += 2\n")
730                         if isArray {
731                                 fmt.Fprintf(buf, "\t}\n")
732                         }
733                 case I32, U32:
734                         if isArray {
735                                 fmt.Fprintf(buf, "\tvar x uint32\n")
736                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint32(%s[i]) }\n", name, name)
737                                 name = "x"
738                         }
739                         fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(%s))\n", name)
740                         fmt.Fprintf(buf, "\tpos += 4\n")
741                         if isArray {
742                                 fmt.Fprintf(buf, "\t}\n")
743                         }
744                 case I64, U64:
745                         if isArray {
746                                 fmt.Fprintf(buf, "\tvar x uint64\n")
747                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = uint64(%s[i]) }\n", name, name)
748                                 name = "x"
749                         }
750                         fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], uint64(%s))\n", name)
751                         fmt.Fprintf(buf, "\tpos += 8\n")
752                         if isArray {
753                                 fmt.Fprintf(buf, "\t}\n")
754                         }
755                 case F64:
756                         if isArray {
757                                 fmt.Fprintf(buf, "\tvar x float64\n")
758                                 fmt.Fprintf(buf, "\tif i < len(%s) { x = float64(%s[i]) }\n", name, name)
759                                 name = "x"
760                         }
761                         fmt.Fprintf(buf, "\to.PutUint64(buf[pos:pos+8], math.Float64bits(float64(%s)))\n", name)
762                         fmt.Fprintf(buf, "\tpos += 8\n")
763                         if isArray {
764                                 fmt.Fprintf(buf, "\t}\n")
765                         }
766                 case BOOL:
767                         fmt.Fprintf(buf, "\tif %s { buf[pos] = 1 }\n", name)
768                         fmt.Fprintf(buf, "\tpos += 1\n")
769                 case STRING:
770                         if length != 0 {
771                                 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s)\n", length, name)
772                                 fmt.Fprintf(buf, "\tpos += %d\n", length)
773                         } else {
774                                 fmt.Fprintf(buf, "\to.PutUint32(buf[pos:pos+4], uint32(len(%s)))\n", name)
775                                 fmt.Fprintf(buf, "\tpos += 4\n")
776                                 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+len(%s)], %s[:])\n", name, name)
777                                 fmt.Fprintf(buf, "\tpos += len(%s)\n", name)
778                         }
779                 default:
780                         fmt.Fprintf(buf, "\t// ??? %s %s\n", name, typ)
781                         return false
782                 }
783                 return true
784         }
785
786         lvl := 0
787         var encodeFields func(fields []*Field, parentName string)
788         encodeFields = func(fields []*Field, parentName string) {
789                 lvl++
790                 defer func() { lvl-- }()
791
792                 n := 0
793                 for _, field := range fields {
794                         if field.ParentMessage != nil {
795                                 // skip internal fields
796                                 switch strings.ToLower(field.Name) {
797                                 case msgIdField:
798                                         continue
799                                 case clientIndexField, contextField:
800                                         if n == 0 {
801                                                 continue
802                                         }
803                                 }
804                         }
805                         n++
806
807                         getFieldName := func(name string) string {
808                                 fieldName := camelCaseName(strings.TrimPrefix(name, "_"))
809                                 return fmt.Sprintf("%s.%s", parentName, fieldName)
810                         }
811
812                         fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
813                         name := fmt.Sprintf("%s.%s", parentName, fieldName)
814                         sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
815                         var sizeFromName string
816                         if sizeFrom != "" {
817                                 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
818                         }
819
820                         fmt.Fprintf(buf, "\t// field[%d] %s\n", lvl, name)
821
822                         getSizeOfField := func() *Field {
823                                 for _, f := range fields {
824                                         if f.SizeFrom == field.Name {
825                                                 return f
826                                         }
827                                 }
828                                 return nil
829                         }
830                         if f := getSizeOfField(); f != nil {
831                                 if encodeBaseType(field.Type, fmt.Sprintf("len(%s)", getFieldName(f.Name)), field.Length, "") {
832                                         continue
833                                 }
834                                 panic(fmt.Sprintf("failed to encode base type of sizefrom field: %s", field.Name))
835                         }
836
837                         if encodeBaseType(field.Type, name, field.Length, sizeFromName) {
838                                 continue
839                         }
840
841                         char := fmt.Sprintf("v%d", lvl)
842                         index := fmt.Sprintf("j%d", lvl)
843
844                         if field.Array {
845                                 if field.Length > 0 {
846                                         fmt.Fprintf(buf, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
847                                 } else if field.SizeFrom != "" {
848                                         //fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
849                                         fmt.Fprintf(buf, "\tfor %[1]s := 0; %[1]s < len(%[2]s); %[1]s++ {\n", index, name)
850                                 }
851
852                                 fmt.Fprintf(buf, "\tvar %s %s\n", char, convertToGoType(ctx.file, field.Type))
853                                 fmt.Fprintf(buf, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
854                                 name = char
855                         }
856
857                         if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
858                                 if encodeBaseType(enum.Type, name, 0, "") {
859                                 } else {
860                                         fmt.Fprintf(buf, "\t// ??? ENUM %s %s\n", name, enum.Type)
861                                 }
862                         } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
863                                 if encodeBaseType(alias.Type, name, alias.Length, "") {
864                                 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
865                                         encodeFields(typ.Fields, name)
866                                 } else {
867                                         fmt.Fprintf(buf, "\t// ??? ALIAS %s %s\n", name, alias.Type)
868                                 }
869                         } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
870                                 encodeFields(typ.Fields, name)
871                         } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
872                                 maxSize := getUnionSize(ctx.file, union)
873                                 fmt.Fprintf(buf, "\tcopy(buf[pos:pos+%d], %s.%s[:])\n", maxSize, name, unionDataField)
874                                 fmt.Fprintf(buf, "\tpos += %d\n", maxSize)
875                         } else {
876                                 fmt.Fprintf(buf, "\t// ??? buf[pos] = (%s)\n", name)
877                         }
878
879                         if field.Array {
880                                 fmt.Fprintf(buf, "\t}\n")
881                         }
882                 }
883         }
884
885         encodeFields(fields, "m")
886
887         fmt.Fprintf(w, "\tvar buf []byte\n")
888         fmt.Fprintf(w, "\tif b == nil {\n")
889         fmt.Fprintf(w, "\tbuf = make([]byte, m.Size())\n")
890         fmt.Fprintf(w, "\t} else {\n")
891         fmt.Fprintf(w, "\tbuf = b\n")
892         fmt.Fprintf(w, "\t}\n")
893         fmt.Fprint(w, buf.String())
894
895         fmt.Fprintf(w, "return buf, nil\n")
896
897         fmt.Fprintf(w, "}\n")
898 }
899
900 func generateMessageUnmarshal(ctx *GenFile, w io.Writer, name string, fields []*Field) {
901         fmt.Fprintf(w, "func (m *%[1]s) Unmarshal(tmp []byte) error {\n", name)
902
903         fmt.Fprintf(w, "\to := binary.BigEndian\n")
904         fmt.Fprintf(w, "\t_ = o\n")
905         fmt.Fprintf(w, "\tpos := 0\n")
906         fmt.Fprintf(w, "\t_ = pos\n")
907
908         decodeBaseType := func(typ, orig, name string, length int, sizefrom string, alloc bool) bool {
909                 t, ok := BaseTypeNames[typ]
910                 if !ok {
911                         return false
912                 }
913
914                 isArray := length > 0 || sizefrom != ""
915
916                 switch t {
917                 case I8, U8, I16, U16, I32, U32, I64, U64, F64:
918                         if isArray {
919                                 if alloc {
920                                         if length != 0 {
921                                                 fmt.Fprintf(w, "\t%s = make([]%s, %d)\n", name, orig, length)
922                                         } else if sizefrom != "" {
923                                                 fmt.Fprintf(w, "\t%s = make([]%s, %s)\n", name, orig, sizefrom)
924                                         }
925                                 }
926                                 fmt.Fprintf(w, "\tfor i := 0; i < len(%s); i++ {\n", name)
927                         }
928                 }
929
930                 switch t {
931                 case I8, U8:
932                         if isArray {
933                                 fmt.Fprintf(w, "\t%s[i] = %s(tmp[pos])\n", name, convertToGoType(ctx.file, typ))
934                         } else {
935                                 fmt.Fprintf(w, "\t%s = %s(tmp[pos])\n", name, orig)
936                         }
937                         fmt.Fprintf(w, "\tpos += 1\n")
938                         if isArray {
939                                 fmt.Fprintf(w, "\t}\n")
940                         }
941                 case I16, U16:
942                         if isArray {
943                                 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
944                         } else {
945                                 fmt.Fprintf(w, "\t%s = %s(o.Uint16(tmp[pos:pos+2]))\n", name, orig)
946                         }
947                         fmt.Fprintf(w, "\tpos += 2\n")
948                         if isArray {
949                                 fmt.Fprintf(w, "\t}\n")
950                         }
951                 case I32, U32:
952                         if isArray {
953                                 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
954                         } else {
955                                 fmt.Fprintf(w, "\t%s = %s(o.Uint32(tmp[pos:pos+4]))\n", name, orig)
956                         }
957                         fmt.Fprintf(w, "\tpos += 4\n")
958                         if isArray {
959                                 fmt.Fprintf(w, "\t}\n")
960                         }
961                 case I64, U64:
962                         if isArray {
963                                 fmt.Fprintf(w, "\t%s[i] = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
964                         } else {
965                                 fmt.Fprintf(w, "\t%s = %s(o.Uint64(tmp[pos:pos+8]))\n", name, orig)
966                         }
967                         fmt.Fprintf(w, "\tpos += 8\n")
968                         if isArray {
969                                 fmt.Fprintf(w, "\t}\n")
970                         }
971                 case F64:
972                         if isArray {
973                                 fmt.Fprintf(w, "\t%s[i] = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
974                         } else {
975                                 fmt.Fprintf(w, "\t%s = %s(math.Float64frombits(o.Uint64(tmp[pos:pos+8])))\n", name, orig)
976                         }
977                         fmt.Fprintf(w, "\tpos += 8\n")
978                         if isArray {
979                                 fmt.Fprintf(w, "\t}\n")
980                         }
981                 case BOOL:
982                         fmt.Fprintf(w, "\t%s = tmp[pos] != 0\n", name)
983                         fmt.Fprintf(w, "\tpos += 1\n")
984                 case STRING:
985                         if length != 0 {
986                                 fmt.Fprintf(w, "\t{\n")
987                                 fmt.Fprintf(w, "\tnul := bytes.Index(tmp[pos:pos+%d], []byte{0x00})\n", length)
988                                 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+nul])\n", name)
989                                 fmt.Fprintf(w, "\tpos += %d\n", length)
990                                 fmt.Fprintf(w, "\t}\n")
991                         } else {
992                                 fmt.Fprintf(w, "\t{\n")
993                                 fmt.Fprintf(w, "\tsiz := o.Uint32(tmp[pos:pos+4])\n")
994                                 fmt.Fprintf(w, "\tpos += 4\n")
995                                 fmt.Fprintf(w, "\t%[1]s = codec.DecodeString(tmp[pos:pos+int(siz)])\n", name)
996                                 fmt.Fprintf(w, "\tpos += len(%s)\n", name)
997                                 fmt.Fprintf(w, "\t}\n")
998                         }
999                 default:
1000                         fmt.Fprintf(w, "\t// ??? %s %s\n", name, typ)
1001                         return false
1002                 }
1003                 return true
1004         }
1005
1006         lvl := 0
1007         var decodeFields func(fields []*Field, parentName string)
1008         decodeFields = func(fields []*Field, parentName string) {
1009                 lvl++
1010                 defer func() { lvl-- }()
1011
1012                 n := 0
1013                 for _, field := range fields {
1014                         if field.ParentMessage != nil {
1015                                 // skip internal fields
1016                                 switch strings.ToLower(field.Name) {
1017                                 case msgIdField:
1018                                         continue
1019                                 case clientIndexField, contextField:
1020                                         if n == 0 {
1021                                                 continue
1022                                         }
1023                                 }
1024                         }
1025                         n++
1026
1027                         fieldName := camelCaseName(strings.TrimPrefix(field.Name, "_"))
1028                         name := fmt.Sprintf("%s.%s", parentName, fieldName)
1029                         sizeFrom := camelCaseName(strings.TrimPrefix(field.SizeFrom, "_"))
1030                         var sizeFromName string
1031                         if sizeFrom != "" {
1032                                 sizeFromName = fmt.Sprintf("%s.%s", parentName, sizeFrom)
1033                         }
1034
1035                         fmt.Fprintf(w, "\t// field[%d] %s\n", lvl, name)
1036
1037                         if decodeBaseType(field.Type, convertToGoType(ctx.file, field.Type), name, field.Length, sizeFromName, true) {
1038                                 continue
1039                         }
1040
1041                         //char := fmt.Sprintf("v%d", lvl)
1042                         index := fmt.Sprintf("j%d", lvl)
1043
1044                         if field.Array {
1045                                 if field.Length > 0 {
1046                                         fmt.Fprintf(w, "\tfor %[2]s := 0; %[2]s < %[1]d; %[2]s ++ {\n", field.Length, index)
1047                                 } else if field.SizeFrom != "" {
1048                                         fieldType := getFieldType(ctx, field)
1049                                         if strings.HasPrefix(fieldType, "[]") {
1050                                                 fmt.Fprintf(w, "\t%s = make(%s, int(%s.%s))\n", name, fieldType, parentName, sizeFrom)
1051                                         }
1052                                         fmt.Fprintf(w, "\tfor %[1]s := 0; %[1]s < int(%[2]s.%[3]s); %[1]s++ {\n", index, parentName, sizeFrom)
1053                                 }
1054
1055                                 /*fmt.Fprintf(w, "\tvar %s %s\n", char, convertToGoType(ctx, field.Type))
1056                                 fmt.Fprintf(w, "\tif %[1]s < len(%[2]s) { %[3]s = %[2]s[%[1]s] }\n", index, name, char)
1057                                 name = char*/
1058                                 name = fmt.Sprintf("%s[%s]", name, index)
1059                         }
1060
1061                         if enum := getEnumByRef(ctx.file, field.Type); enum != nil {
1062                                 if decodeBaseType(enum.Type, convertToGoType(ctx.file, field.Type), name, 0, "", false) {
1063                                 } else {
1064                                         fmt.Fprintf(w, "\t// ??? ENUM %s %s\n", name, enum.Type)
1065                                 }
1066                         } else if alias := getAliasByRef(ctx.file, field.Type); alias != nil {
1067                                 if decodeBaseType(alias.Type, convertToGoType(ctx.file, field.Type), name, alias.Length, "", false) {
1068                                 } else if typ := getTypeByRef(ctx.file, alias.Type); typ != nil {
1069                                         decodeFields(typ.Fields, name)
1070                                 } else {
1071                                         fmt.Fprintf(w, "\t// ??? ALIAS %s %s\n", name, alias.Type)
1072                                 }
1073                         } else if typ := getTypeByRef(ctx.file, field.Type); typ != nil {
1074                                 decodeFields(typ.Fields, name)
1075                         } else if union := getUnionByRef(ctx.file, field.Type); union != nil {
1076                                 maxSize := getUnionSize(ctx.file, union)
1077                                 fmt.Fprintf(w, "\tcopy(%s.%s[:], tmp[pos:pos+%d])\n", name, unionDataField, maxSize)
1078                                 fmt.Fprintf(w, "\tpos += %d\n", maxSize)
1079                         } else {
1080                                 fmt.Fprintf(w, "\t// ??? buf[pos] = (%s)\n", name)
1081                         }
1082
1083                         if field.Array {
1084                                 fmt.Fprintf(w, "\t}\n")
1085                         }
1086                 }
1087         }
1088
1089         decodeFields(fields, "m")
1090
1091         fmt.Fprintf(w, "return nil\n")
1092
1093         fmt.Fprintf(w, "}\n")
1094 }
1095
1096 func getFieldType(ctx *GenFile, field *Field) string {
1097         //fieldName := strings.TrimPrefix(field.Name, "_")
1098         //fieldName = camelCaseName(fieldName)
1099         //fieldName := field.GoName
1100
1101         dataType := convertToGoType(ctx.file, field.Type)
1102         fieldType := dataType
1103
1104         // check if it is array
1105         if field.Length > 0 || field.SizeFrom != "" {
1106                 if dataType == "uint8" {
1107                         dataType = "byte"
1108                 }
1109                 if dataType == "string" && field.Array {
1110                         fieldType = "string"
1111                         dataType = "byte"
1112                 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1113                         fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1114                 } else {
1115                         fieldType = "[]" + dataType
1116                 }
1117         }
1118
1119         return fieldType
1120 }
1121
1122 func generateField(ctx *GenFile, w io.Writer, fields []*Field, i int) {
1123         field := fields[i]
1124
1125         //fieldName := strings.TrimPrefix(field.Name, "_")
1126         //fieldName = camelCaseName(fieldName)
1127         fieldName := field.GoName
1128
1129         dataType := convertToGoType(ctx.file, field.Type)
1130         fieldType := dataType
1131
1132         // generate length field for strings
1133         if field.Type == "string" && field.Length == 0 {
1134                 fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName)
1135         }
1136
1137         // check if it is array
1138         if field.Length > 0 || field.SizeFrom != "" {
1139                 if dataType == "uint8" {
1140                         dataType = "byte"
1141                 }
1142                 if dataType == "string" && field.Array {
1143                         fieldType = "string"
1144                         dataType = "byte"
1145                 } else if _, ok := BaseTypeNames[field.Type]; !ok && field.SizeFrom == "" {
1146                         fieldType = fmt.Sprintf("[%d]%s", field.Length, dataType)
1147                 } else {
1148                         fieldType = "[]" + dataType
1149                 }
1150         }
1151         fmt.Fprintf(w, "\t%s %s", fieldName, fieldType)
1152
1153         fieldTags := map[string]string{}
1154
1155         if field.Length > 0 && field.Array {
1156                 // fixed size array
1157                 fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType)
1158         } else {
1159                 for _, f := range fields {
1160                         if f.SizeFrom == field.Name {
1161                                 // variable sized array
1162                                 //sizeOfName := camelCaseName(f.Name)
1163                                 fieldTags["struc"] = fmt.Sprintf("sizeof=%s", f.GoName)
1164                         }
1165                 }
1166         }
1167
1168         if ctx.IncludeBinapiNames {
1169                 typ := fromApiType(field.Type)
1170                 if field.Array {
1171                         if field.Length > 0 {
1172                                 fieldTags["binapi"] = fmt.Sprintf("%s[%d],name=%s", typ, field.Length, field.Name)
1173                         } else if field.SizeFrom != "" {
1174                                 fieldTags["binapi"] = fmt.Sprintf("%s[%s],name=%s", typ, field.SizeFrom, field.Name)
1175                         }
1176                 } else {
1177                         fieldTags["binapi"] = fmt.Sprintf("%s,name=%s", typ, field.Name)
1178                 }
1179         }
1180         if limit, ok := field.Meta["limit"]; ok && limit.(int) > 0 {
1181                 fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], limit)
1182         }
1183         if def, ok := field.Meta["default"]; ok && def != nil {
1184                 actual := getActualType(ctx.file, fieldType)
1185                 if t, ok := binapiTypes[actual]; ok && t != "float64" {
1186                         defnum := int(def.(float64))
1187                         fieldTags["binapi"] = fmt.Sprintf("%s,default=%d", fieldTags["binapi"], defnum)
1188                 } else {
1189                         fieldTags["binapi"] = fmt.Sprintf("%s,default=%v", fieldTags["binapi"], def)
1190                 }
1191         }
1192
1193         fieldTags["json"] = fmt.Sprintf("%s,omitempty", field.Name)
1194
1195         if len(fieldTags) > 0 {
1196                 fmt.Fprintf(w, "\t`")
1197                 var keys []string
1198                 for k := range fieldTags {
1199                         keys = append(keys, k)
1200                 }
1201                 sort.Strings(keys)
1202                 var n int
1203                 for _, tt := range keys {
1204                         t, ok := fieldTags[tt]
1205                         if !ok {
1206                                 continue
1207                         }
1208                         if n > 0 {
1209                                 fmt.Fprintf(w, " ")
1210                         }
1211                         n++
1212                         fmt.Fprintf(w, `%s:"%s"`, tt, t)
1213                 }
1214                 fmt.Fprintf(w, "`")
1215         }
1216
1217         fmt.Fprintln(w)
1218 }
1219
1220 func generateMessageResetMethod(w io.Writer, structName string) {
1221         fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
1222 }
1223
1224 func generateMessageNameGetter(w io.Writer, structName, msgName string) {
1225         fmt.Fprintf(w, "func (*%s) GetMessageName() string {    return %q }\n", structName, msgName)
1226 }
1227
1228 func generateTypeNameGetter(w io.Writer, structName, msgName string) {
1229         fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
1230 }
1231
1232 func generateIPAddressConversion(w io.Writer, structName string) {
1233         f1 := func(ipVer, ipVerExt int) string {
1234                 return fmt.Sprintf(`address.Af = ADDRESS_IP%[1]d
1235                 var ip%[1]daddr IP%[1]dAddress
1236                 copy(ip%[1]daddr[:], netIP.To%[2]d())
1237                 address.Un.SetIP%[1]d(ip%[1]daddr)`, ipVer, ipVerExt)
1238         }
1239         f2 := func(ipVer, ipVerExt int) string {
1240                 return fmt.Sprintf("ip%[1]dAddress := a.Un.GetIP%[1]d()\nip = net.IP(ip%[1]dAddress[:]).To%[2]d().String()",
1241                         ipVer, ipVerExt)
1242         }
1243         // IP to Address
1244         fmt.Fprintf(w, `func ParseAddress(ip string) (%[1]s, error) {
1245         var address %[1]s
1246         netIP := net.ParseIP(ip)
1247         if netIP == nil {
1248                 return address, fmt.Errorf("invalid address: %[2]s", ip)
1249         }
1250         if ip4 := netIP.To4(); ip4 == nil {
1251                 %[3]s
1252         } else {
1253                 %[4]s
1254         }
1255         return address, nil
1256 }
1257 `, structName, "%s", f1(6, 16), f1(4, 4))
1258         fmt.Fprintln(w)
1259
1260         // Address to IP
1261         fmt.Fprintln(w)
1262         fmt.Fprintf(w, `func (a *%[1]s) ToString() string {
1263         var ip string
1264         if a.Af == ADDRESS_IP6 {
1265                 %[2]s
1266         } else {
1267                 %[3]s
1268         }
1269         return ip
1270 }`, structName, f2(6, 16), f2(4, 4))
1271 }
1272
1273 func generatePrefixConversion(w io.Writer, structName string) {
1274         fErr := func() string {
1275                 return fmt.Sprintf(`if err != nil {
1276                         return Prefix{}, fmt.Errorf("invalid IP %s: %s", ip, err)
1277                 }`, "%s", "%v")
1278         }
1279
1280         // IP to Prefix
1281         fmt.Fprintf(w, `func ParsePrefix(ip string) (prefix %[1]s, err error) {
1282         hasPrefix := strings.Contains(ip, "/")
1283         if hasPrefix {
1284                 netIP, network, err := net.ParseCIDR(ip)
1285                 %[2]s
1286         maskSize, _ := network.Mask.Size()
1287         prefix.Len = byte(maskSize)
1288         prefix.Address, err = ParseAddress(netIP.String())
1289                 %[2]s
1290         } else {
1291                 netIP := net.ParseIP(ip)
1292                 defaultMaskSize, _ := net.CIDRMask(32, 32).Size()
1293                 if netIP.To4() == nil {
1294                         defaultMaskSize, _ = net.CIDRMask(128, 128).Size()
1295                 }
1296                 prefix.Len = byte(defaultMaskSize)
1297                 prefix.Address, err = ParseAddress(netIP.String())
1298                 %[2]s
1299         }
1300         return prefix, nil
1301 }`, structName, fErr(), nil)
1302         fmt.Fprintln(w)
1303
1304         // Prefix to IP
1305         fmt.Fprintln(w)
1306         fmt.Fprintf(w, `func (p *%[1]s) ToString() string {
1307                 ip := p.Address.ToString()
1308                 return ip + "/" + strconv.Itoa(int(p.Len))
1309         }`, structName)
1310 }
1311
1312 func generateMacAddressConversion(w io.Writer, structName string) {
1313         // string to MAC
1314         fmt.Fprintf(w, `func ParseMAC(mac string) (parsed %[1]s, err error) {
1315         var hw net.HardwareAddr
1316         if hw, err = net.ParseMAC(mac); err != nil {
1317                 return
1318         }
1319         copy(parsed[:], hw[:])
1320         return
1321 }`, structName)
1322         fmt.Fprintln(w)
1323
1324         // MAC to string
1325         fmt.Fprintln(w)
1326         fmt.Fprintf(w, `func (m *%[1]s) ToString() string {
1327                 return net.HardwareAddr(m[:]).String()
1328         }`, structName)
1329 }
1330
1331 func generateCrcGetter(w io.Writer, structName, crc string) {
1332         crc = strings.TrimPrefix(crc, "0x")
1333         fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
1334 }
1335
1336 func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
1337         fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
1338         if msgType == requestMessage {
1339                 fmt.Fprintf(w, "\treturn api.RequestMessage")
1340         } else if msgType == replyMessage {
1341                 fmt.Fprintf(w, "\treturn api.ReplyMessage")
1342         } else if msgType == eventMessage {
1343                 fmt.Fprintf(w, "\treturn api.EventMessage")
1344         } else {
1345                 fmt.Fprintf(w, "\treturn api.OtherMessage")
1346         }
1347         fmt.Fprintln(w, "}")
1348         fmt.Fprintln(w)
1349 }
1350
1351 func logf(f string, v ...interface{}) {
1352         logrus.Debugf(f, v...)
1353 }