binapigen: fix union size
[govpp.git] / binapigen / types.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         "strings"
20
21         "github.com/sirupsen/logrus"
22 )
23
24 // define api
25 const (
26         defineApiPrefix = "vl_api_"
27         defineApiSuffix = "_t"
28 )
29
30 // toApiType returns name that is used as type reference in VPP binary API
31 func toApiType(name string) string {
32         return defineApiPrefix + name + defineApiSuffix
33 }
34
35 func fromApiType(typ string) string {
36         name := typ
37         name = strings.TrimPrefix(name, defineApiPrefix)
38         name = strings.TrimSuffix(name, defineApiSuffix)
39         return name
40 }
41
42 const (
43         U8     = "u8"
44         I8     = "i8"
45         U16    = "u16"
46         I16    = "i16"
47         U32    = "u32"
48         I32    = "i32"
49         U64    = "u64"
50         I64    = "i64"
51         F64    = "f64"
52         BOOL   = "bool"
53         STRING = "string"
54 )
55
56 var BaseTypeSizes = map[string]int{
57         U8:     1,
58         I8:     1,
59         U16:    2,
60         I16:    2,
61         U32:    4,
62         I32:    4,
63         U64:    8,
64         I64:    8,
65         F64:    8,
66         BOOL:   1,
67         STRING: 1,
68 }
69
70 var BaseTypesGo = map[string]string{
71         U8:     "uint8",
72         I8:     "int8",
73         U16:    "uint16",
74         I16:    "int16",
75         U32:    "uint32",
76         I32:    "int32",
77         U64:    "uint64",
78         I64:    "int64",
79         F64:    "float64",
80         BOOL:   "bool",
81         STRING: "string",
82 }
83
84 func fieldActualType(field *Field) (actual string) {
85         switch {
86         case field.TypeAlias != nil:
87                 actual = field.TypeAlias.Type
88         case field.TypeEnum != nil:
89                 actual = field.TypeEnum.Type
90         default:
91                 actual = field.Type
92         }
93         return
94 }
95
96 func fieldGoType(g *GenFile, field *Field) string {
97         switch {
98         case field.TypeAlias != nil:
99                 return g.GoIdent(field.TypeAlias.GoIdent)
100         case field.TypeEnum != nil:
101                 return g.GoIdent(field.TypeEnum.GoIdent)
102         case field.TypeStruct != nil:
103                 return g.GoIdent(field.TypeStruct.GoIdent)
104         case field.TypeUnion != nil:
105                 return g.GoIdent(field.TypeUnion.GoIdent)
106         }
107         t, ok := BaseTypesGo[field.Type]
108         if !ok {
109                 logrus.Panicf("type %s is not base type", field.Type)
110         }
111         return t
112 }
113
114 func getFieldType(g *GenFile, field *Field) string {
115         gotype := fieldGoType(g, field)
116         if field.Array {
117                 switch gotype {
118                 case "uint8":
119                         return "[]byte"
120                 case "string":
121                         return "string"
122                 }
123                 if _, ok := BaseTypesGo[field.Type]; !ok && field.Length > 0 {
124                         return fmt.Sprintf("[%d]%s", field.Length, gotype)
125                 }
126                 return "[]" + gotype
127         }
128         return gotype
129 }
130
131 func getUnionSize(union *Union) (maxSize int) {
132         for _, field := range union.Fields {
133                 if size, isBaseType := getSizeOfField(field); isBaseType {
134                         logrus.Panicf("union %s field %s has unexpected type %q", union.Name, field.Name, field.Type)
135                 } else if size > maxSize {
136                         maxSize = size
137                 }
138         }
139         //logf("getUnionSize: %s %+v max=%v", union.Name, union.Fields, maxSize)
140         return
141 }
142
143 func getSizeOfField(field *Field) (size int, isBaseType bool) {
144         if alias := field.TypeAlias; alias != nil {
145                 size = getSizeOfBinapiBaseType(alias.Type, alias.Length)
146                 return
147         }
148         if enum := field.TypeEnum; enum != nil {
149                 size = getSizeOfBinapiBaseType(enum.Type, field.Length)
150                 return
151         }
152         if structType := field.TypeStruct; structType != nil {
153                 size = getSizeOfStruct(structType)
154                 return
155         }
156         if union := field.TypeUnion; union != nil {
157                 size = getUnionSize(union)
158                 return
159         }
160         return size, true
161 }
162
163 func getSizeOfStruct(typ *Struct) (size int) {
164         for _, field := range typ.Fields {
165                 fieldSize, isBaseType := getSizeOfField(field)
166                 if isBaseType {
167                         size += getSizeOfBinapiBaseType(field.Type, field.Length)
168                         continue
169                 }
170                 size += fieldSize
171         }
172         return size
173 }
174
175 // Returns size of base type multiplied by length. Length equal to zero
176 // returns base type size.
177 func getSizeOfBinapiBaseType(typ string, length int) (size int) {
178         if n := BaseTypeSizes[typ]; n > 0 {
179                 if length > 1 {
180                         return n * length
181                 } else {
182                         return n
183                 }
184         }
185         return
186 }