blob: 9ce0287592fbb5af715710c0bacd2c8cb2530920 [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',
Paul Vinciguerra6ce0f352019-07-02 13:06:21 -040072 'i8': '>b',
Ole Troan413f4a52018-11-28 11:36:05 +010073 'string': '>s',
Ole Troana7564e82018-06-12 21:06:44 +020074 'u16': '>H',
Paul Vinciguerra6ce0f352019-07-02 13:06:21 -040075 'i16': '>h',
Ole Troana7564e82018-06-12 21:06:44 +020076 'u32': '>I',
77 'i32': '>i',
78 'u64': '>Q',
Paul Vinciguerra6ce0f352019-07-02 13:06:21 -040079 'i64': '>q',
Paul Vinciguerra888640a2019-05-14 21:01:28 -040080 'f64': '=d',
Ole Troanf47e9b62018-10-16 15:14:03 +020081 'bool': '>?',
Ole Troana7564e82018-06-12 21:06:44 +020082 'header': '>HI'}
83
Ole Troan413f4a52018-11-28 11:36:05 +010084 if elements > 0 and (type == 'u8' or type == 'string'):
Ole Troana7564e82018-06-12 21:06:44 +020085 self.packer = struct.Struct('>%ss' % elements)
86 else:
87 self.packer = struct.Struct(base_types[type])
88 self.size = self.packer.size
Ole Troan85465582019-04-30 10:04:36 +020089 self.options = options
90
91 def __call__(self, args):
92 self.options = args
93 return self
Ole Troana7564e82018-06-12 21:06:44 +020094
95 def pack(self, data, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +020096 if not data: # Default to zero if not specified
Ole Troan85465582019-04-30 10:04:36 +020097 if self.options and 'default' in self.options:
98 data = self.options['default']
99 else:
100 data = 0
Ole Troana7564e82018-06-12 21:06:44 +0200101 return self.packer.pack(data)
102
Ole Troan0bcad322018-12-11 13:04:01 +0100103 def unpack(self, data, offset, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200104 return self.packer.unpack_from(data, offset)[0], self.packer.size
Ole Troana7564e82018-06-12 21:06:44 +0200105
106
Ole Troan413f4a52018-11-28 11:36:05 +0100107class String(object):
Ole Troan9ac11382019-04-23 17:11:01 +0200108 def __init__(self, options):
Ole Troan413f4a52018-11-28 11:36:05 +0100109 self.name = 'string'
110 self.size = 1
111 self.length_field_packer = BaseTypes('u32')
Ole Troan9ac11382019-04-23 17:11:01 +0200112 self.limit = options['limit'] if 'limit' in options else None
Ole Troan413f4a52018-11-28 11:36:05 +0100113
114 def pack(self, list, kwargs=None):
115 if not list:
116 return self.length_field_packer.pack(0) + b""
Ole Troan9ac11382019-04-23 17:11:01 +0200117 if self.limit and len(list) > self.limit:
118 raise VPPSerializerValueError(
119 "Invalid argument length for: {}, {} maximum {}".
120 format(list, len(list), self.limit))
121
Ole Troan413f4a52018-11-28 11:36:05 +0100122 return self.length_field_packer.pack(len(list)) + list.encode('utf8')
123
124 def unpack(self, data, offset=0, result=None, ntc=False):
125 length, length_field_size = self.length_field_packer.unpack(data,
126 offset)
127 if length == 0:
128 return b'', 0
129 p = BaseTypes('u8', length)
130 x, size = p.unpack(data, offset + length_field_size)
Ole Troan8006c6a2018-12-17 12:02:26 +0100131 x2 = x.split(b'\0', 1)[0]
Ole Troan413f4a52018-11-28 11:36:05 +0100132 return (x2.decode('utf8'), size + length_field_size)
133
134
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800135types = {'u8': BaseTypes('u8'), 'u16': BaseTypes('u16'),
136 'u32': BaseTypes('u32'), 'i32': BaseTypes('i32'),
137 'u64': BaseTypes('u64'), 'f64': BaseTypes('f64'),
Ole Troan9ac11382019-04-23 17:11:01 +0200138 'bool': BaseTypes('bool'), 'string': String}
Ole Troana7564e82018-06-12 21:06:44 +0200139
140
Ole Troan0685da42018-10-16 14:42:50 +0200141def vpp_get_type(name):
142 try:
143 return types[name]
144 except KeyError:
145 return None
146
147
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800148class VPPSerializerValueError(ValueError):
149 pass
150
151
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800152class FixedList_u8(object):
Ole Troana7564e82018-06-12 21:06:44 +0200153 def __init__(self, name, field_type, num):
154 self.name = name
155 self.num = num
156 self.packer = BaseTypes(field_type, num)
157 self.size = self.packer.size
Ole Troan413f4a52018-11-28 11:36:05 +0100158 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200159
Ole Troan85465582019-04-30 10:04:36 +0200160 def __call__(self, args):
161 self.options = args
162 return self
163
Ole Troan0bcad322018-12-11 13:04:01 +0100164 def pack(self, data, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200165 """Packs a fixed length bytestring. Left-pads with zeros
166 if input data is too short."""
Ole Troan0bcad322018-12-11 13:04:01 +0100167 if not data:
Ole Troan31555a32018-10-22 09:30:26 +0200168 return b'\x00' * self.size
Ole Troan0bcad322018-12-11 13:04:01 +0100169
170 if len(data) > self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800171 raise VPPSerializerValueError(
172 'Fixed list length error for "{}", got: {}'
173 ' expected: {}'
Ole Troan0bcad322018-12-11 13:04:01 +0100174 .format(self.name, len(data), self.num))
Ole Troana7564e82018-06-12 21:06:44 +0200175
Ole Troan0bcad322018-12-11 13:04:01 +0100176 return self.packer.pack(data)
177
178 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200179 if len(data[offset:]) < self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800180 raise VPPSerializerValueError(
181 'Invalid array length for "{}" got {}'
182 ' expected {}'
183 .format(self.name, len(data[offset:]), self.num))
Ole Troan413f4a52018-11-28 11:36:05 +0100184 if self.field_type == 'string':
185 s = self.packer.unpack(data, offset)
186 s2 = s[0].split(b'\0', 1)[0]
187 return (s2.decode('utf-8'), self.num)
Ole Troana7564e82018-06-12 21:06:44 +0200188 return self.packer.unpack(data, offset)
189
190
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800191class FixedList(object):
Ole Troana7564e82018-06-12 21:06:44 +0200192 def __init__(self, name, field_type, num):
193 self.num = num
194 self.packer = types[field_type]
195 self.size = self.packer.size * num
Ole Troan0bcad322018-12-11 13:04:01 +0100196 self.name = name
197 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200198
Ole Troan85465582019-04-30 10:04:36 +0200199 def __call__(self, args):
200 self.options = args
201 return self
202
Ole Troana7564e82018-06-12 21:06:44 +0200203 def pack(self, list, kwargs):
Ole Troana7564e82018-06-12 21:06:44 +0200204 if len(list) != self.num:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800205 raise VPPSerializerValueError(
206 'Fixed list length error, got: {} expected: {}'
207 .format(len(list), self.num))
Ole Troana7564e82018-06-12 21:06:44 +0200208 b = bytes()
209 for e in list:
210 b += self.packer.pack(e)
211 return b
212
Ole Troan0bcad322018-12-11 13:04:01 +0100213 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200214 # Return a list of arguments
215 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200216 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200217 for e in range(self.num):
Ole Troan0bcad322018-12-11 13:04:01 +0100218 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200219 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200220 offset += size
221 total += size
222 return result, total
Ole Troana7564e82018-06-12 21:06:44 +0200223
224
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800225class VLAList(object):
Ole Troana7564e82018-06-12 21:06:44 +0200226 def __init__(self, name, field_type, len_field_name, index):
Ole Troanc84cbad2018-09-06 22:58:05 +0200227 self.name = name
Ole Troan413f4a52018-11-28 11:36:05 +0100228 self.field_type = field_type
Ole Troana7564e82018-06-12 21:06:44 +0200229 self.index = index
230 self.packer = types[field_type]
231 self.size = self.packer.size
232 self.length_field = len_field_name
233
Ole Troan85465582019-04-30 10:04:36 +0200234 def __call__(self, args):
235 self.options = args
236 return self
237
Ole Troana7564e82018-06-12 21:06:44 +0200238 def pack(self, list, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +0200239 if not list:
240 return b""
Ole Troana7564e82018-06-12 21:06:44 +0200241 if len(list) != kwargs[self.length_field]:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800242 raise VPPSerializerValueError(
243 'Variable length error, got: {} expected: {}'
244 .format(len(list), kwargs[self.length_field]))
Ole Troana7564e82018-06-12 21:06:44 +0200245 b = bytes()
246
247 # u8 array
Ole Troan413f4a52018-11-28 11:36:05 +0100248
Ole Troana7564e82018-06-12 21:06:44 +0200249 if self.packer.size == 1:
250 return bytearray(list)
251
252 for e in list:
253 b += self.packer.pack(e)
254 return b
255
Ole Troan0bcad322018-12-11 13:04:01 +0100256 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200257 # Return a list of arguments
Ole Troanc84cbad2018-09-06 22:58:05 +0200258 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200259
260 # u8 array
261 if self.packer.size == 1:
262 if result[self.index] == 0:
Ole Troanc84cbad2018-09-06 22:58:05 +0200263 return b'', 0
Ole Troana7564e82018-06-12 21:06:44 +0200264 p = BaseTypes('u8', result[self.index])
Ole Troan0bcad322018-12-11 13:04:01 +0100265 return p.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200266
267 r = []
268 for e in range(result[self.index]):
Ole Troan0bcad322018-12-11 13:04:01 +0100269 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200270 r.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200271 offset += size
272 total += size
273 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200274
275
276class VLAList_legacy():
277 def __init__(self, name, field_type):
278 self.packer = types[field_type]
279 self.size = self.packer.size
280
Ole Troan85465582019-04-30 10:04:36 +0200281 def __call__(self, args):
282 self.options = args
283 return self
284
Ole Troana7564e82018-06-12 21:06:44 +0200285 def pack(self, list, kwargs=None):
Ole Troana7564e82018-06-12 21:06:44 +0200286 if self.packer.size == 1:
287 return bytes(list)
288
289 b = bytes()
290 for e in list:
291 b += self.packer.pack(e)
292 return b
293
Ole Troan0bcad322018-12-11 13:04:01 +0100294 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troanc84cbad2018-09-06 22:58:05 +0200295 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200296 # Return a list of arguments
297 if (len(data) - offset) % self.packer.size:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800298 raise VPPSerializerValueError(
299 'Legacy Variable Length Array length mismatch.')
Ole Troana7564e82018-06-12 21:06:44 +0200300 elements = int((len(data) - offset) / self.packer.size)
301 r = []
Ole Troana7564e82018-06-12 21:06:44 +0200302 for e in range(elements):
Ole Troan0bcad322018-12-11 13:04:01 +0100303 x, size = self.packer.unpack(data, offset, ntc=ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200304 r.append(x)
305 offset += self.packer.size
Ole Troanc84cbad2018-09-06 22:58:05 +0200306 total += size
307 return r, total
Ole Troana7564e82018-06-12 21:06:44 +0200308
309
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800310class VPPEnumType(object):
Ole Troana7564e82018-06-12 21:06:44 +0200311 def __init__(self, name, msgdef):
312 self.size = types['u32'].size
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100313 self.enumtype = 'u32'
Ole Troana7564e82018-06-12 21:06:44 +0200314 e_hash = {}
315 for f in msgdef:
316 if type(f) is dict and 'enumtype' in f:
317 if f['enumtype'] != 'u32':
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100318 self.size = types[f['enumtype']].size
319 self.enumtype = f['enumtype']
Ole Troana7564e82018-06-12 21:06:44 +0200320 continue
321 ename, evalue = f
322 e_hash[ename] = evalue
Ole Troan48ae19e2019-03-07 11:28:32 +0100323 self.enum = IntFlag(name, e_hash)
Ole Troana7564e82018-06-12 21:06:44 +0200324 types[name] = self
Ole Troana7564e82018-06-12 21:06:44 +0200325
Ole Troan85465582019-04-30 10:04:36 +0200326 def __call__(self, args):
327 self.options = args
328 return self
329
Ole Troana7564e82018-06-12 21:06:44 +0200330 def __getattr__(self, name):
331 return self.enum[name]
332
Paul Vinciguerraa089ae12019-07-08 15:41:07 -0400333 def __bool__(self):
Ole Troan0685da42018-10-16 14:42:50 +0200334 return True
335
Paul Vinciguerraa089ae12019-07-08 15:41:07 -0400336 if sys.version[0] == '2':
337 __nonzero__ = __bool__
338
Ole Troana7564e82018-06-12 21:06:44 +0200339 def pack(self, data, kwargs=None):
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100340 return types[self.enumtype].pack(data)
Ole Troana7564e82018-06-12 21:06:44 +0200341
Ole Troan0bcad322018-12-11 13:04:01 +0100342 def unpack(self, data, offset=0, result=None, ntc=False):
Andrew Yourtchenko5c4b9f12019-03-19 14:52:29 +0100343 x, size = types[self.enumtype].unpack(data, offset)
Ole Troanc84cbad2018-09-06 22:58:05 +0200344 return self.enum(x), size
Ole Troana7564e82018-06-12 21:06:44 +0200345
346
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800347class VPPUnionType(object):
Ole Troana7564e82018-06-12 21:06:44 +0200348 def __init__(self, name, msgdef):
349 self.name = name
350 self.size = 0
351 self.maxindex = 0
352 fields = []
353 self.packers = collections.OrderedDict()
354 for i, f in enumerate(msgdef):
355 if type(f) is dict and 'crc' in f:
356 self.crc = f['crc']
357 continue
358 f_type, f_name = f
359 if f_type not in types:
360 logger.debug('Unknown union type {}'.format(f_type))
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800361 raise VPPSerializerValueError(
362 'Unknown message type {}'.format(f_type))
Ole Troana7564e82018-06-12 21:06:44 +0200363 fields.append(f_name)
364 size = types[f_type].size
365 self.packers[f_name] = types[f_type]
366 if size > self.size:
367 self.size = size
368 self.maxindex = i
369
370 types[name] = self
371 self.tuple = collections.namedtuple(name, fields, rename=True)
Ole Troana7564e82018-06-12 21:06:44 +0200372
Ole Troan85465582019-04-30 10:04:36 +0200373 def __call__(self, args):
374 self.options = args
375 return self
376
Ole Troan31555a32018-10-22 09:30:26 +0200377 # Union of variable length?
Ole Troana7564e82018-06-12 21:06:44 +0200378 def pack(self, data, kwargs=None):
Ole Troan31555a32018-10-22 09:30:26 +0200379 if not data:
380 return b'\x00' * self.size
381
Ole Troana7564e82018-06-12 21:06:44 +0200382 for k, v in data.items():
383 logger.debug("Key: {} Value: {}".format(k, v))
384 b = self.packers[k].pack(v, kwargs)
Ole Troana7564e82018-06-12 21:06:44 +0200385 break
386 r = bytearray(self.size)
Ole Troanb199e982018-08-02 19:19:21 +0200387 r[:len(b)] = b
Ole Troana7564e82018-06-12 21:06:44 +0200388 return r
389
Ole Troan0bcad322018-12-11 13:04:01 +0100390 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200391 r = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200392 maxsize = 0
Ole Troana7564e82018-06-12 21:06:44 +0200393 for k, p in self.packers.items():
Ole Troan0bcad322018-12-11 13:04:01 +0100394 x, size = p.unpack(data, offset, ntc=ntc)
Ole Troanc84cbad2018-09-06 22:58:05 +0200395 if size > maxsize:
396 maxsize = size
397 r.append(x)
398 return self.tuple._make(r), maxsize
Ole Troana7564e82018-06-12 21:06:44 +0200399
400
Ole Troan0bcad322018-12-11 13:04:01 +0100401class VPPTypeAlias(object):
402 def __init__(self, name, msgdef):
403 self.name = name
404 t = vpp_get_type(msgdef['type'])
405 if not t:
Ole Troan53fffa12018-11-13 12:36:56 +0100406 raise ValueError()
Ole Troan0bcad322018-12-11 13:04:01 +0100407 if 'length' in msgdef:
408 if msgdef['length'] == 0:
409 raise ValueError()
410 if msgdef['type'] == 'u8':
411 self.packer = FixedList_u8(name, msgdef['type'],
412 msgdef['length'])
413 self.size = self.packer.size
414 else:
415 self.packer = FixedList(name, msgdef['type'], msgdef['length'])
Ole Troan8c8acc02018-11-27 10:05:23 +0100416 else:
Ole Troan0bcad322018-12-11 13:04:01 +0100417 self.packer = t
418 self.size = t.size
419
420 types[name] = self
421
Ole Troan85465582019-04-30 10:04:36 +0200422 def __call__(self, args):
423 self.options = args
424 return self
425
Ole Troan0bcad322018-12-11 13:04:01 +0100426 def pack(self, data, kwargs=None):
427 if data and conversion_required(data, self.name):
428 try:
429 return conversion_packer(data, self.name)
430 # Python 2 and 3 raises different exceptions from inet_pton
431 except(OSError, socket.error, TypeError):
432 pass
433
434 return self.packer.pack(data, kwargs)
435
436 def unpack(self, data, offset=0, result=None, ntc=False):
437 t, size = self.packer.unpack(data, offset, result, ntc=ntc)
438 if not ntc:
439 return conversion_unpacker(t, self.name), size
440 return t, size
Ole Troan53fffa12018-11-13 12:36:56 +0100441
442
Paul Vinciguerra7e713f12018-11-26 12:04:48 -0800443class VPPType(object):
Ole Troana7564e82018-06-12 21:06:44 +0200444 # Set everything up to be able to pack / unpack
445 def __init__(self, name, msgdef):
446 self.name = name
447 self.msgdef = msgdef
448 self.packers = []
449 self.fields = []
450 self.fieldtypes = []
451 self.field_by_name = {}
452 size = 0
453 for i, f in enumerate(msgdef):
454 if type(f) is dict and 'crc' in f:
455 self.crc = f['crc']
456 continue
457 f_type, f_name = f[:2]
458 self.fields.append(f_name)
459 self.field_by_name[f_name] = None
460 self.fieldtypes.append(f_type)
461 if f_type not in types:
462 logger.debug('Unknown type {}'.format(f_type))
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800463 raise VPPSerializerValueError(
464 'Unknown message type {}'.format(f_type))
Ole Troan9ac11382019-04-23 17:11:01 +0200465
Ole Troan85465582019-04-30 10:04:36 +0200466 fieldlen = len(f)
Ole Troan9ac11382019-04-23 17:11:01 +0200467 options = [x for x in f if type(x) is dict]
468 if len(options):
469 self.options = options[0]
Ole Troan85465582019-04-30 10:04:36 +0200470 fieldlen -= 1
Ole Troan9ac11382019-04-23 17:11:01 +0200471 else:
472 self.options = {}
Ole Troan85465582019-04-30 10:04:36 +0200473 if fieldlen == 3: # list
Ole Troana7564e82018-06-12 21:06:44 +0200474 list_elements = f[2]
475 if list_elements == 0:
476 p = VLAList_legacy(f_name, f_type)
477 self.packers.append(p)
Ole Troan413f4a52018-11-28 11:36:05 +0100478 elif f_type == 'u8' or f_type == 'string':
Ole Troana7564e82018-06-12 21:06:44 +0200479 p = FixedList_u8(f_name, f_type, list_elements)
480 self.packers.append(p)
481 size += p.size
482 else:
483 p = FixedList(f_name, f_type, list_elements)
484 self.packers.append(p)
485 size += p.size
Ole Troan85465582019-04-30 10:04:36 +0200486 elif fieldlen == 4: # Variable length list
Ole Troan413f4a52018-11-28 11:36:05 +0100487 length_index = self.fields.index(f[3])
488 p = VLAList(f_name, f_type, f[3], length_index)
489 self.packers.append(p)
Ole Troana7564e82018-06-12 21:06:44 +0200490 else:
Ole Troan85465582019-04-30 10:04:36 +0200491 p = types[f_type](self.options)
Ole Troan9ac11382019-04-23 17:11:01 +0200492 self.packers.append(p)
493 size += p.size
Ole Troana7564e82018-06-12 21:06:44 +0200494
495 self.size = size
496 self.tuple = collections.namedtuple(name, self.fields, rename=True)
497 types[name] = self
Ole Troana7564e82018-06-12 21:06:44 +0200498
Ole Troan85465582019-04-30 10:04:36 +0200499 def __call__(self, args):
500 self.options = args
501 return self
502
Ole Troana7564e82018-06-12 21:06:44 +0200503 def pack(self, data, kwargs=None):
504 if not kwargs:
505 kwargs = data
Ole Troana7564e82018-06-12 21:06:44 +0200506 b = bytes()
Ole Troan31555a32018-10-22 09:30:26 +0200507
Ole Troan0bcad322018-12-11 13:04:01 +0100508 # Try one of the format functions
509 if data and conversion_required(data, self.name):
510 return conversion_packer(data, self.name)
511
512 for i, a in enumerate(self.fields):
Ole Troan31555a32018-10-22 09:30:26 +0200513 if data and type(data) is not dict and a not in data:
Paul Vinciguerra6ccc6e92018-11-27 08:15:22 -0800514 raise VPPSerializerValueError(
515 "Invalid argument: {} expected {}.{}".
516 format(data, self.name, a))
Ole Troan31555a32018-10-22 09:30:26 +0200517
518 # Defaulting to zero.
519 if not data or a not in data: # Default to 0
520 arg = None
521 kwarg = None # No default for VLA
522 else:
523 arg = data[a]
524 kwarg = kwargs[a] if a in kwargs else None
Ole Troana7564e82018-06-12 21:06:44 +0200525 if isinstance(self.packers[i], VPPType):
Ole Troan0bcad322018-12-11 13:04:01 +0100526 b += self.packers[i].pack(arg, kwarg)
Ole Troana7564e82018-06-12 21:06:44 +0200527 else:
Ole Troan31555a32018-10-22 09:30:26 +0200528 b += self.packers[i].pack(arg, kwargs)
529
Ole Troana7564e82018-06-12 21:06:44 +0200530 return b
531
Ole Troan0bcad322018-12-11 13:04:01 +0100532 def unpack(self, data, offset=0, result=None, ntc=False):
Ole Troana7564e82018-06-12 21:06:44 +0200533 # Return a list of arguments
534 result = []
Ole Troanc84cbad2018-09-06 22:58:05 +0200535 total = 0
Ole Troana7564e82018-06-12 21:06:44 +0200536 for p in self.packers:
Ole Troan0bcad322018-12-11 13:04:01 +0100537 x, size = p.unpack(data, offset, result, ntc)
Ole Troana7564e82018-06-12 21:06:44 +0200538 if type(x) is tuple and len(x) == 1:
539 x = x[0]
540 result.append(x)
Ole Troanc84cbad2018-09-06 22:58:05 +0200541 offset += size
542 total += size
543 t = self.tuple._make(result)
Ole Troan0bcad322018-12-11 13:04:01 +0100544 if not ntc:
545 t = conversion_unpacker(t, self.name)
Ole Troanc84cbad2018-09-06 22:58:05 +0200546 return t, total
Ole Troana7564e82018-06-12 21:06:44 +0200547
548
549class VPPMessage(VPPType):
550 pass