pyloxi: factor out generic list parsing code
diff --git a/py_gen/templates/action.py b/py_gen/templates/action.py
index 439e56f..8f08ced 100644
--- a/py_gen/templates/action.py
+++ b/py_gen/templates/action.py
@@ -37,18 +37,13 @@
def unpack_list(buf):
if len(buf) % 8 != 0: raise loxi.ProtocolError("action list length not a multiple of 8")
- actions = []
- offset = 0
- while offset < len(buf):
- type, length = struct.unpack_from("!HH", buf, offset)
- if length == 0: raise loxi.ProtocolError("action length is 0")
+ def deserializer(buf):
+ type, length = struct.unpack_from("!HH", buf)
if length % 8 != 0: raise loxi.ProtocolError("action length not a multiple of 8")
- if offset + length > len(buf): raise loxi.ProtocolError("action length overruns list length")
parser = parsers.get(type)
if not parser: raise loxi.ProtocolError("unknown action type %d" % type)
- actions.append(parser(buffer(buf, offset, length)))
- offset += length
- return actions
+ return parser(buf)
+ return util.unpack_list(deserializer, "!2xH", buf)
class Action(object):
type = None # override in subclass
diff --git a/py_gen/templates/common.py b/py_gen/templates/common.py
index 08fb65d..fbcec25 100644
--- a/py_gen/templates/common.py
+++ b/py_gen/templates/common.py
@@ -39,41 +39,19 @@
common = sys.modules[__name__]
def unpack_list_flow_stats_entry(buf):
- entries = []
- offset = 0
- while offset < len(buf):
- length, = struct.unpack_from("!H", buf, offset)
- if length == 0: raise loxi.ProtocolError("entry length is 0")
- if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
- entries.append(flow_stats_entry.unpack(buffer(buf, offset, length)))
- offset += length
- return entries
+ return util.unpack_list(flow_stats_entry.unpack, "!H", buf)
def unpack_list_queue_prop(buf):
- entries = []
- offset = 0
- while offset < len(buf):
- type, length, = struct.unpack_from("!HH", buf, offset)
- if length == 0: raise loxi.ProtocolError("entry length is 0")
- if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
+ def deserializer(buf):
+ type, = struct.unpack_from("!H", buf)
if type == const.OFPQT_MIN_RATE:
- entry = queue_prop_min_rate.unpack(buffer(buf, offset, length))
+ return queue_prop_min_rate.unpack(buf)
else:
raise loxi.ProtocolError("unknown queue prop %d" % type)
- entries.append(entry)
- offset += length
- return entries
+ return util.unpack_list(deserializer, "!2xH", buf)
def unpack_list_packet_queue(buf):
- entries = []
- offset = 0
- while offset < len(buf):
- _, length, = struct.unpack_from("!LH", buf, offset)
- if length == 0: raise loxi.ProtocolError("entry length is 0")
- if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
- entries.append(packet_queue.unpack(buffer(buf, offset, length)))
- offset += length
- return entries
+ return util.unpack_list(packet_queue.unpack, "!4xH", buf)
:: for ofclass in ofclasses:
class ${ofclass.pyname}(object):
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index 5933e14..bfe11d0 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -31,6 +31,7 @@
import loxi
import const
+import struct
def unpack_array(deserializer, element_size, buf):
"""
@@ -41,6 +42,25 @@
n = len(buf) / element_size
return [deserializer(buffer(buf, i*element_size, element_size)) for i in range(n)]
+def unpack_list(deserializer, length_fmt, buf):
+ """
+ Deserialize a list of variable-length entries.
+ 'length_fmt' is a struct format string with exactly one non-padding format
+ character that returns the length of the given element.
+ The deserializer function should take a buffer and return the new object.
+ """
+ entries = []
+ offset = 0
+ length_struct = struct.Struct(length_fmt)
+ while offset < len(buf):
+ if offset + length_struct.size > len(buf): raise loxi.ProtocolError("entry header overruns list length")
+ length, = length_struct.unpack_from(buf, offset)
+ if length < length_struct.size: raise loxi.ProtocolError("entry length is less than the header length")
+ if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
+ entries.append(deserializer(buffer(buf, offset, length)))
+ offset += length
+ return entries
+
def pretty_mac(mac):
return ':'.join(["%02x" % x for x in mac])
diff --git a/py_gen/tests.py b/py_gen/tests.py
index 23d40b8..68f8b52 100644
--- a/py_gen/tests.py
+++ b/py_gen/tests.py
@@ -153,7 +153,7 @@
import loxi.of10 as ofp
buf = '\x00' * 8
- with self.assertRaisesRegexp(ofp.ProtocolError, 'is 0'):
+ with self.assertRaisesRegexp(ofp.ProtocolError, 'is less than the header length'):
ofp.action.unpack_list(buf)
buf = '\x00\x00\x00\x04'