blob: a69cd62a0e8fa5979d682440d1341bfb1e61432d [file] [log] [blame]
Rich Lane15cbe842013-04-26 16:04:11 -07001:: # Copyright 2013, Big Switch Networks, Inc.
2:: #
3:: # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4:: # the following special exception:
5:: #
6:: # LOXI Exception
7:: #
8:: # As a special exception to the terms of the EPL, you may distribute libraries
9:: # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10:: # that copyright and licensing notices generated by LoxiGen are not altered or removed
11:: # from the LoxiGen Libraries and the notice provided below is (i) included in
12:: # the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13:: # documentation for the LoxiGen Libraries, if distributed in binary form.
14:: #
15:: # Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16:: #
17:: # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18:: # a copy of the EPL at:
19:: #
20:: # http://www.eclipse.org/legal/epl-v10.html
21:: #
22:: # Unless required by applicable law or agreed to in writing, software
23:: # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24:: # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25:: # EPL for the specific language governing permissions and limitations
26:: # under the EPL.
27::
28:: include('_copyright.py')
29"""
30Utility functions independent of the protocol version
31"""
32
33:: include('_autogen.py')
34
35import loxi
36import struct
37
38def unpack_array(deserializer, element_size, buf):
39 """
40 Deserialize an array of fixed length elements.
41 The deserializer function should take a buffer and return the new object.
42 """
43 if len(buf) % element_size != 0: raise loxi.ProtocolError("invalid array length")
44 n = len(buf) / element_size
45 return [deserializer(buffer(buf, i*element_size, element_size)) for i in range(n)]
46
47def unpack_list(deserializer, length_fmt, buf, extra_len=0):
48 """
49 Deserialize a list of variable-length entries.
50 'length_fmt' is a struct format string with exactly one non-padding format
51 character that returns the length of the given element, minus extra_len.
52 The deserializer function should take a buffer and return the new object.
53 """
54 entries = []
55 offset = 0
56 length_struct = struct.Struct(length_fmt)
57 n = len(buf)
58 while offset < n:
59 if offset + length_struct.size > len(buf): raise loxi.ProtocolError("entry header overruns list length")
60 length, = length_struct.unpack_from(buf, offset)
61 length += extra_len
62 if length < length_struct.size: raise loxi.ProtocolError("entry length is less than the header length")
63 if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
64 entries.append(deserializer(buffer(buf, offset, length)))
65 offset += length
66 return entries
Rich Lane1de06ab2013-04-26 16:58:37 -070067
68class OFReader(object):
69 """
70 Cursor over a read-only buffer
71
72 OpenFlow messages are best thought of as a sequence of elements of
73 variable size, rather than a C-style struct with fixed offsets and
74 known field lengths. This class supports efficiently reading
75 fields sequentially and is intended to be used recursively by the
76 parsers of child objects which will implicitly update the offset.
77 """
78 def __init__(self, buf):
79 self.buf = buf
80 self.offset = 0
81
82 def read(self, fmt):
83 st = struct.Struct(fmt)
84 if self.offset + st.size > len(self.buf):
85 raise loxi.ProtocolError("Buffer too short")
86 result = st.unpack_from(self.buf, self.offset)
87 self.offset += st.size
88 return result
89
90 def read_all(self):
91 buf = buffer(self.buf, self.offset)
92 self.offset += len(buf)
93 return str(buf)
94
95 def peek(self, fmt):
96 st = struct.Struct(fmt)
97 if self.offset + st.size > len(self.buf):
98 raise loxi.ProtocolError("Buffer too short")
99 result = st.unpack_from(self.buf, self.offset)
100 return result
101
102 def skip(self, length):
103 if self.offset + length > len(self.buf):
104 raise loxi.ProtocolError("Buffer too short")
105 self.offset += length
106
107 def is_empty(self):
108 return self.offset == len(self.buf)
109
110 # Used when parsing variable length objects which have external length
111 # fields (e.g. the actions list in an OF 1.0 packet-out message).
112 def slice(self, length):
113 if self.offset + length > len(self.buf):
114 raise loxi.ProtocolError("Buffer too short")
115 buf = OFReader(buffer(self.buf, self.offset, length))
116 self.offset += length
117 return buf