1 # Copyright 2014 The Android Open Source Project
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 """A simple module for declaring C-like structures.
19 >>> # Declare a struct type by specifying name, field formats and field names.
20 ... # Field formats are the same as those used in the struct module.
22 >>> NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid")
25 >>> # Create instances from tuples or raw bytes. Data past the end is ignored.
26 ... n1 = NLMsgHdr((44, 32, 0x2, 0, 491))
28 NLMsgHdr(length=44, type=32, flags=2, seq=0, pid=491)
30 >>> n2 = NLMsgHdr("\x2c\x00\x00\x00\x21\x00\x02\x00"
31 ... "\x00\x00\x00\x00\xfe\x01\x00\x00" + "junk at end")
33 NLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510)
35 >>> # Serialize to raw bytes.
36 ... print n1.Pack().encode("hex")
37 2c0000002000020000000000eb010000
39 >>> # Parse the beginning of a byte stream as a struct, and return the struct
40 ... # and the remainder of the stream for further reading.
41 ... data = ("\x2c\x00\x00\x00\x21\x00\x02\x00"
42 ... "\x00\x00\x00\x00\xfe\x01\x00\x00"
44 >>> cstruct.Read(data, NLMsgHdr)
45 (NLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510), 'more data')
53 def Struct(name, fmt, fields):
54 """Function that returns struct classes."""
61 def __init__(cls, unused_name, unused_bases, namespace):
62 # Make the class object have the name that's passed in.
63 type.__init__(cls, namespace["_name"], unused_bases, namespace)
65 class CStruct(object):
66 """Class representing a C-like structure."""
74 _length = struct.calcsize(_format)
75 if isinstance(_fields, str):
76 _fields = _fields.split(" ")
78 def _SetValues(self, values):
79 super(CStruct, self).__setattr__("_values", list(values))
81 def _Parse(self, data):
82 data = data[:self._length]
83 values = list(struct.unpack(self._format, data))
84 self._SetValues(values)
86 def __init__(self, values):
87 # Initializing from a string.
88 if isinstance(values, str):
89 if len(values) < self._length:
90 raise TypeError("%s requires string of length %d, got %d" %
91 (self._name, self._length, len(values)))
94 # Initializing from a tuple.
95 if len(values) != len(self._fields):
96 raise TypeError("%s has exactly %d fields (%d given)" %
97 (self._name, len(self._fields), len(values)))
98 self._SetValues(values)
100 def _FieldIndex(self, attr):
102 return self._fields.index(attr)
104 raise AttributeError("'%s' has no attribute '%s'" %
107 def __getattr__(self, name):
108 return self._values[self._FieldIndex(name)]
110 def __setattr__(self, name, value):
111 self._values[self._FieldIndex(name)] = value
118 return struct.pack(self._format, *self._values)
121 return "%s(%s)" % (self._name, ", ".join(
122 "%s=%s" % (i, v) for i, v in zip(self._fields, self._values)))
128 """Returns a C pointer to the serialized structure."""
129 buf = ctypes.create_string_buffer(self.Pack())
130 # Store the C buffer in the object so it doesn't get garbage collected.
131 super(CStruct, self).__setattr__("_buffer", buf)
132 return ctypes.addressof(self._buffer)
137 def Read(data, struct_type):
138 length = len(struct_type)
139 return struct_type(data), data[length:]