blob: fe9a083d6c88d8c5ce615b6cb0f5068b4df00b24 [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
Ole Troan85465582019-04-30 10:04:36 +020016import logging
Ole Troan85465582019-04-30 10:04:36 +020017import socket
Paul Vinciguerraae8819f2019-06-07 13:35:37 -040018import struct
19import sys
Paul Vinciguerra42bdba82019-03-07 11:26:23 -080020
21if sys.version_info <= (3, 4):
Paul Vinciguerraae8819f2019-06-07 13:35:37 -040022 from aenum import IntEnum # noqa: F401
Ole Troan48ae19e2019-03-07 11:28:32 +010023else:
Paul Vinciguerraae8819f2019-06-07 13:35:37 -040024 from enum import IntEnum # noqa: F401
Paul Vinciguerra42bdba82019-03-07 11:26:23 -080025
26if sys.version_info <= (3, 6):
Paul Vinciguerraae8819f2019-06-07 13:35:37 -040027 from aenum import IntFlag # noqa: F401
Paul Vinciguerra42bdba82019-03-07 11:26:23 -080028else:
Paul Vinciguerra42bdba82019-03-07 11:26:23 -080029
Paul Vinciguerraae8819f2019-06-07 13:35:37 -040030 from enum import IntFlag # noqa: F401
31
32from . import vpp_format # noqa: E402
Ole Troana7564e82018-06-12 21:06:44 +020033
34#
35# Set log-level in application by doing e.g.:
36# logger = logging.getLogger('vpp_serializer')
37# logger.setLevel(logging.DEBUG)
38#
39logger = logging.getLogger(__name__)
40
Ole Troan0bcad322018-12-11 13:04:01 +010041if sys.version[0] == '2':
Ole Troan8006c6a2018-12-17 12:02:26 +010042 def check(d): type(d) is dict
Ole Troan0bcad322018-12-11 13:04:01 +010043else:
Ole Troan8006c6a2018-12-17 12:02:26 +010044 def check(d): type(d) is dict or type(d) is bytes
45
Ole Troan0bcad322018-12-11 13:04:01 +010046
47def conversion_required(data, field_type):
48 if check(data):
49 return False
50 try:
51 if type(data).__name__ in vpp_format.conversion_table[field_type]:
52 return True
53 except KeyError:
54 return False
55
56
57def conversion_packer(data, field_type):
58 t = type(data).__name__
59 return types[field_type].pack(vpp_format.
60 conversion_table[field_type][t](data))
61
62
63def conversion_unpacker(data, field_type):
64 if field_type not in vpp_format.conversion_unpacker_table:
65 return data
66 return vpp_format.conversion_unpacker_table[field_type](data)
67
Ole Troana7564e82018-06-12 21:06:44 +020068
Paul Vinciguerra7e713f12018-11-26 12:04:48 -080069class BaseTypes(object):
Ole Troan85465582019-04-30 10:04:36 +020070 def __init__(self, type, elements=0, options=None):
Ole Troana7564e82018-06-12 21:06:44 +020071 base_types = {'u8': '>B',
Ole Troan413f4a52018-11-28 11:36:05 +010072 'string': '>s',
Ole Troana7564e82018-06-12 21:06:44 +020073 'u16': '>H',
74 'u32': '>I',
75 'i32': '>i',
76 'u64': '>Q',
77 'f64': '>d',
Ole Troanf47e9b62018-10-16 15:14:03 +020078 'bool': '>?',
Ole Troana7564e82018-06-12 21:06:44 +020079 'header': '>HI'}
80
Ole Troan413f4a52018-11-28 11:36:05 +010081 if elements > 0 and (type == 'u8' or type == 'string'):
Ole Troana7564e82018-06-12 21:06:44 +020082 self.packer = struct.Struct('>%ss' % elements)
83 else:
84 self.packer = struct.Struct(base_types[type])
85 self.size = self.packer.size
Ole Troan85465582019-04-30 10:04:36 +020086 self.options = options
87
88 def __call__(self, args):
89 self.options = args
90 return self
Ole Troana7564e82018-06-12 21:06:44 +020091
92 def pack(self, data, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +020093 if not data: # Default to zero if not specified
Ole Troan85465582019-04-30 10:04:36 +020094 if self.options and 'default' in self.options:
95 data = self.options['default']
96 else:
97 data = 0
Ole Troana7564e82018-06-12 21:06:44 +020098 return self.packer.pack(data)
99
Ole Troan0bcad322018-12-11 13:04:01 +0100100 def unpack(self, data, offset, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200101 return self.packer.unpack_from(data, offset)[0], self.packer.size
Ole Troana7564e82018-06-12 21:06:44 +0200102
103
Ole Troan413f4a52018-11-28 11:36:05 +0100104class String(object):
Ole Troan9ac11382019-04-23 17:11:01 +0200105 def __init__(self, options):
Ole Troan413f4a52018-11-28 11:36:05 +0100106 self.name = 'string'
107 self.size = 1
108 self.length_field_packer = BaseTypes('u32')
Ole Troan9ac11382019-04-23 17:11:01 +0200109 self.limit = options['limit'] if 'limit' in options else None
Ole Troan413f4a52018-11-28 11:36:05 +0100110
111 def pack(self, list, kwargs=None):
112 if not list:
113 return self.length_field_packer.pack(0) + b""
Ole Troan9ac11382019-04-23 17:11:01 +0200114 if self.limit and len(list) > self.limit:
115 raise VPPSerializerValueError(
116 "Invalid argument length for: {}, {} maximum {}".
117 format(list, len(list), self.limit))
118
Ole Troan413f4a52018-11-28 11:36:05 +0100119 return self.length_field_packer.pack(len(list)) + list.encode('utf8')
120
121 def unpack(self, data, offset=0, result=None, ntc=False):
122 length, length_field_size = self.length_field_packer.unpack(data,
123 offset)
124 if length == 0:
125 return b'', 0
126 p = BaseTypes('u8', length)
127 x, size = p.unpack(data, offset + length_field_size)
Ole Troan8006c6a2018-12-17 12:02:26 +0100128 x2 = x.split(b'\0', 1)[0]
Ole Troan413f4a52018-11-28 11:36:05 +0100129 return (x2.decode('utf8'), size + length_field_size)
130
131
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800132types = {'u8': BaseTypes('u8'), 'u16': BaseTypes('u16'),
133 'u32': BaseTypes('u32'), 'i32': BaseTypes('i32'),
134 'u64': BaseTypes('u64'), 'f64': BaseTypes('f64'),
Ole Troan9ac11382019-04-23 17:11:01 +0200135 'bool': BaseTypes('bool'), 'string': String}
Ole Troana7564e82018-06-12 21:06:44 +0200136
137
Ole Troan0685da42018-10-16 14:42:50 +0200138def vpp_get_type(name):
139 try:
140 return types[name]
141 except KeyError:
142 return None
143
144
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800145class VPPSerializerValueError(ValueError):
146 pass
147
148
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800149class FixedList_u8(object):
Ole Troana7564e82018-06-12 21:06:44 +0200150 def __init__(self, name, field_type, num):
151 self.name = name
152 self.num = num
153 self.packer = BaseTypes(field_type, num)
154 self.size = self.packer.size
Ole Troan413f4a52018-11-28 11:36:05 +0100155 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200156
Ole Troan85465582019-04-30 10:04:36 +0200157 def __call__(self, args):
158 self.options = args
159 return self
160
Ole Troan0bcad322018-12-11 13:04:01 +0100161 def pack(self, data, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200162 """Packs a fixed length bytestring. Left-pads with zeros
163 if input data is too short."""
Ole Troan0bcad322018-12-11 13:04:01 +0100164 if not data:
Ole Troan31555a32018-10-22 09:30:26 +0200165 return b'\x00' * self.size
Ole Troan0bcad322018-12-11 13:04:01 +0100166
167 if len(data) > self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800168 raise VPPSerializerValueError(
169 'Fixed list length error for "{}", got: {}'
170 ' expected: {}'
Ole Troan0bcad322018-12-11 13:04:01 +0100171 .format(self.name, len(data), self.num))
Ole Troana7564e82018-06-12 21:06:44 +0200172
Ole Troan0bcad322018-12-11 13:04:01 +0100173 return self.packer.pack(data)
174
175 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200176 if len(data[offset:]) < self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800177 raise VPPSerializerValueError(
178 'Invalid array length for "{}" got {}'
179 ' expected {}'
180 .format(self.name, len(data[offset:]), self.num))
Ole Troan413f4a52018-11-28 11:36:05 +0100181 if self.field_type == 'string':
182 s = self.packer.unpack(data, offset)
183 s2 = s[0].split(b'\0', 1)[0]
184 return (s2.decode('utf-8'), self.num)
Ole Troana7564e82018-06-12 21:06:44 +0200185 return self.packer.unpack(data, offset)
186
187
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800188class FixedList(object):
Ole Troana7564e82018-06-12 21:06:44 +0200189 def __init__(self, name, field_type, num):
190 self.num = num
191 self.packer = types[field_type]
192 self.size = self.packer.size * num
Ole Troan0bcad322018-12-11 13:04:01 +0100193 self.name = name
194 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200195
Ole Troan85465582019-04-30 10:04:36 +0200196 def __call__(self, args):
197 self.options = args
198 return self
199
Ole Troana7564e82018-06-12 21:06:44 +0200200 def pack(self, list, kwargs):
Ole Troana7564e82018-06-12 21:06:44 +0200201 if len(list) != self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800202 raise VPPSerializerValueError(
203 'Fixed list length error, got: {} expected: {}'
204 .format(len(list), self.num))
Ole Troana7564e82018-06-12 21:06:44 +0200205 b = bytes()
206 for e in list:
207 b += self.packer.pack(e)
208 return b
209
Ole Troan0bcad322018-12-11 13:04:01 +0100210 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200211 # Return a list of arguments
212 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200213 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200214 for e in range(self.num):
Ole Troan0bcad322018-12-11 13:04:01 +0100215 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200216 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200217 offset += size
218 total += size
219 return result, total
Ole Troana7564e82018-06-12 21:06:44 +0200220
221
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800222class VLAList(object):
Ole Troana7564e82018-06-12 21:06:44 +0200223 def __init__(self, name, field_type, len_field_name, index):
Ole Troanc84cbad2018-09-06 22:58:05 +0200224 self.name = name
Ole Troan413f4a52018-11-28 11:36:05 +0100225 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200226 self.index = index
227 self.packer = types[field_type]
228 self.size = self.packer.size
229 self.length_field = len_field_name
230
Ole Troan85465582019-04-30 10:04:36 +0200231 def __call__(self, args):
232 self.options = args
233 return self
234
Ole Troana7564e82018-06-12 21:06:44 +0200235 def pack(self, list, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +0200236 if not list:
237 return b""
Ole Troana7564e82018-06-12 21:06:44 +0200238 if len(list) != kwargs[self.length_field]:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800239 raise VPPSerializerValueError(
240 'Variable length error, got: {} expected: {}'
241 .format(len(list), kwargs[self.length_field]))
Ole Troana7564e82018-06-12 21:06:44 +0200242 b = bytes()
243
244 # u8 array
Ole Troan413f4a52018-11-28 11:36:05 +0100245
Ole Troana7564e82018-06-12 21:06:44 +0200246 if self.packer.size == 1:
247 return bytearray(list)
248
249 for e in list:
250 b += self.packer.pack(e)
251 return b
252
Ole Troan0bcad322018-12-11 13:04:01 +0100253 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200254 # Return a list of arguments
Ole Troanc84cbad2018-09-06 22:58:05 +0200255 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200256
257 # u8 array
258 if self.packer.size == 1:
259 if result[self.index] == 0:
Ole Troanc84cbad2018-09-06 22:58:05 +0200260 return b'', 0
Ole Troana7564e82018-06-12 21:06:44 +0200261 p = BaseTypes('u8', result[self.index])
Ole Troan0bcad322018-12-11 13:04:01 +0100262 return p.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200263
264 r = []
265 for e in range(result[self.index]):
Ole Troan0bcad322018-12-11 13:04:01 +0100266 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200267 r.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200268 offset += size
269 total += size
270 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200271
272
273class VLAList_legacy():
274 def __init__(self, name, field_type):
275 self.packer = types[field_type]
276 self.size = self.packer.size
277
Ole Troan85465582019-04-30 10:04:36 +0200278 def __call__(self, args):
279 self.options = args
280 return self
281
Ole Troana7564e82018-06-12 21:06:44 +0200282 def pack(self, list, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200283 if self.packer.size == 1:
284 return bytes(list)
285
286 b = bytes()
287 for e in list:
288 b += self.packer.pack(e)
289 return b
290
Ole Troan0bcad322018-12-11 13:04:01 +0100291 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200292 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200293 # Return a list of arguments
294 if (len(data) - offset) % self.packer.size:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800295 raise VPPSerializerValueError(
296 'Legacy Variable Length Array length mismatch.')
Ole Troana7564e82018-06-12 21:06:44 +0200297 elements = int((len(data) - offset) / self.packer.size)
298 r = []
Ole Troana7564e82018-06-12 21:06:44 +0200299 for e in range(elements):
Ole Troan0bcad322018-12-11 13:04:01 +0100300 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200301 r.append(x)
302 offset += self.packer.size
Ole Troanc84cbad2018-09-06 22:58:05 +0200303 total += size
304 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200305
306
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800307class VPPEnumType(object):
Ole Troana7564e82018-06-12 21:06:44 +0200308 def __init__(self, name, msgdef):
309 self.size = types['u32'].size
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100310 self.enumtype = 'u32'
Ole Troana7564e82018-06-12 21:06:44 +0200311 e_hash = {}
312 for f in msgdef:
313 if type(f) is dict and 'enumtype' in f:
314 if f['enumtype'] != 'u32':
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100315 self.size = types[f['enumtype']].size
316 self.enumtype = f['enumtype']
Ole Troana7564e82018-06-12 21:06:44 +0200317 continue
318 ename, evalue = f
319 e_hash[ename] = evalue
Ole Troan48ae19e2019-03-07 11:28:32 +0100320 self.enum = IntFlag(name, e_hash)
Ole Troana7564e82018-06-12 21:06:44 +0200321 types[name] = self
Ole Troana7564e82018-06-12 21:06:44 +0200322
Ole Troan85465582019-04-30 10:04:36 +0200323 def __call__(self, args):
324 self.options = args
325 return self
326
Ole Troana7564e82018-06-12 21:06:44 +0200327 def __getattr__(self, name):
328 return self.enum[name]
329
Ole Troan0685da42018-10-16 14:42:50 +0200330 def __nonzero__(self):
331 return True
332
Ole Troana7564e82018-06-12 21:06:44 +0200333 def pack(self, data, kwargs=None):
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100334 return types[self.enumtype].pack(data)
Ole Troana7564e82018-06-12 21:06:44 +0200335
Ole Troan0bcad322018-12-11 13:04:01 +0100336 def unpack(self, data, offset=0, result=None, ntc=False):
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100337 x, size = types[self.enumtype].unpack(data, offset)
Ole Troanc84cbad2018-09-06 22:58:05 +0200338 return self.enum(x), size
Ole Troana7564e82018-06-12 21:06:44 +0200339
340
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800341class VPPUnionType(object):
Ole Troana7564e82018-06-12 21:06:44 +0200342 def __init__(self, name, msgdef):
343 self.name = name
344 self.size = 0
345 self.maxindex = 0
346 fields = []
347 self.packers = collections.OrderedDict()
348 for i, f in enumerate(msgdef):
349 if type(f) is dict and 'crc' in f:
350 self.crc = f['crc']
351 continue
352 f_type, f_name = f
353 if f_type not in types:
354 logger.debug('Unknown union type {}'.format(f_type))
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800355 raise VPPSerializerValueError(
356 'Unknown message type {}'.format(f_type))
Ole Troana7564e82018-06-12 21:06:44 +0200357 fields.append(f_name)
358 size = types[f_type].size
359 self.packers[f_name] = types[f_type]
360 if size > self.size:
361 self.size = size
362 self.maxindex = i
363
364 types[name] = self
365 self.tuple = collections.namedtuple(name, fields, rename=True)
Ole Troana7564e82018-06-12 21:06:44 +0200366
Ole Troan85465582019-04-30 10:04:36 +0200367 def __call__(self, args):
368 self.options = args
369 return self
370
Ole Troan31555a32018-10-22 09:30:26 +0200371 # Union of variable length?
Ole Troana7564e82018-06-12 21:06:44 +0200372 def pack(self, data, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +0200373 if not data:
374 return b'\x00' * self.size
375
Ole Troana7564e82018-06-12 21:06:44 +0200376 for k, v in data.items():
377 logger.debug("Key: {} Value: {}".format(k, v))
378 b = self.packers[k].pack(v, kwargs)
Ole Troana7564e82018-06-12 21:06:44 +0200379 break
380 r = bytearray(self.size)
Ole Troanb199e982018-08-02 19:19:21 +0200381 r[:len(b)] = b
Ole Troana7564e82018-06-12 21:06:44 +0200382 return r
383
Ole Troan0bcad322018-12-11 13:04:01 +0100384 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200385 r = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200386 maxsize = 0
Ole Troana7564e82018-06-12 21:06:44 +0200387 for k, p in self.packers.items():
Ole Troan0bcad322018-12-11 13:04:01 +0100388 x, size = p.unpack(data, offset, ntc=ntc)
Ole Troanc84cbad2018-09-06 22:58:05 +0200389 if size > maxsize:
390 maxsize = size
391 r.append(x)
392 return self.tuple._make(r), maxsize
Ole Troana7564e82018-06-12 21:06:44 +0200393
394
Ole Troan0bcad322018-12-11 13:04:01 +0100395class VPPTypeAlias(object):
396 def __init__(self, name, msgdef):
397 self.name = name
398 t = vpp_get_type(msgdef['type'])
399 if not t:
Ole Troan53fffa12018-11-13 12:36:56 +0100400 raise ValueError()
Ole Troan0bcad322018-12-11 13:04:01 +0100401 if 'length' in msgdef:
402 if msgdef['length'] == 0:
403 raise ValueError()
404 if msgdef['type'] == 'u8':
405 self.packer = FixedList_u8(name, msgdef['type'],
406 msgdef['length'])
407 self.size = self.packer.size
408 else:
409 self.packer = FixedList(name, msgdef['type'], msgdef['length'])
Ole Troan8c8acc02018-11-27 10:05:23 +0100410 else:
Ole Troan0bcad322018-12-11 13:04:01 +0100411 self.packer = t
412 self.size = t.size
413
414 types[name] = self
415
Ole Troan85465582019-04-30 10:04:36 +0200416 def __call__(self, args):
417 self.options = args
418 return self
419
Ole Troan0bcad322018-12-11 13:04:01 +0100420 def pack(self, data, kwargs=None):
421 if data and conversion_required(data, self.name):
422 try:
423 return conversion_packer(data, self.name)
424 # Python 2 and 3 raises different exceptions from inet_pton
425 except(OSError, socket.error, TypeError):
426 pass
427
428 return self.packer.pack(data, kwargs)
429
430 def unpack(self, data, offset=0, result=None, ntc=False):
431 t, size = self.packer.unpack(data, offset, result, ntc=ntc)
432 if not ntc:
433 return conversion_unpacker(t, self.name), size
434 return t, size
Ole Troan53fffa12018-11-13 12:36:56 +0100435
436
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800437class VPPType(object):
Ole Troana7564e82018-06-12 21:06:44 +0200438 # Set everything up to be able to pack / unpack
439 def __init__(self, name, msgdef):
440 self.name = name
441 self.msgdef = msgdef
442 self.packers = []
443 self.fields = []
444 self.fieldtypes = []
445 self.field_by_name = {}
446 size = 0
447 for i, f in enumerate(msgdef):
448 if type(f) is dict and 'crc' in f:
449 self.crc = f['crc']
450 continue
451 f_type, f_name = f[:2]
452 self.fields.append(f_name)
453 self.field_by_name[f_name] = None
454 self.fieldtypes.append(f_type)
455 if f_type not in types:
456 logger.debug('Unknown type {}'.format(f_type))
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800457 raise VPPSerializerValueError(
458 'Unknown message type {}'.format(f_type))
Ole Troan9ac11382019-04-23 17:11:01 +0200459
Ole Troan85465582019-04-30 10:04:36 +0200460 fieldlen = len(f)
Ole Troan9ac11382019-04-23 17:11:01 +0200461 options = [x for x in f if type(x) is dict]
462 if len(options):
463 self.options = options[0]
Ole Troan85465582019-04-30 10:04:36 +0200464 fieldlen -= 1
Ole Troan9ac11382019-04-23 17:11:01 +0200465 else:
466 self.options = {}
Ole Troan85465582019-04-30 10:04:36 +0200467 if fieldlen == 3: # list
Ole Troana7564e82018-06-12 21:06:44 +0200468 list_elements = f[2]
469 if list_elements == 0:
470 p = VLAList_legacy(f_name, f_type)
471 self.packers.append(p)
Ole Troan413f4a52018-11-28 11:36:05 +0100472 elif f_type == 'u8' or f_type == 'string':
Ole Troana7564e82018-06-12 21:06:44 +0200473 p = FixedList_u8(f_name, f_type, list_elements)
474 self.packers.append(p)
475 size += p.size
476 else:
477 p = FixedList(f_name, f_type, list_elements)
478 self.packers.append(p)
479 size += p.size
Ole Troan85465582019-04-30 10:04:36 +0200480 elif fieldlen == 4: # Variable length list
Ole Troan413f4a52018-11-28 11:36:05 +0100481 length_index = self.fields.index(f[3])
482 p = VLAList(f_name, f_type, f[3], length_index)
483 self.packers.append(p)
Ole Troana7564e82018-06-12 21:06:44 +0200484 else:
Ole Troan85465582019-04-30 10:04:36 +0200485 p = types[f_type](self.options)
Ole Troan9ac11382019-04-23 17:11:01 +0200486 self.packers.append(p)
487 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200488
489 self.size = size
490 self.tuple = collections.namedtuple(name, self.fields, rename=True)
491 types[name] = self
Ole Troana7564e82018-06-12 21:06:44 +0200492
Ole Troan85465582019-04-30 10:04:36 +0200493 def __call__(self, args):
494 self.options = args
495 return self
496
Ole Troana7564e82018-06-12 21:06:44 +0200497 def pack(self, data, kwargs=None):
498 if not kwargs:
499 kwargs = data
Ole Troana7564e82018-06-12 21:06:44 +0200500 b = bytes()
Ole Troan31555a32018-10-22 09:30:26 +0200501
Ole Troan0bcad322018-12-11 13:04:01 +0100502 # Try one of the format functions
503 if data and conversion_required(data, self.name):
504 return conversion_packer(data, self.name)
505
506 for i, a in enumerate(self.fields):
Ole Troan31555a32018-10-22 09:30:26 +0200507 if data and type(data) is not dict and a not in data:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800508 raise VPPSerializerValueError(
509 "Invalid argument: {} expected {}.{}".
510 format(data, self.name, a))
Ole Troan31555a32018-10-22 09:30:26 +0200511
512 # Defaulting to zero.
513 if not data or a not in data: # Default to 0
514 arg = None
515 kwarg = None # No default for VLA
516 else:
517 arg = data[a]
518 kwarg = kwargs[a] if a in kwargs else None
Ole Troana7564e82018-06-12 21:06:44 +0200519 if isinstance(self.packers[i], VPPType):
Ole Troan0bcad322018-12-11 13:04:01 +0100520 b += self.packers[i].pack(arg, kwarg)
Ole Troana7564e82018-06-12 21:06:44 +0200521 else:
Ole Troan31555a32018-10-22 09:30:26 +0200522 b += self.packers[i].pack(arg, kwargs)
523
Ole Troana7564e82018-06-12 21:06:44 +0200524 return b
525
Ole Troan0bcad322018-12-11 13:04:01 +0100526 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200527 # Return a list of arguments
528 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200529 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200530 for p in self.packers:
Ole Troan0bcad322018-12-11 13:04:01 +0100531 x, size = p.unpack(data, offset, result, ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200532 if type(x) is tuple and len(x) == 1:
533 x = x[0]
534 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200535 offset += size
536 total += size
537 t = self.tuple._make(result)
Ole Troan0bcad322018-12-11 13:04:01 +0100538 if not ntc:
539 t = conversion_unpacker(t, self.name)
Ole Troanc84cbad2018-09-06 22:58:05 +0200540 return t, total
Ole Troana7564e82018-06-12 21:06:44 +0200541
542
543class VPPMessage(VPPType):
544 pass