blob: d7da457ea1ae2481fd562b97ce5b33d6a9e22e6d [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
308 # u8 array
309 if self.packer.size == 1:
Ole Troan22674292019-10-21 18:59:11 +0200310 if isinstance(lst, list):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200311 return b"".join(lst)
Ole Troan22674292019-10-21 18:59:11 +0200312 return bytes(lst)
Ole Troana7564e82018-06-12 21:06:44 +0200313
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300314 b = bytearray()
Ole Troan22674292019-10-21 18:59:11 +0200315 for e in lst:
Ole Troana7564e82018-06-12 21:06:44 +0200316 b += self.packer.pack(e)
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300317 return bytes(b)
Ole Troana7564e82018-06-12 21:06:44 +0200318
Ole Troan0bcad322018-12-11 13:04:01 +0100319 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200320 # Return a list of arguments
Ole Troanc84cbad2018-09-06 22:58:05 +0200321 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200322
323 # u8 array
324 if self.packer.size == 1:
325 if result[self.index] == 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200326 return b"", 0
327 p = BaseTypes("u8", result[self.index])
Ole Troan0bcad322018-12-11 13:04:01 +0100328 return p.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200329
330 r = []
331 for e in range(result[self.index]):
Ole Troan0bcad322018-12-11 13:04:01 +0100332 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200333 r.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200334 offset += size
335 total += size
336 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200337
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500338 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200339 return "VLAList(name=%s, field_type=%s, " "len_field_name=%s, index=%s)" % (
340 self.name,
341 self.field_type,
342 self.length_field,
343 self.index,
344 )
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500345
Ole Troana7564e82018-06-12 21:06:44 +0200346
Ole Troan418ebb72019-11-19 04:38:20 +0100347class VLAList_legacy(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200348 def __init__(self, name, field_type):
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500349 self.name = name
350 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200351 self.packer = types[field_type]
352 self.size = self.packer.size
353
354 def pack(self, list, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200355 if self.packer.size == 1:
356 return bytes(list)
357
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300358 b = bytearray()
Ole Troana7564e82018-06-12 21:06:44 +0200359 for e in list:
360 b += self.packer.pack(e)
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300361 return bytes(b)
Ole Troana7564e82018-06-12 21:06:44 +0200362
Ole Troan0bcad322018-12-11 13:04:01 +0100363 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200364 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200365 # Return a list of arguments
366 if (len(data) - offset) % self.packer.size:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800367 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200368 "Legacy Variable Length Array length mismatch."
369 )
Ole Troana7564e82018-06-12 21:06:44 +0200370 elements = int((len(data) - offset) / self.packer.size)
371 r = []
Ole Troana7564e82018-06-12 21:06:44 +0200372 for e in range(elements):
Ole Troan0bcad322018-12-11 13:04:01 +0100373 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200374 r.append(x)
375 offset += self.packer.size
Ole Troanc84cbad2018-09-06 22:58:05 +0200376 total += size
377 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200378
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500379 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200380 return "VLAList_legacy(name=%s, field_type=%s)" % (self.name, self.field_type)
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500381
Ole Troana7564e82018-06-12 21:06:44 +0200382
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500383# Will change to IntEnum after 21.04 release
Ole Troan418ebb72019-11-19 04:38:20 +0100384class VPPEnumType(Packer):
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500385 output_class = IntFlag
386
Ole Troan40dc4b32019-10-22 14:01:53 +0200387 def __init__(self, name, msgdef, options=None):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200388 self.size = types["u32"].size
Ole Troan40dc4b32019-10-22 14:01:53 +0200389 self.name = name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200390 self.enumtype = "u32"
Ole Troan40dc4b32019-10-22 14:01:53 +0200391 self.msgdef = msgdef
Ole Troana7564e82018-06-12 21:06:44 +0200392 e_hash = {}
393 for f in msgdef:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200394 if type(f) is dict and "enumtype" in f:
395 if f["enumtype"] != "u32":
396 self.size = types[f["enumtype"]].size
397 self.enumtype = f["enumtype"]
Ole Troana7564e82018-06-12 21:06:44 +0200398 continue
399 ename, evalue = f
400 e_hash[ename] = evalue
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500401 self.enum = self.output_class(name, e_hash)
Ole Troana7564e82018-06-12 21:06:44 +0200402 types[name] = self
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500403 class_types[name] = self.__class__
Ole Troan40dc4b32019-10-22 14:01:53 +0200404 self.options = options
Ole Troan85465582019-04-30 10:04:36 +0200405
Ole Troana7564e82018-06-12 21:06:44 +0200406 def __getattr__(self, name):
407 return self.enum[name]
408
Paul Vinciguerraa089ae12019-07-08 15:41:07 -0400409 def __bool__(self):
Ole Troan0685da42018-10-16 14:42:50 +0200410 return True
411
Ole Troana7564e82018-06-12 21:06:44 +0200412 def pack(self, data, kwargs=None):
Ole Troan40dc4b32019-10-22 14:01:53 +0200413 if data is None: # Default to zero if not specified
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200414 if self.options and "default" in self.options:
415 data = self.options["default"]
Ole Troan40dc4b32019-10-22 14:01:53 +0200416 else:
417 data = 0
418
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100419 return types[self.enumtype].pack(data)
Ole Troana7564e82018-06-12 21:06:44 +0200420
Ole Troan0bcad322018-12-11 13:04:01 +0100421 def unpack(self, data, offset=0, result=None, ntc=False):
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100422 x, size = types[self.enumtype].unpack(data, offset)
Ole Troanc84cbad2018-09-06 22:58:05 +0200423 return self.enum(x), size
Ole Troana7564e82018-06-12 21:06:44 +0200424
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500425 @classmethod
426 def _get_packer_with_options(cls, f_type, options):
427 return cls(f_type, types[f_type].msgdef, options=options)
Ole Troana7564e82018-06-12 21:06:44 +0200428
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500429 def __repr__(self):
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500430 return "%s(name=%s, msgdef=%s, options=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200431 self.__class__.__name__,
432 self.name,
433 self.msgdef,
434 self.options,
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500435 )
436
Ole Troan418ebb72019-11-19 04:38:20 +0100437
Paul Vinciguerra3825d932020-12-03 21:06:28 -0500438class VPPEnumFlagType(VPPEnumType):
439 output_class = IntFlag
440
441 def __init__(self, name, msgdef, options=None):
442 super(VPPEnumFlagType, self).__init__(name, msgdef, options)
443
444
Ole Troan418ebb72019-11-19 04:38:20 +0100445class VPPUnionType(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200446 def __init__(self, name, msgdef):
447 self.name = name
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500448 self.msgdef = msgdef
Ole Troana7564e82018-06-12 21:06:44 +0200449 self.size = 0
450 self.maxindex = 0
451 fields = []
452 self.packers = collections.OrderedDict()
453 for i, f in enumerate(msgdef):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200454 if type(f) is dict and "crc" in f:
455 self.crc = f["crc"]
Ole Troana7564e82018-06-12 21:06:44 +0200456 continue
457 f_type, f_name = f
458 if f_type not in types:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200459 logger.debug("Unknown union type {}".format(f_type))
460 raise VPPSerializerValueError("Unknown message type {}".format(f_type))
Ole Troana7564e82018-06-12 21:06:44 +0200461 fields.append(f_name)
462 size = types[f_type].size
463 self.packers[f_name] = types[f_type]
464 if size > self.size:
465 self.size = size
466 self.maxindex = i
467
468 types[name] = self
469 self.tuple = collections.namedtuple(name, fields, rename=True)
Ole Troana7564e82018-06-12 21:06:44 +0200470
Ole Troan31555a32018-10-22 09:30:26 +0200471 # Union of variable length?
Ole Troana7564e82018-06-12 21:06:44 +0200472 def pack(self, data, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +0200473 if not data:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200474 return b"\x00" * self.size
Ole Troan31555a32018-10-22 09:30:26 +0200475
Ole Troana7564e82018-06-12 21:06:44 +0200476 for k, v in data.items():
477 logger.debug("Key: {} Value: {}".format(k, v))
478 b = self.packers[k].pack(v, kwargs)
Ole Troana7564e82018-06-12 21:06:44 +0200479 break
480 r = bytearray(self.size)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200481 r[: len(b)] = b
Ole Troana7564e82018-06-12 21:06:44 +0200482 return r
483
Ole Troan0bcad322018-12-11 13:04:01 +0100484 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200485 r = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200486 maxsize = 0
Ole Troana7564e82018-06-12 21:06:44 +0200487 for k, p in self.packers.items():
Ole Troan0bcad322018-12-11 13:04:01 +0100488 x, size = p.unpack(data, offset, ntc=ntc)
Ole Troanc84cbad2018-09-06 22:58:05 +0200489 if size > maxsize:
490 maxsize = size
491 r.append(x)
492 return self.tuple._make(r), maxsize
Ole Troana7564e82018-06-12 21:06:44 +0200493
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500494 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200495 return "VPPUnionType(name=%s, msgdef=%r)" % (self.name, self.msgdef)
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500496
Ole Troana7564e82018-06-12 21:06:44 +0200497
Ole Troan418ebb72019-11-19 04:38:20 +0100498class VPPTypeAlias(Packer):
499 def __init__(self, name, msgdef, options=None):
Ole Troan0bcad322018-12-11 13:04:01 +0100500 self.name = name
Ole Troan418ebb72019-11-19 04:38:20 +0100501 self.msgdef = msgdef
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200502 t = vpp_get_type(msgdef["type"])
Ole Troan0bcad322018-12-11 13:04:01 +0100503 if not t:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200504 raise ValueError("No such type: {}".format(msgdef["type"]))
505 if "length" in msgdef:
506 if msgdef["length"] == 0:
Ole Troan0bcad322018-12-11 13:04:01 +0100507 raise ValueError()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200508 if msgdef["type"] == "u8":
509 self.packer = FixedList_u8(name, msgdef["type"], msgdef["length"])
Ole Troan0bcad322018-12-11 13:04:01 +0100510 self.size = self.packer.size
511 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200512 self.packer = FixedList(name, msgdef["type"], msgdef["length"])
Ole Troan8c8acc02018-11-27 10:05:23 +0100513 else:
Ole Troan0bcad322018-12-11 13:04:01 +0100514 self.packer = t
515 self.size = t.size
516
517 types[name] = self
Ole Troan75761b92019-09-11 17:49:08 +0200518 self.toplevelconversion = False
Ole Troan418ebb72019-11-19 04:38:20 +0100519 self.options = options
Ole Troan0bcad322018-12-11 13:04:01 +0100520
521 def pack(self, data, kwargs=None):
522 if data and conversion_required(data, self.name):
523 try:
524 return conversion_packer(data, self.name)
525 # Python 2 and 3 raises different exceptions from inet_pton
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200526 except (OSError, socket.error, TypeError):
Ole Troan0bcad322018-12-11 13:04:01 +0100527 pass
Ole Troan418ebb72019-11-19 04:38:20 +0100528 if data is None: # Default to zero if not specified
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200529 if self.options and "default" in self.options:
530 data = self.options["default"]
Ole Troan418ebb72019-11-19 04:38:20 +0100531 else:
532 data = 0
Ole Troan0bcad322018-12-11 13:04:01 +0100533
534 return self.packer.pack(data, kwargs)
535
Ole Troanda47f672019-11-24 15:22:55 +0100536 @staticmethod
537 def _get_packer_with_options(f_type, options):
538 return VPPTypeAlias(f_type, types[f_type].msgdef, options=options)
Ole Troan418ebb72019-11-19 04:38:20 +0100539
Ole Troan0bcad322018-12-11 13:04:01 +0100540 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troan418ebb72019-11-19 04:38:20 +0100541 if ntc is False and self.name in vpp_format.conversion_unpacker_table:
Ole Troan75761b92019-09-11 17:49:08 +0200542 # Disable type conversion for dependent types
543 ntc = True
544 self.toplevelconversion = True
Ole Troan0bcad322018-12-11 13:04:01 +0100545 t, size = self.packer.unpack(data, offset, result, ntc=ntc)
Ole Troan75761b92019-09-11 17:49:08 +0200546 if self.toplevelconversion:
547 self.toplevelconversion = False
Ole Troan0bcad322018-12-11 13:04:01 +0100548 return conversion_unpacker(t, self.name), size
549 return t, size
Ole Troan53fffa12018-11-13 12:36:56 +0100550
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500551 def __repr__(self):
552 return "VPPTypeAlias(name=%s, msgdef=%s, options=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200553 self.name,
554 self.msgdef,
555 self.options,
556 )
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500557
Ole Troan53fffa12018-11-13 12:36:56 +0100558
Ole Troan418ebb72019-11-19 04:38:20 +0100559class VPPType(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200560 # Set everything up to be able to pack / unpack
561 def __init__(self, name, msgdef):
562 self.name = name
563 self.msgdef = msgdef
564 self.packers = []
565 self.fields = []
566 self.fieldtypes = []
567 self.field_by_name = {}
568 size = 0
569 for i, f in enumerate(msgdef):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200570 if type(f) is dict and "crc" in f:
571 self.crc = f["crc"]
Ole Troana7564e82018-06-12 21:06:44 +0200572 continue
573 f_type, f_name = f[:2]
574 self.fields.append(f_name)
575 self.field_by_name[f_name] = None
576 self.fieldtypes.append(f_type)
577 if f_type not in types:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200578 logger.debug("Unknown type {}".format(f_type))
579 raise VPPSerializerValueError("Unknown message type {}".format(f_type))
Ole Troan9ac11382019-04-23 17:11:01 +0200580
Ole Troan85465582019-04-30 10:04:36 +0200581 fieldlen = len(f)
Ole Troan9ac11382019-04-23 17:11:01 +0200582 options = [x for x in f if type(x) is dict]
583 if len(options):
584 self.options = options[0]
Ole Troan85465582019-04-30 10:04:36 +0200585 fieldlen -= 1
Ole Troan9ac11382019-04-23 17:11:01 +0200586 else:
587 self.options = {}
Ole Troan85465582019-04-30 10:04:36 +0200588 if fieldlen == 3: # list
Ole Troana7564e82018-06-12 21:06:44 +0200589 list_elements = f[2]
590 if list_elements == 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200591 if f_type == "string":
Ole Troane5ff5a32019-08-23 22:55:18 +0200592 p = String(f_name, 0, self.options)
593 else:
594 p = VLAList_legacy(f_name, f_type)
Ole Troana7564e82018-06-12 21:06:44 +0200595 self.packers.append(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200596 elif f_type == "u8":
Ole Troana7564e82018-06-12 21:06:44 +0200597 p = FixedList_u8(f_name, f_type, list_elements)
598 self.packers.append(p)
599 size += p.size
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200600 elif f_type == "string":
Ole Troane5ff5a32019-08-23 22:55:18 +0200601 p = String(f_name, list_elements, self.options)
602 self.packers.append(p)
603 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200604 else:
605 p = FixedList(f_name, f_type, list_elements)
606 self.packers.append(p)
607 size += p.size
Ole Troan85465582019-04-30 10:04:36 +0200608 elif fieldlen == 4: # Variable length list
Ole Troan413f4a52018-11-28 11:36:05 +0100609 length_index = self.fields.index(f[3])
610 p = VLAList(f_name, f_type, f[3], length_index)
611 self.packers.append(p)
Ole Troana7564e82018-06-12 21:06:44 +0200612 else:
Ole Troan418ebb72019-11-19 04:38:20 +0100613 # default support for types that decay to basetype
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200614 if "default" in self.options:
Ole Troan418ebb72019-11-19 04:38:20 +0100615 p = self.get_packer_with_options(f_type, self.options)
Ole Troan40dc4b32019-10-22 14:01:53 +0200616 else:
617 p = types[f_type]
Ole Troan418ebb72019-11-19 04:38:20 +0100618
Ole Troan9ac11382019-04-23 17:11:01 +0200619 self.packers.append(p)
620 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200621
622 self.size = size
623 self.tuple = collections.namedtuple(name, self.fields, rename=True)
624 types[name] = self
Ole Troan75761b92019-09-11 17:49:08 +0200625 self.toplevelconversion = False
Ole Troana7564e82018-06-12 21:06:44 +0200626
627 def pack(self, data, kwargs=None):
628 if not kwargs:
629 kwargs = data
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300630 b = bytearray()
Ole Troan31555a32018-10-22 09:30:26 +0200631
Ole Troan0bcad322018-12-11 13:04:01 +0100632 # Try one of the format functions
633 if data and conversion_required(data, self.name):
634 return conversion_packer(data, self.name)
635
636 for i, a in enumerate(self.fields):
Ole Troan31555a32018-10-22 09:30:26 +0200637 if data and type(data) is not dict and a not in data:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800638 raise VPPSerializerValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200639 "Invalid argument: {} expected {}.{}".format(data, self.name, a)
640 )
Ole Troan31555a32018-10-22 09:30:26 +0200641
642 # Defaulting to zero.
643 if not data or a not in data: # Default to 0
644 arg = None
645 kwarg = None # No default for VLA
646 else:
647 arg = data[a]
648 kwarg = kwargs[a] if a in kwargs else None
Ole Troana7564e82018-06-12 21:06:44 +0200649 if isinstance(self.packers[i], VPPType):
Ole Troan0bcad322018-12-11 13:04:01 +0100650 b += self.packers[i].pack(arg, kwarg)
Ole Troana7564e82018-06-12 21:06:44 +0200651 else:
Ole Troan31555a32018-10-22 09:30:26 +0200652 b += self.packers[i].pack(arg, kwargs)
653
Viktor Velichkin7c3096d2022-05-31 22:12:15 +0300654 return bytes(b)
Ole Troana7564e82018-06-12 21:06:44 +0200655
Ole Troan0bcad322018-12-11 13:04:01 +0100656 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200657 # Return a list of arguments
658 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200659 total = 0
Ole Troan418ebb72019-11-19 04:38:20 +0100660 if ntc is False and self.name in vpp_format.conversion_unpacker_table:
Ole Troan75761b92019-09-11 17:49:08 +0200661 # Disable type conversion for dependent types
662 ntc = True
663 self.toplevelconversion = True
664
Ole Troana7564e82018-06-12 21:06:44 +0200665 for p in self.packers:
Ole Troan0bcad322018-12-11 13:04:01 +0100666 x, size = p.unpack(data, offset, result, ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200667 if type(x) is tuple and len(x) == 1:
668 x = x[0]
669 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200670 offset += size
671 total += size
672 t = self.tuple._make(result)
Ole Troan75761b92019-09-11 17:49:08 +0200673
674 if self.toplevelconversion:
675 self.toplevelconversion = False
Ole Troan0bcad322018-12-11 13:04:01 +0100676 t = conversion_unpacker(t, self.name)
Ole Troanc84cbad2018-09-06 22:58:05 +0200677 return t, total
Ole Troana7564e82018-06-12 21:06:44 +0200678
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500679 def __repr__(self):
680 return "%s(name=%s, msgdef=%s)" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200681 self.__class__.__name__,
682 self.name,
683 self.msgdef,
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500684 )
685
Ole Troana7564e82018-06-12 21:06:44 +0200686
687class VPPMessage(VPPType):
688 pass