blob: f46f207300fdbb2758e8a459ac426a5f4627c631 [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#
30logger = logging.getLogger(__name__)
31
Ole Troan0bcad322018-12-11 13:04:01 +010032if sys.version[0] == '2':
Ole Troan8006c6a2018-12-17 12:02:26 +010033 def check(d): type(d) is dict
Ole Troan0bcad322018-12-11 13:04:01 +010034else:
Ole Troan8006c6a2018-12-17 12:02:26 +010035 def check(d): type(d) is dict or type(d) is bytes
36
Ole Troan0bcad322018-12-11 13:04:01 +010037
38def conversion_required(data, field_type):
39 if check(data):
40 return False
41 try:
42 if type(data).__name__ in vpp_format.conversion_table[field_type]:
43 return True
44 except KeyError:
45 return False
46
47
48def conversion_packer(data, field_type):
49 t = type(data).__name__
50 return types[field_type].pack(vpp_format.
51 conversion_table[field_type][t](data))
52
53
54def conversion_unpacker(data, field_type):
55 if field_type not in vpp_format.conversion_unpacker_table:
56 return data
57 return vpp_format.conversion_unpacker_table[field_type](data)
58
Ole Troana7564e82018-06-12 21:06:44 +020059
Ole Troan418ebb72019-11-19 04:38:20 +010060# TODO: post 20.01, remove inherit from object.
61class Packer(object):
62 options = {}
63
64 def pack(self, data, kwargs):
65 raise NotImplementedError
66
67 def unpack(self, data, offset, result=None, ntc=False):
68 raise NotImplementedError
69
70 # override as appropriate in subclasses
Ole Troanda47f672019-11-24 15:22:55 +010071 @staticmethod
72 def _get_packer_with_options(f_type, options):
Ole Troan418ebb72019-11-19 04:38:20 +010073 return types[f_type]
74
75 def get_packer_with_options(self, f_type, options):
76 if options is not None:
77 try:
Ole Troanda47f672019-11-24 15:22:55 +010078 c = types[f_type].__class__
79 return c._get_packer_with_options(f_type, options)
Ole Troan418ebb72019-11-19 04:38:20 +010080 except IndexError:
81 raise VPPSerializerValueError(
82 "Options not supported for {}{} ({})".
Ole Troanda47f672019-11-24 15:22:55 +010083 format(f_type, types[f_type].__class__,
84 options))
Ole Troan418ebb72019-11-19 04:38:20 +010085
86
87class BaseTypes(Packer):
Ole Troan85465582019-04-30 10:04:36 +020088 def __init__(self, type, elements=0, options=None):
Paul Vinciguerra14b0b472019-11-23 23:49:57 -050089 self._type = type
90 self._elements = elements
Ole Troana7564e82018-06-12 21:06:44 +020091 base_types = {'u8': '>B',
Paul Vinciguerra6ce0f352019-07-02 13:06:21 -040092 'i8': '>b',
Ole Troan413f4a52018-11-28 11:36:05 +010093 'string': '>s',
Ole Troana7564e82018-06-12 21:06:44 +020094 'u16': '>H',
Paul Vinciguerra6ce0f352019-07-02 13:06:21 -040095 'i16': '>h',
Ole Troana7564e82018-06-12 21:06:44 +020096 'u32': '>I',
97 'i32': '>i',
98 'u64': '>Q',
Paul Vinciguerra6ce0f352019-07-02 13:06:21 -040099 'i64': '>q',
Paul Vinciguerra888640a2019-05-14 21:01:28 -0400100 'f64': '=d',
Ole Troanf47e9b62018-10-16 15:14:03 +0200101 'bool': '>?',
Ole Troana7564e82018-06-12 21:06:44 +0200102 'header': '>HI'}
103
Ole Troan413f4a52018-11-28 11:36:05 +0100104 if elements > 0 and (type == 'u8' or type == 'string'):
Ole Troana7564e82018-06-12 21:06:44 +0200105 self.packer = struct.Struct('>%ss' % elements)
106 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
Ole Troan85465582019-04-30 10:04:36 +0200113 if self.options and 'default' in self.options:
114 data = self.options['default']
115 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):
127 return "BaseTypes(type=%s, elements=%s, options=%s)" % (self._type,
128 self._elements,
129 self.options)
130
Ole Troan418ebb72019-11-19 04:38:20 +0100131
132class String(Packer):
Ole Troane5ff5a32019-08-23 22:55:18 +0200133 def __init__(self, name, num, options):
134 self.name = name
135 self.num = num
Ole Troan413f4a52018-11-28 11:36:05 +0100136 self.size = 1
137 self.length_field_packer = BaseTypes('u32')
Ole Troane5ff5a32019-08-23 22:55:18 +0200138 self.limit = options['limit'] if 'limit' in options else num
139 self.fixed = True if num else False
140 if self.fixed and not self.limit:
Ole Troan9ac11382019-04-23 17:11:01 +0200141 raise VPPSerializerValueError(
Paul Vinciguerracebed412020-12-01 23:45:38 -0500142 "Invalid combination for: {}, {} fixed:{} limit:{}".
143 format(name, options, self.fixed, self.limit))
Ole Troan9ac11382019-04-23 17:11:01 +0200144
Ole Troane5ff5a32019-08-23 22:55:18 +0200145 def pack(self, list, kwargs=None):
146 if not list:
147 if self.fixed:
148 return b"\x00" * self.limit
149 return self.length_field_packer.pack(0) + b""
150 if self.limit and len(list) > self.limit - 1:
151 raise VPPSerializerValueError(
152 "Invalid argument length for: {}, {} maximum {}".
153 format(list, len(list), self.limit - 1))
154 if self.fixed:
155 return list.encode('ascii').ljust(self.limit, b'\x00')
156 return self.length_field_packer.pack(len(list)) + list.encode('ascii')
Ole Troan413f4a52018-11-28 11:36:05 +0100157
158 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troane5ff5a32019-08-23 22:55:18 +0200159 if self.fixed:
160 p = BaseTypes('u8', self.num)
161 s = p.unpack(data, offset)
162 s2 = s[0].split(b'\0', 1)[0]
163 return (s2.decode('ascii'), self.num)
164
Ole Troan413f4a52018-11-28 11:36:05 +0100165 length, length_field_size = self.length_field_packer.unpack(data,
166 offset)
167 if length == 0:
Ole Troane5ff5a32019-08-23 22:55:18 +0200168 return '', 0
Ole Troan413f4a52018-11-28 11:36:05 +0100169 p = BaseTypes('u8', length)
170 x, size = p.unpack(data, offset + length_field_size)
Ole Troan33a58172019-09-04 09:12:29 +0200171 return (x.decode('ascii', errors='replace'), size + length_field_size)
Ole Troan413f4a52018-11-28 11:36:05 +0100172
173
Paul Vinciguerraba2c7fa2019-11-23 21:02:27 -0500174types = {'u8': BaseTypes('u8'), 'i8': BaseTypes('i8'),
175 'u16': BaseTypes('u16'), 'i16': BaseTypes('i16'),
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800176 'u32': BaseTypes('u32'), 'i32': BaseTypes('i32'),
Paul Vinciguerraba2c7fa2019-11-23 21:02:27 -0500177 'u64': BaseTypes('u64'), 'i64': BaseTypes('i64'),
178 'f64': BaseTypes('f64'),
Ole Troan9ac11382019-04-23 17:11:01 +0200179 'bool': BaseTypes('bool'), 'string': String}
Ole Troana7564e82018-06-12 21:06:44 +0200180
Ole Troan40dc4b32019-10-22 14:01:53 +0200181class_types = {}
Ole Troana7564e82018-06-12 21:06:44 +0200182
Ole Troan418ebb72019-11-19 04:38:20 +0100183
Ole Troan0685da42018-10-16 14:42:50 +0200184def vpp_get_type(name):
185 try:
186 return types[name]
187 except KeyError:
188 return None
189
190
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800191class VPPSerializerValueError(ValueError):
192 pass
193
194
Ole Troan418ebb72019-11-19 04:38:20 +0100195class FixedList_u8(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200196 def __init__(self, name, field_type, num):
197 self.name = name
198 self.num = num
199 self.packer = BaseTypes(field_type, num)
200 self.size = self.packer.size
Ole Troan413f4a52018-11-28 11:36:05 +0100201 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200202
Ole Troan0bcad322018-12-11 13:04:01 +0100203 def pack(self, data, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200204 """Packs a fixed length bytestring. Left-pads with zeros
205 if input data is too short."""
Ole Troan0bcad322018-12-11 13:04:01 +0100206 if not data:
Ole Troan31555a32018-10-22 09:30:26 +0200207 return b'\x00' * self.size
Ole Troan0bcad322018-12-11 13:04:01 +0100208
209 if len(data) > self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800210 raise VPPSerializerValueError(
211 'Fixed list length error for "{}", got: {}'
212 ' expected: {}'
Ole Troan0bcad322018-12-11 13:04:01 +0100213 .format(self.name, len(data), self.num))
Ole Troana7564e82018-06-12 21:06:44 +0200214
Ole Troan64e978b2019-10-17 21:40:36 +0200215 try:
216 return self.packer.pack(data)
217 except struct.error:
218 raise VPPSerializerValueError(
219 'Packing failed for "{}" {}'
220 .format(self.name, kwargs))
Ole Troan418ebb72019-11-19 04:38:20 +0100221
Ole Troan0bcad322018-12-11 13:04:01 +0100222 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200223 if len(data[offset:]) < self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800224 raise VPPSerializerValueError(
225 'Invalid array length for "{}" got {}'
226 ' expected {}'
227 .format(self.name, len(data[offset:]), self.num))
Ole Troana7564e82018-06-12 21:06:44 +0200228 return self.packer.unpack(data, offset)
229
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500230 def __repr__(self):
231 return "FixedList_u8(name=%s, field_type=%s, num=%s)" % (
232 self.name, self.field_type, self.num
233 )
234
Ole Troana7564e82018-06-12 21:06:44 +0200235
Ole Troan418ebb72019-11-19 04:38:20 +0100236class FixedList(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200237 def __init__(self, name, field_type, num):
238 self.num = num
239 self.packer = types[field_type]
240 self.size = self.packer.size * num
Ole Troan0bcad322018-12-11 13:04:01 +0100241 self.name = name
242 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200243
244 def pack(self, list, kwargs):
Ole Troana7564e82018-06-12 21:06:44 +0200245 if len(list) != self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800246 raise VPPSerializerValueError(
247 'Fixed list length error, got: {} expected: {}'
248 .format(len(list), self.num))
Ole Troana7564e82018-06-12 21:06:44 +0200249 b = bytes()
250 for e in list:
251 b += self.packer.pack(e)
252 return b
253
Ole Troan0bcad322018-12-11 13:04:01 +0100254 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200255 # Return a list of arguments
256 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200257 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200258 for e in range(self.num):
Ole Troan0bcad322018-12-11 13:04:01 +0100259 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200260 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200261 offset += size
262 total += size
263 return result, total
Ole Troana7564e82018-06-12 21:06:44 +0200264
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500265 def __repr__(self):
Paul Vinciguerra99fbf052019-11-26 14:15:53 -0500266 return "FixedList(name=%s, field_type=%s, num=%s)" % (
Ole Troanda47f672019-11-24 15:22:55 +0100267 self.name, self.field_type, self.num)
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500268
Ole Troana7564e82018-06-12 21:06:44 +0200269
Ole Troan418ebb72019-11-19 04:38:20 +0100270class VLAList(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200271 def __init__(self, name, field_type, len_field_name, index):
Ole Troanc84cbad2018-09-06 22:58:05 +0200272 self.name = name
Ole Troan413f4a52018-11-28 11:36:05 +0100273 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200274 self.index = index
275 self.packer = types[field_type]
276 self.size = self.packer.size
277 self.length_field = len_field_name
278
Ole Troan22674292019-10-21 18:59:11 +0200279 def pack(self, lst, kwargs=None):
280 if not lst:
Ole Troan31555a32018-10-22 09:30:26 +0200281 return b""
Ole Troan22674292019-10-21 18:59:11 +0200282 if len(lst) != kwargs[self.length_field]:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800283 raise VPPSerializerValueError(
284 'Variable length error, got: {} expected: {}'
Ole Troan22674292019-10-21 18:59:11 +0200285 .format(len(lst), kwargs[self.length_field]))
Ole Troana7564e82018-06-12 21:06:44 +0200286
287 # u8 array
288 if self.packer.size == 1:
Ole Troan22674292019-10-21 18:59:11 +0200289 if isinstance(lst, list):
290 return b''.join(lst)
291 return bytes(lst)
Ole Troana7564e82018-06-12 21:06:44 +0200292
Ole Troan22674292019-10-21 18:59:11 +0200293 b = bytes()
294 for e in lst:
Ole Troana7564e82018-06-12 21:06:44 +0200295 b += self.packer.pack(e)
296 return b
297
Ole Troan0bcad322018-12-11 13:04:01 +0100298 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200299 # Return a list of arguments
Ole Troanc84cbad2018-09-06 22:58:05 +0200300 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200301
302 # u8 array
303 if self.packer.size == 1:
304 if result[self.index] == 0:
Ole Troanc84cbad2018-09-06 22:58:05 +0200305 return b'', 0
Ole Troana7564e82018-06-12 21:06:44 +0200306 p = BaseTypes('u8', result[self.index])
Ole Troan0bcad322018-12-11 13:04:01 +0100307 return p.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200308
309 r = []
310 for e in range(result[self.index]):
Ole Troan0bcad322018-12-11 13:04:01 +0100311 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200312 r.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200313 offset += size
314 total += size
315 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200316
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500317 def __repr__(self):
318 return "VLAList(name=%s, field_type=%s, " \
319 "len_field_name=%s, index=%s)" % (
Ole Troanda47f672019-11-24 15:22:55 +0100320 self.name, self.field_type, self.length_field, self.index
321 )
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500322
Ole Troana7564e82018-06-12 21:06:44 +0200323
Ole Troan418ebb72019-11-19 04:38:20 +0100324class VLAList_legacy(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200325 def __init__(self, name, field_type):
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500326 self.name = name
327 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200328 self.packer = types[field_type]
329 self.size = self.packer.size
330
331 def pack(self, list, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200332 if self.packer.size == 1:
333 return bytes(list)
334
335 b = bytes()
336 for e in list:
337 b += self.packer.pack(e)
338 return b
339
Ole Troan0bcad322018-12-11 13:04:01 +0100340 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200341 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200342 # Return a list of arguments
343 if (len(data) - offset) % self.packer.size:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800344 raise VPPSerializerValueError(
345 'Legacy Variable Length Array length mismatch.')
Ole Troana7564e82018-06-12 21:06:44 +0200346 elements = int((len(data) - offset) / self.packer.size)
347 r = []
Ole Troana7564e82018-06-12 21:06:44 +0200348 for e in range(elements):
Ole Troan0bcad322018-12-11 13:04:01 +0100349 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200350 r.append(x)
351 offset += self.packer.size
Ole Troanc84cbad2018-09-06 22:58:05 +0200352 total += size
353 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200354
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500355 def __repr__(self):
356 return "VLAList_legacy(name=%s, field_type=%s)" % (
357 self.name, self.field_type
358 )
359
Ole Troana7564e82018-06-12 21:06:44 +0200360
Ole Troan418ebb72019-11-19 04:38:20 +0100361class VPPEnumType(Packer):
Ole Troan40dc4b32019-10-22 14:01:53 +0200362 def __init__(self, name, msgdef, options=None):
Ole Troana7564e82018-06-12 21:06:44 +0200363 self.size = types['u32'].size
Ole Troan40dc4b32019-10-22 14:01:53 +0200364 self.name = name
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100365 self.enumtype = 'u32'
Ole Troan40dc4b32019-10-22 14:01:53 +0200366 self.msgdef = msgdef
Ole Troana7564e82018-06-12 21:06:44 +0200367 e_hash = {}
368 for f in msgdef:
369 if type(f) is dict and 'enumtype' in f:
370 if f['enumtype'] != 'u32':
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100371 self.size = types[f['enumtype']].size
372 self.enumtype = f['enumtype']
Ole Troana7564e82018-06-12 21:06:44 +0200373 continue
374 ename, evalue = f
375 e_hash[ename] = evalue
Ole Troan48ae19e2019-03-07 11:28:32 +0100376 self.enum = IntFlag(name, e_hash)
Ole Troana7564e82018-06-12 21:06:44 +0200377 types[name] = self
Ole Troan40dc4b32019-10-22 14:01:53 +0200378 class_types[name] = VPPEnumType
379 self.options = options
Ole Troan85465582019-04-30 10:04:36 +0200380
Ole Troana7564e82018-06-12 21:06:44 +0200381 def __getattr__(self, name):
382 return self.enum[name]
383
Paul Vinciguerraa089ae12019-07-08 15:41:07 -0400384 def __bool__(self):
Ole Troan0685da42018-10-16 14:42:50 +0200385 return True
386
Ole Troan418ebb72019-11-19 04:38:20 +0100387 # TODO: Remove post 20.01.
Paul Vinciguerraa089ae12019-07-08 15:41:07 -0400388 if sys.version[0] == '2':
389 __nonzero__ = __bool__
390
Ole Troana7564e82018-06-12 21:06:44 +0200391 def pack(self, data, kwargs=None):
Ole Troan40dc4b32019-10-22 14:01:53 +0200392 if data is None: # Default to zero if not specified
393 if self.options and 'default' in self.options:
394 data = self.options['default']
395 else:
396 data = 0
397
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100398 return types[self.enumtype].pack(data)
Ole Troana7564e82018-06-12 21:06:44 +0200399
Ole Troan0bcad322018-12-11 13:04:01 +0100400 def unpack(self, data, offset=0, result=None, ntc=False):
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100401 x, size = types[self.enumtype].unpack(data, offset)
Ole Troanc84cbad2018-09-06 22:58:05 +0200402 return self.enum(x), size
Ole Troana7564e82018-06-12 21:06:44 +0200403
Ole Troanda47f672019-11-24 15:22:55 +0100404 @staticmethod
405 def _get_packer_with_options(f_type, options):
406 return VPPEnumType(f_type, types[f_type].msgdef, options=options)
Ole Troana7564e82018-06-12 21:06:44 +0200407
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500408 def __repr__(self):
409 return "VPPEnumType(name=%s, msgdef=%s, options=%s)" % (
410 self.name, self.msgdef, self.options
411 )
412
Ole Troan418ebb72019-11-19 04:38:20 +0100413
414class VPPUnionType(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200415 def __init__(self, name, msgdef):
416 self.name = name
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500417 self.msgdef = msgdef
Ole Troana7564e82018-06-12 21:06:44 +0200418 self.size = 0
419 self.maxindex = 0
420 fields = []
421 self.packers = collections.OrderedDict()
422 for i, f in enumerate(msgdef):
423 if type(f) is dict and 'crc' in f:
424 self.crc = f['crc']
425 continue
426 f_type, f_name = f
427 if f_type not in types:
428 logger.debug('Unknown union type {}'.format(f_type))
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800429 raise VPPSerializerValueError(
430 'Unknown message type {}'.format(f_type))
Ole Troana7564e82018-06-12 21:06:44 +0200431 fields.append(f_name)
432 size = types[f_type].size
433 self.packers[f_name] = types[f_type]
434 if size > self.size:
435 self.size = size
436 self.maxindex = i
437
438 types[name] = self
439 self.tuple = collections.namedtuple(name, fields, rename=True)
Ole Troana7564e82018-06-12 21:06:44 +0200440
Ole Troan31555a32018-10-22 09:30:26 +0200441 # Union of variable length?
Ole Troana7564e82018-06-12 21:06:44 +0200442 def pack(self, data, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +0200443 if not data:
444 return b'\x00' * self.size
445
Ole Troana7564e82018-06-12 21:06:44 +0200446 for k, v in data.items():
447 logger.debug("Key: {} Value: {}".format(k, v))
448 b = self.packers[k].pack(v, kwargs)
Ole Troana7564e82018-06-12 21:06:44 +0200449 break
450 r = bytearray(self.size)
Ole Troanb199e982018-08-02 19:19:21 +0200451 r[:len(b)] = b
Ole Troana7564e82018-06-12 21:06:44 +0200452 return r
453
Ole Troan0bcad322018-12-11 13:04:01 +0100454 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200455 r = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200456 maxsize = 0
Ole Troana7564e82018-06-12 21:06:44 +0200457 for k, p in self.packers.items():
Ole Troan0bcad322018-12-11 13:04:01 +0100458 x, size = p.unpack(data, offset, ntc=ntc)
Ole Troanc84cbad2018-09-06 22:58:05 +0200459 if size > maxsize:
460 maxsize = size
461 r.append(x)
462 return self.tuple._make(r), maxsize
Ole Troana7564e82018-06-12 21:06:44 +0200463
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500464 def __repr__(self):
465 return"VPPUnionType(name=%s, msgdef=%r)" % (self.name, self.msgdef)
466
Ole Troana7564e82018-06-12 21:06:44 +0200467
Ole Troan418ebb72019-11-19 04:38:20 +0100468class VPPTypeAlias(Packer):
469 def __init__(self, name, msgdef, options=None):
Ole Troan0bcad322018-12-11 13:04:01 +0100470 self.name = name
Ole Troan418ebb72019-11-19 04:38:20 +0100471 self.msgdef = msgdef
Ole Troan0bcad322018-12-11 13:04:01 +0100472 t = vpp_get_type(msgdef['type'])
473 if not t:
Ole Troan75761b92019-09-11 17:49:08 +0200474 raise ValueError('No such type: {}'.format(msgdef['type']))
Ole Troan0bcad322018-12-11 13:04:01 +0100475 if 'length' in msgdef:
476 if msgdef['length'] == 0:
477 raise ValueError()
478 if msgdef['type'] == 'u8':
479 self.packer = FixedList_u8(name, msgdef['type'],
480 msgdef['length'])
481 self.size = self.packer.size
482 else:
483 self.packer = FixedList(name, msgdef['type'], msgdef['length'])
Ole Troan8c8acc02018-11-27 10:05:23 +0100484 else:
Ole Troan0bcad322018-12-11 13:04:01 +0100485 self.packer = t
486 self.size = t.size
487
488 types[name] = self
Ole Troan75761b92019-09-11 17:49:08 +0200489 self.toplevelconversion = False
Ole Troan418ebb72019-11-19 04:38:20 +0100490 self.options = options
Ole Troan0bcad322018-12-11 13:04:01 +0100491
492 def pack(self, data, kwargs=None):
493 if data and conversion_required(data, self.name):
494 try:
495 return conversion_packer(data, self.name)
496 # Python 2 and 3 raises different exceptions from inet_pton
497 except(OSError, socket.error, TypeError):
498 pass
Ole Troan418ebb72019-11-19 04:38:20 +0100499 if data is None: # Default to zero if not specified
500 if self.options and 'default' in self.options:
501 data = self.options['default']
502 else:
503 data = 0
Ole Troan0bcad322018-12-11 13:04:01 +0100504
505 return self.packer.pack(data, kwargs)
506
Ole Troanda47f672019-11-24 15:22:55 +0100507 @staticmethod
508 def _get_packer_with_options(f_type, options):
509 return VPPTypeAlias(f_type, types[f_type].msgdef, options=options)
Ole Troan418ebb72019-11-19 04:38:20 +0100510
Ole Troan0bcad322018-12-11 13:04:01 +0100511 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troan418ebb72019-11-19 04:38:20 +0100512 if ntc is False and self.name in vpp_format.conversion_unpacker_table:
Ole Troan75761b92019-09-11 17:49:08 +0200513 # Disable type conversion for dependent types
514 ntc = True
515 self.toplevelconversion = True
Ole Troan0bcad322018-12-11 13:04:01 +0100516 t, size = self.packer.unpack(data, offset, result, ntc=ntc)
Ole Troan75761b92019-09-11 17:49:08 +0200517 if self.toplevelconversion:
518 self.toplevelconversion = False
Ole Troan0bcad322018-12-11 13:04:01 +0100519 return conversion_unpacker(t, self.name), size
520 return t, size
Ole Troan53fffa12018-11-13 12:36:56 +0100521
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500522 def __repr__(self):
523 return "VPPTypeAlias(name=%s, msgdef=%s, options=%s)" % (
524 self.name, self.msgdef, self.options)
525
Ole Troan53fffa12018-11-13 12:36:56 +0100526
Ole Troan418ebb72019-11-19 04:38:20 +0100527class VPPType(Packer):
Ole Troana7564e82018-06-12 21:06:44 +0200528 # Set everything up to be able to pack / unpack
529 def __init__(self, name, msgdef):
530 self.name = name
531 self.msgdef = msgdef
532 self.packers = []
533 self.fields = []
534 self.fieldtypes = []
535 self.field_by_name = {}
536 size = 0
537 for i, f in enumerate(msgdef):
538 if type(f) is dict and 'crc' in f:
539 self.crc = f['crc']
540 continue
541 f_type, f_name = f[:2]
542 self.fields.append(f_name)
543 self.field_by_name[f_name] = None
544 self.fieldtypes.append(f_type)
545 if f_type not in types:
546 logger.debug('Unknown type {}'.format(f_type))
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800547 raise VPPSerializerValueError(
548 'Unknown message type {}'.format(f_type))
Ole Troan9ac11382019-04-23 17:11:01 +0200549
Ole Troan85465582019-04-30 10:04:36 +0200550 fieldlen = len(f)
Ole Troan9ac11382019-04-23 17:11:01 +0200551 options = [x for x in f if type(x) is dict]
552 if len(options):
553 self.options = options[0]
Ole Troan85465582019-04-30 10:04:36 +0200554 fieldlen -= 1
Ole Troan9ac11382019-04-23 17:11:01 +0200555 else:
556 self.options = {}
Ole Troan85465582019-04-30 10:04:36 +0200557 if fieldlen == 3: # list
Ole Troana7564e82018-06-12 21:06:44 +0200558 list_elements = f[2]
559 if list_elements == 0:
Ole Troane5ff5a32019-08-23 22:55:18 +0200560 if f_type == 'string':
561 p = String(f_name, 0, self.options)
562 else:
563 p = VLAList_legacy(f_name, f_type)
Ole Troana7564e82018-06-12 21:06:44 +0200564 self.packers.append(p)
Ole Troane5ff5a32019-08-23 22:55:18 +0200565 elif f_type == 'u8':
Ole Troana7564e82018-06-12 21:06:44 +0200566 p = FixedList_u8(f_name, f_type, list_elements)
567 self.packers.append(p)
568 size += p.size
Ole Troane5ff5a32019-08-23 22:55:18 +0200569 elif f_type == 'string':
570 p = String(f_name, list_elements, self.options)
571 self.packers.append(p)
572 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200573 else:
574 p = FixedList(f_name, f_type, list_elements)
575 self.packers.append(p)
576 size += p.size
Ole Troan85465582019-04-30 10:04:36 +0200577 elif fieldlen == 4: # Variable length list
Ole Troan413f4a52018-11-28 11:36:05 +0100578 length_index = self.fields.index(f[3])
579 p = VLAList(f_name, f_type, f[3], length_index)
580 self.packers.append(p)
Ole Troana7564e82018-06-12 21:06:44 +0200581 else:
Ole Troan418ebb72019-11-19 04:38:20 +0100582 # default support for types that decay to basetype
Ole Troan40dc4b32019-10-22 14:01:53 +0200583 if 'default' in self.options:
Ole Troan418ebb72019-11-19 04:38:20 +0100584 p = self.get_packer_with_options(f_type, self.options)
Ole Troan40dc4b32019-10-22 14:01:53 +0200585 else:
586 p = types[f_type]
Ole Troan418ebb72019-11-19 04:38:20 +0100587
Ole Troan9ac11382019-04-23 17:11:01 +0200588 self.packers.append(p)
589 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200590
591 self.size = size
592 self.tuple = collections.namedtuple(name, self.fields, rename=True)
593 types[name] = self
Ole Troan75761b92019-09-11 17:49:08 +0200594 self.toplevelconversion = False
Ole Troana7564e82018-06-12 21:06:44 +0200595
596 def pack(self, data, kwargs=None):
597 if not kwargs:
598 kwargs = data
Ole Troana7564e82018-06-12 21:06:44 +0200599 b = bytes()
Ole Troan31555a32018-10-22 09:30:26 +0200600
Ole Troan0bcad322018-12-11 13:04:01 +0100601 # Try one of the format functions
602 if data and conversion_required(data, self.name):
603 return conversion_packer(data, self.name)
604
605 for i, a in enumerate(self.fields):
Ole Troan31555a32018-10-22 09:30:26 +0200606 if data and type(data) is not dict and a not in data:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800607 raise VPPSerializerValueError(
608 "Invalid argument: {} expected {}.{}".
609 format(data, self.name, a))
Ole Troan31555a32018-10-22 09:30:26 +0200610
611 # Defaulting to zero.
612 if not data or a not in data: # Default to 0
613 arg = None
614 kwarg = None # No default for VLA
615 else:
616 arg = data[a]
617 kwarg = kwargs[a] if a in kwargs else None
Ole Troana7564e82018-06-12 21:06:44 +0200618 if isinstance(self.packers[i], VPPType):
Ole Troan0bcad322018-12-11 13:04:01 +0100619 b += self.packers[i].pack(arg, kwarg)
Ole Troana7564e82018-06-12 21:06:44 +0200620 else:
Ole Troan31555a32018-10-22 09:30:26 +0200621 b += self.packers[i].pack(arg, kwargs)
622
Ole Troana7564e82018-06-12 21:06:44 +0200623 return b
624
Ole Troan0bcad322018-12-11 13:04:01 +0100625 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200626 # Return a list of arguments
627 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200628 total = 0
Ole Troan418ebb72019-11-19 04:38:20 +0100629 if ntc is False and self.name in vpp_format.conversion_unpacker_table:
Ole Troan75761b92019-09-11 17:49:08 +0200630 # Disable type conversion for dependent types
631 ntc = True
632 self.toplevelconversion = True
633
Ole Troana7564e82018-06-12 21:06:44 +0200634 for p in self.packers:
Ole Troan0bcad322018-12-11 13:04:01 +0100635 x, size = p.unpack(data, offset, result, ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200636 if type(x) is tuple and len(x) == 1:
637 x = x[0]
638 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200639 offset += size
640 total += size
641 t = self.tuple._make(result)
Ole Troan75761b92019-09-11 17:49:08 +0200642
643 if self.toplevelconversion:
644 self.toplevelconversion = False
Ole Troan0bcad322018-12-11 13:04:01 +0100645 t = conversion_unpacker(t, self.name)
Ole Troanc84cbad2018-09-06 22:58:05 +0200646 return t, total
Ole Troana7564e82018-06-12 21:06:44 +0200647
Paul Vinciguerra14b0b472019-11-23 23:49:57 -0500648 def __repr__(self):
649 return "%s(name=%s, msgdef=%s)" % (
650 self.__class__.__name__, self.name, self.msgdef
651 )
652
Ole Troana7564e82018-06-12 21:06:44 +0200653
654class VPPMessage(VPPType):
655 pass