Binary API generator improvements
[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 // BaseType represents base types in VPP binary API.
31 type BaseType int
32
33 const (
34         U8 BaseType = iota + 1
35         I8
36         U16
37         I16
38         U32
39         I32
40         U64
41         I64
42         F64
43         BOOL
44         STRING
45 )
46
47 var (
48         BaseTypes = map[BaseType]string{
49                 U8:     "u8",
50                 I8:     "i8",
51                 U16:    "u16",
52                 I16:    "i16",
53                 U32:    "u32",
54                 I32:    "i32",
55                 U64:    "u64",
56                 I64:    "i64",
57                 F64:    "f64",
58                 BOOL:   "bool",
59                 STRING: "string",
60         }
61         BaseTypeNames = map[string]BaseType{
62                 "u8":     U8,
63                 "i8":     I8,
64                 "u16":    U16,
65                 "i16":    I16,
66                 "u32":    U32,
67                 "i32":    I32,
68                 "u64":    U64,
69                 "i64":    I64,
70                 "f64":    F64,
71                 "bool":   BOOL,
72                 "string": STRING,
73         }
74 )
75
76 var BaseTypeSizes = map[BaseType]int{
77         U8:     1,
78         I8:     1,
79         U16:    2,
80         I16:    2,
81         U32:    4,
82         I32:    4,
83         U64:    8,
84         I64:    8,
85         F64:    8,
86         BOOL:   1,
87         STRING: 1,
88 }
89
90 type Kind int
91
92 const (
93         _ = iota
94         Uint8Kind
95         Int8Kind
96         Uint16Kind
97         Int16Kind
98         Uint32Kind
99         Int32Kind
100         Uint64Kind
101         Int64Kind
102         Float64Kind
103         BoolKind
104         StringKind
105         EnumKind
106         AliasKind
107         StructKind
108         UnionKind
109         MessageKind
110 )
111
112 // toApiType returns name that is used as type reference in VPP binary API
113 func toApiType(name string) string {
114         return defineApiPrefix + name + defineApiSuffix
115 }
116
117 func fromApiType(typ string) string {
118         name := typ
119         name = strings.TrimPrefix(name, defineApiPrefix)
120         name = strings.TrimSuffix(name, defineApiSuffix)
121         return name
122 }
123
124 func getSizeOfType(module *File, typ *Struct) (size int) {
125         for _, field := range typ.Fields {
126                 enum := getEnumByRef(module, field.Type)
127                 if enum != nil {
128                         size += getSizeOfBinapiTypeLength(enum.Type, field.Length)
129                         continue
130                 }
131                 size += getSizeOfBinapiTypeLength(field.Type, field.Length)
132         }
133         return size
134 }
135
136 func getEnumByRef(file *File, ref string) *Enum {
137         for _, typ := range file.Enums {
138                 if ref == toApiType(typ.Name) {
139                         return typ
140                 }
141         }
142         return nil
143 }
144
145 func getTypeByRef(file *File, ref string) *Struct {
146         for _, typ := range file.Structs {
147                 if ref == toApiType(typ.Name) {
148                         return typ
149                 }
150         }
151         return nil
152 }
153
154 func getAliasByRef(file *File, ref string) *Alias {
155         for _, alias := range file.Aliases {
156                 if ref == toApiType(alias.Name) {
157                         return alias
158                 }
159         }
160         return nil
161 }
162
163 func getUnionByRef(file *File, ref string) *Union {
164         for _, union := range file.Unions {
165                 if ref == toApiType(union.Name) {
166                         return union
167                 }
168         }
169         return nil
170 }
171
172 func getBinapiTypeSize(binapiType string) (size int) {
173         typName := BaseTypeNames[binapiType]
174         return BaseTypeSizes[typName]
175 }
176
177 // binapiTypes is a set of types used VPP binary API for translation to Go types
178 var binapiTypes = map[string]string{
179         "u8":  "uint8",
180         "i8":  "int8",
181         "u16": "uint16",
182         "i16": "int16",
183         "u32": "uint32",
184         "i32": "int32",
185         "u64": "uint64",
186         "i64": "int64",
187         "f64": "float64",
188 }
189 var BaseTypesGo = map[BaseType]string{
190         U8:     "uint8",
191         I8:     "int8",
192         U16:    "uint16",
193         I16:    "int16",
194         U32:    "uint32",
195         I32:    "int32",
196         U64:    "uint64",
197         I64:    "int64",
198         F64:    "float64",
199         BOOL:   "bool",
200         STRING: "string",
201 }
202
203 func getActualType(file *File, typ string) (actual string) {
204         for _, enum := range file.Enums {
205                 if enum.GoName == typ {
206                         return enum.Type
207                 }
208         }
209         for _, alias := range file.Aliases {
210                 if alias.GoName == typ {
211                         return alias.Type
212                 }
213         }
214         return typ
215 }
216
217 // convertToGoType translates the VPP binary API type into Go type.
218 // Imported types are with import prefix.
219 func convertToGoType(file *File, binapiType string) (typ string) {
220         if t, ok := binapiTypes[binapiType]; ok {
221                 // basic types
222                 typ = t
223         } else if r, ok := file.refmap[binapiType]; ok {
224                 // specific types (enums/types/unions)
225                 var prefix string
226                 typ = camelCaseName(r)
227                 // look in imports using name and type name eventually
228                 if imp, ok := file.imports[typ]; ok {
229                         prefix = fmt.Sprintf("%s.", imp)
230                 } else if imp, ok := file.imports[fromApiType(binapiType)]; ok {
231                         prefix = fmt.Sprintf("%s.", imp)
232                 }
233                 typ = fmt.Sprintf("%s%s", prefix, typ)
234         } else {
235                 switch binapiType {
236                 case "bool", "string":
237                         typ = binapiType
238                 default:
239                         // fallback type
240                         logrus.Warnf("found unknown VPP binary API type %q, using byte", binapiType)
241                         typ = "byte"
242                 }
243         }
244         return typ
245 }
246
247 func getSizeOfBinapiTypeLength(typ string, length int) (size int) {
248         if n := getBinapiTypeSize(typ); n > 0 {
249                 if length > 0 {
250                         return n * length
251                 } else {
252                         return n
253                 }
254         }
255
256         return
257 }
258
259 func getUnionSize(file *File, union *Union) (maxSize int) {
260         for _, field := range union.Fields {
261                 typ := getTypeByRef(file, field.Type)
262                 if typ != nil {
263                         if size := getSizeOfType(file, typ); size > maxSize {
264                                 maxSize = size
265                         }
266                         continue
267                 }
268                 alias := getAliasByRef(file, field.Type)
269                 if alias != nil {
270                         if size := getSizeOfBinapiTypeLength(alias.Type, alias.Length); size > maxSize {
271                                 maxSize = size
272                         }
273                         continue
274                 } else {
275                         logf("no type or alias found for union %s field type %q", union.Name, field.Type)
276                         continue
277                 }
278         }
279         logf("getUnionSize: %s %+v max=%v", union.Name, union.Fields, maxSize)
280         return
281 }