blob: d8461398765ff2157ea8868cd786ae843f47a761 [file] [log] [blame]
Ole Troana7564e82018-06-12 21:06:44 +02001#
2# Copyright (c) 2018 Cisco and/or its affiliates.
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at:
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#
Ole Troana7564e82018-06-12 21:06:44 +020015import collections
Paul Vinciguerra2e876062020-05-12 10:45:17 -040016from enum import IntFlag
Ole Troan85465582019-04-30 10:04:36 +020017import logging
Ole Troan85465582019-04-30 10:04:36 +020018import socket
Paul Vinciguerraae8819f2019-06-07 13:35:37 -040019import struct
20import sys
Paul Vinciguerra42bdba82019-03-07 11:26:23 -080021
Paul Vinciguerra2e876062020-05-12 10:45:17 -040022from . import vpp_format
Paul Vinciguerra42bdba82019-03-07 11:26:23 -080023
Ole Troana7564e82018-06-12 21:06:44 +020024
25#
26# Set log-level in application by doing e.g.:
27# logger = logging.getLogger('vpp_serializer')
28# logger.setLevel(logging.DEBUG)
29#
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020030logger = logging.getLogger("vpp_papi.serializer")
Ole Troana7564e82018-06-12 21:06:44 +020031
Paul Vinciguerrad7adc292020-12-02 14:34:27 -050032
33def check(d):
34 return type(d) is dict or type(d) is bytes
Ole Troan8006c6a2018-12-17 12:02:26 +010035
Ole Troan0bcad322018-12-11 13:04:01 +010036
37def conversion_required(data, field_type):
38 if check(data):
39 return False
40 try:
41 if type(data).__name__ in vpp_format.conversion_table[field_type]:
42 return True
43 except KeyError:
44 return False
45
46
47def conversion_packer(data, field_type):
48 t = type(data).__name__
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020049 return types[field_type].pack(vpp_format.conversion_table[field_type][t](data))
Ole Troan0bcad322018-12-11 13:04:01 +010050
51
52def conversion_unpacker(data, field_type):
53 if field_type not in vpp_format.conversion_unpacker_table:
54 return data
55 return vpp_format.conversion_unpacker_table[field_type](data)
56
Ole Troana7564e82018-06-12 21:06:44 +020057
Paul Vinciguerrad7adc292020-12-02 14:34:27 -050058class Packer:
Ole Troan418ebb72019-11-19 04:38:20 +010059 options = {}
60
61 def pack(self, data, kwargs):
62 raise NotImplementedError
63
64 def unpack(self, data, offset, result=None, ntc=False):
65 raise NotImplementedError
66
67 # override as appropriate in subclasses
Ole Troanda47f672019-11-24 15:22:55 +010068 @staticmethod
69 def _get_packer_with_options(f_type, options):
Ole Troan418ebb72019-11-19 04:38:20 +010070 return types[f_type]
71
72 def get_packer_with_options(self, f_type, options):
73 if options is not None:
74 try:
Ole Troanda47f672019-11-24 15:22:55 +010075 c = types[f_type].__class__
76 return c._get_packer_with_options(f_type, options)
Ole Troan418ebb72019-11-19 04:38:20 +010077 except IndexError:
78 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020079 "Options not supported for {}{} ({})".format(
80 f_type, types[f_type].__class__, options
81 )
82 )
Ole Troan418ebb72019-11-19 04:38:20 +010083
84
85class BaseTypes(Packer):
Ole Troan85465582019-04-30 10:04:36 +020086 def __init__(self, type, elements=0, options=None):
Paul Vinciguerra14b0b472019-11-23 23:49:57 -050087 self._type = type
88 self._elements = elements
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020089 base_types = {
90 "u8": ">B",
91 "i8": ">b",
92 "string": ">s",
93 "u16": ">H",
94 "i16": ">h",
95 "u32": ">I",
96 "i32": ">i",
97 "u64": ">Q",
98 "i64": ">q",
99 "f64": "=d",
100 "bool": ">?",
101 "header": ">HI",
102 }
Ole Troana7564e82018-06-12 21:06:44 +0200103
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200104 if elements > 0 and (type == "u8" or type == "string"):
105 self.packer = struct.Struct(">%ss" % elements)
Ole Troana7564e82018-06-12 21:06:44 +0200106 else:
107 self.packer = struct.Struct(base_types[type])
108 self.size = self.packer.size
Ole Troan85465582019-04-30 10:04:36 +0200109 self.options = options
110
Ole Troana7564e82018-06-12 21:06:44 +0200111 def pack(self, data, kwargs=None):
Ole Troan40dc4b32019-10-22 14:01:53 +0200112 if data is None: # Default to zero if not specified
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200113 if self.options and "default" in self.options:
114 data = self.options["default"]
Ole Troan85465582019-04-30 10:04:36 +0200115 else:
116 data = 0
Ole Troana7564e82018-06-12 21:06:44 +0200117 return self.packer.pack(data)
118
Ole Troan0bcad322018-12-11 13:04:01 +0100119 def unpack(self, data, offset, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200120 return self.packer.unpack_from(data, offset)[0], self.packer.size
Ole Troana7564e82018-06-12 21:06:44 +0200121
Ole Troanda47f672019-11-24 15:22:55 +0100122 @staticmethod
123 def _get_packer_with_options(f_type, options):
124 return BaseTypes(f_type, options=options)
Ole Troana7564e82018-06-12 21:06:44 +0200125
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500126 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200127 return "BaseTypes(type=%s, elements=%s, options=%s)" % (
128 self._type,
129 self._elements,
130 self.options,
131 )
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500132
Ole Troan418ebb72019-11-19 04:38:20 +0100133
134class String(Packer):
Ole Troane5ff5a32019-08-23 22:55:18 +0200135 def __init__(self, name, num, options):
136 self.name = name
137 self.num = num
Ole Troan413f4a52018-11-28 11:36:05 +0100138 self.size = 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200139 self.length_field_packer = BaseTypes("u32")
140 self.limit = options["limit"] if "limit" in options else num
Ole Troane5ff5a32019-08-23 22:55:18 +0200141 self.fixed = True if num else False
142 if self.fixed and not self.limit:
Ole Troan9ac11382019-04-23 17:11:01 +0200143 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200144 "Invalid combination for: {}, {} fixed:{} limit:{}".format(
145 name, options, self.fixed, self.limit
146 )
147 )
Ole Troan9ac11382019-04-23 17:11:01 +0200148
Ole Troane5ff5a32019-08-23 22:55:18 +0200149 def pack(self, list, kwargs=None):
150 if not list:
151 if self.fixed:
152 return b"\x00" * self.limit
153 return self.length_field_packer.pack(0) + b""
154 if self.limit and len(list) > self.limit - 1:
155 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200156 "Invalid argument length for: {}, {} maximum {}".format(
157 list, len(list), self.limit - 1
158 )
159 )
Ole Troane5ff5a32019-08-23 22:55:18 +0200160 if self.fixed:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200161 return list.encode("ascii").ljust(self.limit, b"\x00")
162 return self.length_field_packer.pack(len(list)) + list.encode("ascii")
Ole Troan413f4a52018-11-28 11:36:05 +0100163
164 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troane5ff5a32019-08-23 22:55:18 +0200165 if self.fixed:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200166 p = BaseTypes("u8", self.num)
Ole Troane5ff5a32019-08-23 22:55:18 +0200167 s = p.unpack(data, offset)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200168 s2 = s[0].split(b"\0", 1)[0]
169 return (s2.decode("ascii"), self.num)
Ole Troane5ff5a32019-08-23 22:55:18 +0200170
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200171 length, length_field_size = self.length_field_packer.unpack(data, offset)
Ole Troan413f4a52018-11-28 11:36:05 +0100172 if length == 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200173 return "", 0
174 p = BaseTypes("u8", length)
Ole Troan413f4a52018-11-28 11:36:05 +0100175 x, size = p.unpack(data, offset + length_field_size)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200176 return (x.decode("ascii", errors="replace"), size + length_field_size)
Ole Troan413f4a52018-11-28 11:36:05 +0100177
178
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200179types = {
180 "u8": BaseTypes("u8"),
181 "i8": BaseTypes("i8"),
182 "u16": BaseTypes("u16"),
183 "i16": BaseTypes("i16"),
184 "u32": BaseTypes("u32"),
185 "i32": BaseTypes("i32"),
186 "u64": BaseTypes("u64"),
187 "i64": BaseTypes("i64"),
188 "f64": BaseTypes("f64"),
189 "bool": BaseTypes("bool"),
190 "string": String,
191}
Ole Troana7564e82018-06-12 21:06:44 +0200192
Ole Troan40dc4b32019-10-22 14:01:53 +0200193class_types = {}
Ole Troana7564e82018-06-12 21:06:44 +0200194
Ole Troan418ebb72019-11-19 04:38:20 +0100195
Ole Troan0685da42018-10-16 14:42:50 +0200196def vpp_get_type(name):
197 try:
198 return types[name]
199 except KeyError:
200 return None
201
202
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800203class VPPSerializerValueError(ValueError):
204 pass
205
206
Ole Troan418ebb72019-11-19 04:38:20 +0100207class FixedList_u8(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200208 def __init__(self, name, field_type, num):
209 self.name = name
210 self.num = num
211 self.packer = BaseTypes(field_type, num)
212 self.size = self.packer.size
Ole Troan413f4a52018-11-28 11:36:05 +0100213 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200214
Ole Troan0bcad322018-12-11 13:04:01 +0100215 def pack(self, data, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200216 """Packs a fixed length bytestring. Left-pads with zeros
217 if input data is too short."""
Ole Troan0bcad322018-12-11 13:04:01 +0100218 if not data:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200219 return b"\x00" * self.size
Ole Troan0bcad322018-12-11 13:04:01 +0100220
221 if len(data) > self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800222 raise VPPSerializerValueError(
223 'Fixed list length error for "{}", got: {}'
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200224 " expected: {}".format(self.name, len(data), self.num)
225 )
Ole Troana7564e82018-06-12 21:06:44 +0200226
Ole Troan64e978b2019-10-17 21:40:36 +0200227 try:
228 return self.packer.pack(data)
229 except struct.error:
230 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200231 'Packing failed for "{}" {}'.format(self.name, kwargs)
232 )
Ole Troan418ebb72019-11-19 04:38:20 +0100233
Ole Troan0bcad322018-12-11 13:04:01 +0100234 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200235 if len(data[offset:]) < self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800236 raise VPPSerializerValueError(
237 'Invalid array length for "{}" got {}'
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200238 " expected {}".format(self.name, len(data[offset:]), self.num)
239 )
Ole Troana7564e82018-06-12 21:06:44 +0200240 return self.packer.unpack(data, offset)
241
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500242 def __repr__(self):
243 return "FixedList_u8(name=%s, field_type=%s, num=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200244 self.name,
245 self.field_type,
246 self.num,
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500247 )
248
Ole Troana7564e82018-06-12 21:06:44 +0200249
Ole Troan418ebb72019-11-19 04:38:20 +0100250class FixedList(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200251 def __init__(self, name, field_type, num):
252 self.num = num
253 self.packer = types[field_type]
254 self.size = self.packer.size * num
Ole Troan0bcad322018-12-11 13:04:01 +0100255 self.name = name
256 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200257
258 def pack(self, list, kwargs):
Ole Troana7564e82018-06-12 21:06:44 +0200259 if len(list) != self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800260 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200261 "Fixed list length error, got: {} expected: {}".format(
262 len(list), self.num
263 )
264 )
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300265 b = bytearray()
Ole Troana7564e82018-06-12 21:06:44 +0200266 for e in list:
267 b += self.packer.pack(e)
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300268 return bytes(b)
Ole Troana7564e82018-06-12 21:06:44 +0200269
Ole Troan0bcad322018-12-11 13:04:01 +0100270 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200271 # Return a list of arguments
272 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200273 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200274 for e in range(self.num):
Ole Troan0bcad322018-12-11 13:04:01 +0100275 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200276 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200277 offset += size
278 total += size
279 return result, total
Ole Troana7564e82018-06-12 21:06:44 +0200280
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500281 def __repr__(self):
Paul Vinciguerra99fbf052019-11-26 14:15:53 -0500282 return "FixedList(name=%s, field_type=%s, num=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200283 self.name,
284 self.field_type,
285 self.num,
286 )
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500287
Ole Troana7564e82018-06-12 21:06:44 +0200288
Ole Troan418ebb72019-11-19 04:38:20 +0100289class VLAList(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200290 def __init__(self, name, field_type, len_field_name, index):
Ole Troanc84cbad2018-09-06 22:58:05 +0200291 self.name = name
Ole Troan413f4a52018-11-28 11:36:05 +0100292 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200293 self.index = index
294 self.packer = types[field_type]
295 self.size = self.packer.size
296 self.length_field = len_field_name
297
Ole Troan22674292019-10-21 18:59:11 +0200298 def pack(self, lst, kwargs=None):
299 if not lst:
Ole Troan31555a32018-10-22 09:30:26 +0200300 return b""
Ole Troan22674292019-10-21 18:59:11 +0200301 if len(lst) != kwargs[self.length_field]:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800302 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200303 "Variable length error, got: {} expected: {}".format(
304 len(lst), kwargs[self.length_field]
305 )
306 )
Ole Troana7564e82018-06-12 21:06:44 +0200307 # u8 array
Ole Troan86698fc2022-12-07 15:30:58 +0100308 if self.packer.size == 1 and self.field_type == "u8":
Ole Troan22674292019-10-21 18:59:11 +0200309 if isinstance(lst, list):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200310 return b"".join(lst)
Ole Troan22674292019-10-21 18:59:11 +0200311 return bytes(lst)
Ole Troana7564e82018-06-12 21:06:44 +0200312
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300313 b = bytearray()
Ole Troan22674292019-10-21 18:59:11 +0200314 for e in lst:
Ole Troana7564e82018-06-12 21:06:44 +0200315 b += self.packer.pack(e)
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300316 return bytes(b)
Ole Troana7564e82018-06-12 21:06:44 +0200317
Ole Troan0bcad322018-12-11 13:04:01 +0100318 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200319 # Return a list of arguments
Ole Troanc84cbad2018-09-06 22:58:05 +0200320 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200321
322 # u8 array
Ole Troan86698fc2022-12-07 15:30:58 +0100323 if self.packer.size == 1 and self.field_type == "u8":
Ole Troana7564e82018-06-12 21:06:44 +0200324 if result[self.index] == 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200325 return b"", 0
326 p = BaseTypes("u8", result[self.index])
Ole Troan0bcad322018-12-11 13:04:01 +0100327 return p.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200328
329 r = []
330 for e in range(result[self.index]):
Ole Troan0bcad322018-12-11 13:04:01 +0100331 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200332 r.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200333 offset += size
334 total += size
335 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200336
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500337 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200338 return "VLAList(name=%s, field_type=%s, " "len_field_name=%s, index=%s)" % (
339 self.name,
340 self.field_type,
341 self.length_field,
342 self.index,
343 )
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500344
Ole Troana7564e82018-06-12 21:06:44 +0200345
Ole Troan418ebb72019-11-19 04:38:20 +0100346class VLAList_legacy(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200347 def __init__(self, name, field_type):
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500348 self.name = name
349 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200350 self.packer = types[field_type]
351 self.size = self.packer.size
352
353 def pack(self, list, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200354 if self.packer.size == 1:
355 return bytes(list)
356
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300357 b = bytearray()
Ole Troana7564e82018-06-12 21:06:44 +0200358 for e in list:
359 b += self.packer.pack(e)
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300360 return bytes(b)
Ole Troana7564e82018-06-12 21:06:44 +0200361
Ole Troan0bcad322018-12-11 13:04:01 +0100362 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200363 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200364 # Return a list of arguments
365 if (len(data) - offset) % self.packer.size:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800366 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200367 "Legacy Variable Length Array length mismatch."
368 )
Ole Troana7564e82018-06-12 21:06:44 +0200369 elements = int((len(data) - offset) / self.packer.size)
370 r = []
Ole Troana7564e82018-06-12 21:06:44 +0200371 for e in range(elements):
Ole Troan0bcad322018-12-11 13:04:01 +0100372 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200373 r.append(x)
374 offset += self.packer.size
Ole Troanc84cbad2018-09-06 22:58:05 +0200375 total += size
376 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200377
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500378 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200379 return "VLAList_legacy(name=%s, field_type=%s)" % (self.name, self.field_type)
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500380
Ole Troana7564e82018-06-12 21:06:44 +0200381
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500382# Will change to IntEnum after 21.04 release
Ole Troan418ebb72019-11-19 04:38:20 +0100383class VPPEnumType(Packer):
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500384 output_class = IntFlag
385
Ole Troan40dc4b32019-10-22 14:01:53 +0200386 def __init__(self, name, msgdef, options=None):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200387 self.size = types["u32"].size
Ole Troan40dc4b32019-10-22 14:01:53 +0200388 self.name = name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200389 self.enumtype = "u32"
Ole Troan40dc4b32019-10-22 14:01:53 +0200390 self.msgdef = msgdef
Ole Troana7564e82018-06-12 21:06:44 +0200391 e_hash = {}
392 for f in msgdef:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200393 if type(f) is dict and "enumtype" in f:
394 if f["enumtype"] != "u32":
395 self.size = types[f["enumtype"]].size
396 self.enumtype = f["enumtype"]
Ole Troana7564e82018-06-12 21:06:44 +0200397 continue
398 ename, evalue = f
399 e_hash[ename] = evalue
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500400 self.enum = self.output_class(name, e_hash)
Ole Troana7564e82018-06-12 21:06:44 +0200401 types[name] = self
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500402 class_types[name] = self.__class__
Ole Troan40dc4b32019-10-22 14:01:53 +0200403 self.options = options
Ole Troan85465582019-04-30 10:04:36 +0200404
Ole Troana7564e82018-06-12 21:06:44 +0200405 def __getattr__(self, name):
406 return self.enum[name]
407
Paul Vinciguerraa089ae12019-07-08 15:41:07 -0400408 def __bool__(self):
Ole Troan0685da42018-10-16 14:42:50 +0200409 return True
410
Ole Troana7564e82018-06-12 21:06:44 +0200411 def pack(self, data, kwargs=None):
Ole Troan40dc4b32019-10-22 14:01:53 +0200412 if data is None: # Default to zero if not specified
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200413 if self.options and "default" in self.options:
414 data = self.options["default"]
Ole Troan40dc4b32019-10-22 14:01:53 +0200415 else:
416 data = 0
417
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100418 return types[self.enumtype].pack(data)
Ole Troana7564e82018-06-12 21:06:44 +0200419
Ole Troan0bcad322018-12-11 13:04:01 +0100420 def unpack(self, data, offset=0, result=None, ntc=False):
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100421 x, size = types[self.enumtype].unpack(data, offset)
Ole Troanc84cbad2018-09-06 22:58:05 +0200422 return self.enum(x), size
Ole Troana7564e82018-06-12 21:06:44 +0200423
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500424 @classmethod
425 def _get_packer_with_options(cls, f_type, options):
426 return cls(f_type, types[f_type].msgdef, options=options)
Ole Troana7564e82018-06-12 21:06:44 +0200427
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500428 def __repr__(self):
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500429 return "%s(name=%s, msgdef=%s, options=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200430 self.__class__.__name__,
431 self.name,
432 self.msgdef,
433 self.options,
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500434 )
435
Ole Troan418ebb72019-11-19 04:38:20 +0100436
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500437class VPPEnumFlagType(VPPEnumType):
438 output_class = IntFlag
439
440 def __init__(self, name, msgdef, options=None):
441 super(VPPEnumFlagType, self).__init__(name, msgdef, options)
442
443
Ole Troan418ebb72019-11-19 04:38:20 +0100444class VPPUnionType(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200445 def __init__(self, name, msgdef):
446 self.name = name
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500447 self.msgdef = msgdef
Ole Troana7564e82018-06-12 21:06:44 +0200448 self.size = 0
449 self.maxindex = 0
450 fields = []
451 self.packers = collections.OrderedDict()
452 for i, f in enumerate(msgdef):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200453 if type(f) is dict and "crc" in f:
454 self.crc = f["crc"]
Ole Troana7564e82018-06-12 21:06:44 +0200455 continue
456 f_type, f_name = f
457 if f_type not in types:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200458 logger.debug("Unknown union type {}".format(f_type))
459 raise VPPSerializerValueError("Unknown message type {}".format(f_type))
Ole Troana7564e82018-06-12 21:06:44 +0200460 fields.append(f_name)
461 size = types[f_type].size
462 self.packers[f_name] = types[f_type]
463 if size > self.size:
464 self.size = size
465 self.maxindex = i
466
467 types[name] = self
468 self.tuple = collections.namedtuple(name, fields, rename=True)
Ole Troana7564e82018-06-12 21:06:44 +0200469
Ole Troan31555a32018-10-22 09:30:26 +0200470 # Union of variable length?
Ole Troana7564e82018-06-12 21:06:44 +0200471 def pack(self, data, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +0200472 if not data:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200473 return b"\x00" * self.size
Ole Troan31555a32018-10-22 09:30:26 +0200474
Ole Troana7564e82018-06-12 21:06:44 +0200475 for k, v in data.items():
476 logger.debug("Key: {} Value: {}".format(k, v))
477 b = self.packers[k].pack(v, kwargs)
Ole Troana7564e82018-06-12 21:06:44 +0200478 break
479 r = bytearray(self.size)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200480 r[: len(b)] = b
Ole Troana7564e82018-06-12 21:06:44 +0200481 return r
482
Ole Troan0bcad322018-12-11 13:04:01 +0100483 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200484 r = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200485 maxsize = 0
Ole Troana7564e82018-06-12 21:06:44 +0200486 for k, p in self.packers.items():
Ole Troan0bcad322018-12-11 13:04:01 +0100487 x, size = p.unpack(data, offset, ntc=ntc)
Ole Troanc84cbad2018-09-06 22:58:05 +0200488 if size > maxsize:
489 maxsize = size
490 r.append(x)
491 return self.tuple._make(r), maxsize
Ole Troana7564e82018-06-12 21:06:44 +0200492
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500493 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200494 return "VPPUnionType(name=%s, msgdef=%r)" % (self.name, self.msgdef)
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500495
Ole Troana7564e82018-06-12 21:06:44 +0200496
Ole Troan418ebb72019-11-19 04:38:20 +0100497class VPPTypeAlias(Packer):
498 def __init__(self, name, msgdef, options=None):
Ole Troan0bcad322018-12-11 13:04:01 +0100499 self.name = name
Ole Troan418ebb72019-11-19 04:38:20 +0100500 self.msgdef = msgdef
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200501 t = vpp_get_type(msgdef["type"])
Ole Troan0bcad322018-12-11 13:04:01 +0100502 if not t:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200503 raise ValueError("No such type: {}".format(msgdef["type"]))
504 if "length" in msgdef:
505 if msgdef["length"] == 0:
Ole Troan0bcad322018-12-11 13:04:01 +0100506 raise ValueError()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200507 if msgdef["type"] == "u8":
508 self.packer = FixedList_u8(name, msgdef["type"], msgdef["length"])
Ole Troan0bcad322018-12-11 13:04:01 +0100509 self.size = self.packer.size
510 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200511 self.packer = FixedList(name, msgdef["type"], msgdef["length"])
Ole Troan8c8acc02018-11-27 10:05:23 +0100512 else:
Ole Troan0bcad322018-12-11 13:04:01 +0100513 self.packer = t
514 self.size = t.size
515
516 types[name] = self
Ole Troan75761b92019-09-11 17:49:08 +0200517 self.toplevelconversion = False
Ole Troan418ebb72019-11-19 04:38:20 +0100518 self.options = options
Ole Troan0bcad322018-12-11 13:04:01 +0100519
520 def pack(self, data, kwargs=None):
521 if data and conversion_required(data, self.name):
522 try:
523 return conversion_packer(data, self.name)
524 # Python 2 and 3 raises different exceptions from inet_pton
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200525 except (OSError, socket.error, TypeError):
Ole Troan0bcad322018-12-11 13:04:01 +0100526 pass
Ole Troan418ebb72019-11-19 04:38:20 +0100527 if data is None: # Default to zero if not specified
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200528 if self.options and "default" in self.options:
529 data = self.options["default"]
Ole Troan418ebb72019-11-19 04:38:20 +0100530 else:
531 data = 0
Ole Troan0bcad322018-12-11 13:04:01 +0100532
533 return self.packer.pack(data, kwargs)
534
Ole Troanda47f672019-11-24 15:22:55 +0100535 @staticmethod
536 def _get_packer_with_options(f_type, options):
537 return VPPTypeAlias(f_type, types[f_type].msgdef, options=options)
Ole Troan418ebb72019-11-19 04:38:20 +0100538
Ole Troan0bcad322018-12-11 13:04:01 +0100539 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troan418ebb72019-11-19 04:38:20 +0100540 if ntc is False and self.name in vpp_format.conversion_unpacker_table:
Ole Troan75761b92019-09-11 17:49:08 +0200541 # Disable type conversion for dependent types
542 ntc = True
543 self.toplevelconversion = True
Ole Troan0bcad322018-12-11 13:04:01 +0100544 t, size = self.packer.unpack(data, offset, result, ntc=ntc)
Ole Troan75761b92019-09-11 17:49:08 +0200545 if self.toplevelconversion:
546 self.toplevelconversion = False
Ole Troan0bcad322018-12-11 13:04:01 +0100547 return conversion_unpacker(t, self.name), size
548 return t, size
Ole Troan53fffa12018-11-13 12:36:56 +0100549
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500550 def __repr__(self):
551 return "VPPTypeAlias(name=%s, msgdef=%s, options=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200552 self.name,
553 self.msgdef,
554 self.options,
555 )
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500556
Ole Troan53fffa12018-11-13 12:36:56 +0100557
Ole Troan418ebb72019-11-19 04:38:20 +0100558class VPPType(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200559 # Set everything up to be able to pack / unpack
560 def __init__(self, name, msgdef):
561 self.name = name
562 self.msgdef = msgdef
563 self.packers = []
564 self.fields = []
565 self.fieldtypes = []
566 self.field_by_name = {}
567 size = 0
568 for i, f in enumerate(msgdef):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200569 if type(f) is dict and "crc" in f:
570 self.crc = f["crc"]
Ole Troana7564e82018-06-12 21:06:44 +0200571 continue
572 f_type, f_name = f[:2]
573 self.fields.append(f_name)
574 self.field_by_name[f_name] = None
575 self.fieldtypes.append(f_type)
576 if f_type not in types:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200577 logger.debug("Unknown type {}".format(f_type))
578 raise VPPSerializerValueError("Unknown message type {}".format(f_type))
Ole Troan9ac11382019-04-23 17:11:01 +0200579
Ole Troan85465582019-04-30 10:04:36 +0200580 fieldlen = len(f)
Ole Troan9ac11382019-04-23 17:11:01 +0200581 options = [x for x in f if type(x) is dict]
582 if len(options):
583 self.options = options[0]
Ole Troan85465582019-04-30 10:04:36 +0200584 fieldlen -= 1
Ole Troan9ac11382019-04-23 17:11:01 +0200585 else:
586 self.options = {}
Ole Troan85465582019-04-30 10:04:36 +0200587 if fieldlen == 3: # list
Ole Troana7564e82018-06-12 21:06:44 +0200588 list_elements = f[2]
589 if list_elements == 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200590 if f_type == "string":
Ole Troane5ff5a32019-08-23 22:55:18 +0200591 p = String(f_name, 0, self.options)
592 else:
593 p = VLAList_legacy(f_name, f_type)
Ole Troana7564e82018-06-12 21:06:44 +0200594 self.packers.append(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200595 elif f_type == "u8":
Ole Troana7564e82018-06-12 21:06:44 +0200596 p = FixedList_u8(f_name, f_type, list_elements)
597 self.packers.append(p)
598 size += p.size
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200599 elif f_type == "string":
Ole Troane5ff5a32019-08-23 22:55:18 +0200600 p = String(f_name, list_elements, self.options)
601 self.packers.append(p)
602 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200603 else:
604 p = FixedList(f_name, f_type, list_elements)
605 self.packers.append(p)
606 size += p.size
Ole Troan85465582019-04-30 10:04:36 +0200607 elif fieldlen == 4: # Variable length list
Ole Troan413f4a52018-11-28 11:36:05 +0100608 length_index = self.fields.index(f[3])
609 p = VLAList(f_name, f_type, f[3], length_index)
610 self.packers.append(p)
Ole Troana7564e82018-06-12 21:06:44 +0200611 else:
Ole Troan418ebb72019-11-19 04:38:20 +0100612 # default support for types that decay to basetype
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200613 if "default" in self.options:
Ole Troan418ebb72019-11-19 04:38:20 +0100614 p = self.get_packer_with_options(f_type, self.options)
Ole Troan40dc4b32019-10-22 14:01:53 +0200615 else:
616 p = types[f_type]
Ole Troan418ebb72019-11-19 04:38:20 +0100617
Ole Troan9ac11382019-04-23 17:11:01 +0200618 self.packers.append(p)
619 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200620 self.size = size
621 self.tuple = collections.namedtuple(name, self.fields, rename=True)
622 types[name] = self
Ole Troan75761b92019-09-11 17:49:08 +0200623 self.toplevelconversion = False
Ole Troana7564e82018-06-12 21:06:44 +0200624
625 def pack(self, data, kwargs=None):
626 if not kwargs:
627 kwargs = data
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300628 b = bytearray()
Ole Troan31555a32018-10-22 09:30:26 +0200629
Ole Troan0bcad322018-12-11 13:04:01 +0100630 # Try one of the format functions
631 if data and conversion_required(data, self.name):
632 return conversion_packer(data, self.name)
633
634 for i, a in enumerate(self.fields):
Ole Troan31555a32018-10-22 09:30:26 +0200635 if data and type(data) is not dict and a not in data:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800636 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200637 "Invalid argument: {} expected {}.{}".format(data, self.name, a)
638 )
Ole Troan31555a32018-10-22 09:30:26 +0200639
640 # Defaulting to zero.
641 if not data or a not in data: # Default to 0
642 arg = None
643 kwarg = None # No default for VLA
644 else:
645 arg = data[a]
646 kwarg = kwargs[a] if a in kwargs else None
Ole Troana7564e82018-06-12 21:06:44 +0200647 if isinstance(self.packers[i], VPPType):
Ole Troan0bcad322018-12-11 13:04:01 +0100648 b += self.packers[i].pack(arg, kwarg)
Ole Troana7564e82018-06-12 21:06:44 +0200649 else:
Ole Troan31555a32018-10-22 09:30:26 +0200650 b += self.packers[i].pack(arg, kwargs)
651
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300652 return bytes(b)
Ole Troana7564e82018-06-12 21:06:44 +0200653
Ole Troan0bcad322018-12-11 13:04:01 +0100654 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200655 # Return a list of arguments
656 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200657 total = 0
Ole Troan418ebb72019-11-19 04:38:20 +0100658 if ntc is False and self.name in vpp_format.conversion_unpacker_table:
Ole Troan75761b92019-09-11 17:49:08 +0200659 # Disable type conversion for dependent types
660 ntc = True
661 self.toplevelconversion = True
662
Ole Troana7564e82018-06-12 21:06:44 +0200663 for p in self.packers:
Ole Troan0bcad322018-12-11 13:04:01 +0100664 x, size = p.unpack(data, offset, result, ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200665 if type(x) is tuple and len(x) == 1:
666 x = x[0]
667 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200668 offset += size
669 total += size
670 t = self.tuple._make(result)
Ole Troan75761b92019-09-11 17:49:08 +0200671
672 if self.toplevelconversion:
673 self.toplevelconversion = False
Ole Troan0bcad322018-12-11 13:04:01 +0100674 t = conversion_unpacker(t, self.name)
Ole Troanc84cbad2018-09-06 22:58:05 +0200675 return t, total
Ole Troana7564e82018-06-12 21:06:44 +0200676
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500677 def __repr__(self):
678 return "%s(name=%s, msgdef=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200679 self.__class__.__name__,
680 self.name,
681 self.msgdef,
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500682 )
683
Ole Troana7564e82018-06-12 21:06:44 +0200684
685class VPPMessage(VPPType):
686 pass