xref: /aosp_15_r20/kernel/tests/net/test/cstruct.py (revision 2f2c4c7ab4226c71756b9c31670392fdd6887c4f)
1*2f2c4c7aSAndroid Build Coastguard Worker# Copyright 2014 The Android Open Source Project
2*2f2c4c7aSAndroid Build Coastguard Worker#
3*2f2c4c7aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*2f2c4c7aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*2f2c4c7aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*2f2c4c7aSAndroid Build Coastguard Worker#
7*2f2c4c7aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0
8*2f2c4c7aSAndroid Build Coastguard Worker#
9*2f2c4c7aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*2f2c4c7aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*2f2c4c7aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*2f2c4c7aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*2f2c4c7aSAndroid Build Coastguard Worker# limitations under the License.
14*2f2c4c7aSAndroid Build Coastguard Worker
15*2f2c4c7aSAndroid Build Coastguard Worker"""A simple module for declaring C-like structures.
16*2f2c4c7aSAndroid Build Coastguard Worker
17*2f2c4c7aSAndroid Build Coastguard WorkerExample usage:
18*2f2c4c7aSAndroid Build Coastguard Worker
19*2f2c4c7aSAndroid Build Coastguard Worker>>> # Declare a struct type by specifying name, field formats and field names.
20*2f2c4c7aSAndroid Build Coastguard Worker... # Field formats are the same as those used in the struct module, except:
21*2f2c4c7aSAndroid Build Coastguard Worker... # - S: Nested Struct.
22*2f2c4c7aSAndroid Build Coastguard Worker... # - A: NULL-padded ASCII string. Like s, but printing ignores contiguous
23*2f2c4c7aSAndroid Build Coastguard Worker... #      trailing NULL blocks at the end.
24*2f2c4c7aSAndroid Build Coastguard Worker... import cstruct
25*2f2c4c7aSAndroid Build Coastguard Worker>>> NLMsgHdr = cstruct.Struct("NLMsgHdr", "=LHHLL", "length type flags seq pid")
26*2f2c4c7aSAndroid Build Coastguard Worker>>>
27*2f2c4c7aSAndroid Build Coastguard Worker>>>
28*2f2c4c7aSAndroid Build Coastguard Worker>>> # Create instances from a tuple of values, raw bytes, zero-initialized, or
29*2f2c4c7aSAndroid Build Coastguard Worker>>> # using keywords.
30*2f2c4c7aSAndroid Build Coastguard Worker... n1 = NLMsgHdr((44, 32, 0x2, 0, 491))
31*2f2c4c7aSAndroid Build Coastguard Worker>>> print(n1)
32*2f2c4c7aSAndroid Build Coastguard WorkerNLMsgHdr(length=44, type=32, flags=2, seq=0, pid=491)
33*2f2c4c7aSAndroid Build Coastguard Worker>>>
34*2f2c4c7aSAndroid Build Coastguard Worker>>> n2 = NLMsgHdr("\x2c\x00\x00\x00\x21\x00\x02\x00"
35*2f2c4c7aSAndroid Build Coastguard Worker...               "\x00\x00\x00\x00\xfe\x01\x00\x00" + "junk at end")
36*2f2c4c7aSAndroid Build Coastguard Worker>>> print(n2)
37*2f2c4c7aSAndroid Build Coastguard WorkerNLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510)
38*2f2c4c7aSAndroid Build Coastguard Worker>>>
39*2f2c4c7aSAndroid Build Coastguard Worker>>> n3 = netlink.NLMsgHdr() # Zero-initialized
40*2f2c4c7aSAndroid Build Coastguard Worker>>> print(n3)
41*2f2c4c7aSAndroid Build Coastguard WorkerNLMsgHdr(length=0, type=0, flags=0, seq=0, pid=0)
42*2f2c4c7aSAndroid Build Coastguard Worker>>>
43*2f2c4c7aSAndroid Build Coastguard Worker>>> n4 = netlink.NLMsgHdr(length=44, type=33) # Other fields zero-initialized
44*2f2c4c7aSAndroid Build Coastguard Worker>>> print(n4)
45*2f2c4c7aSAndroid Build Coastguard WorkerNLMsgHdr(length=44, type=33, flags=0, seq=0, pid=0)
46*2f2c4c7aSAndroid Build Coastguard Worker>>>
47*2f2c4c7aSAndroid Build Coastguard Worker>>> # Serialize to raw bytes.
48*2f2c4c7aSAndroid Build Coastguard Worker... print(n1.Pack().encode("hex"))
49*2f2c4c7aSAndroid Build Coastguard Worker2c0000002000020000000000eb010000
50*2f2c4c7aSAndroid Build Coastguard Worker>>>
51*2f2c4c7aSAndroid Build Coastguard Worker>>> # Parse the beginning of a byte stream as a struct, and return the struct
52*2f2c4c7aSAndroid Build Coastguard Worker... # and the remainder of the stream for further reading.
53*2f2c4c7aSAndroid Build Coastguard Worker... data = ("\x2c\x00\x00\x00\x21\x00\x02\x00"
54*2f2c4c7aSAndroid Build Coastguard Worker...         "\x00\x00\x00\x00\xfe\x01\x00\x00"
55*2f2c4c7aSAndroid Build Coastguard Worker...         "more data")
56*2f2c4c7aSAndroid Build Coastguard Worker>>> cstruct.Read(data, NLMsgHdr)
57*2f2c4c7aSAndroid Build Coastguard Worker(NLMsgHdr(length=44, type=33, flags=2, seq=0, pid=510), 'more data')
58*2f2c4c7aSAndroid Build Coastguard Worker>>>
59*2f2c4c7aSAndroid Build Coastguard Worker>>> # Structs can contain one or more nested structs. The nested struct types
60*2f2c4c7aSAndroid Build Coastguard Worker... # are specified in a list as an optional last argument. Nested structs may
61*2f2c4c7aSAndroid Build Coastguard Worker... # contain nested structs.
62*2f2c4c7aSAndroid Build Coastguard Worker... S = cstruct.Struct("S", "=BI", "byte1 int2")
63*2f2c4c7aSAndroid Build Coastguard Worker>>> N = cstruct.Struct("N", "!BSiS", "byte1 s2 int3 s2", [S, S])
64*2f2c4c7aSAndroid Build Coastguard Worker>>> NN = cstruct.Struct("NN", "SHS", "s1 word2 n3", [S, N])
65*2f2c4c7aSAndroid Build Coastguard Worker>>> nn = NN((S((1, 25000)), -29876, N((55, S((5, 6)), 1111, S((7, 8))))))
66*2f2c4c7aSAndroid Build Coastguard Worker>>> nn.n3.s2.int2 = 5
67*2f2c4c7aSAndroid Build Coastguard Worker>>>
68*2f2c4c7aSAndroid Build Coastguard Worker"""
69*2f2c4c7aSAndroid Build Coastguard Worker
70*2f2c4c7aSAndroid Build Coastguard Workerimport binascii
71*2f2c4c7aSAndroid Build Coastguard Workerimport ctypes
72*2f2c4c7aSAndroid Build Coastguard Workerimport string
73*2f2c4c7aSAndroid Build Coastguard Workerimport struct
74*2f2c4c7aSAndroid Build Coastguard Workerimport re
75*2f2c4c7aSAndroid Build Coastguard Worker
76*2f2c4c7aSAndroid Build Coastguard Worker
77*2f2c4c7aSAndroid Build Coastguard Workerdef _PythonFormat(fmt):
78*2f2c4c7aSAndroid Build Coastguard Worker  if "A" in fmt:
79*2f2c4c7aSAndroid Build Coastguard Worker    fmt = fmt.replace("A", "s")
80*2f2c4c7aSAndroid Build Coastguard Worker  return re.split('\d+$', fmt)[0]
81*2f2c4c7aSAndroid Build Coastguard Worker
82*2f2c4c7aSAndroid Build Coastguard Workerdef CalcSize(fmt):
83*2f2c4c7aSAndroid Build Coastguard Worker  return struct.calcsize(_PythonFormat(fmt))
84*2f2c4c7aSAndroid Build Coastguard Worker
85*2f2c4c7aSAndroid Build Coastguard Workerdef CalcNumElements(fmt):
86*2f2c4c7aSAndroid Build Coastguard Worker  fmt = _PythonFormat(fmt)
87*2f2c4c7aSAndroid Build Coastguard Worker  prevlen = len(fmt)
88*2f2c4c7aSAndroid Build Coastguard Worker  fmt = fmt.replace("S", "")
89*2f2c4c7aSAndroid Build Coastguard Worker  numstructs = prevlen - len(fmt)
90*2f2c4c7aSAndroid Build Coastguard Worker  size = struct.calcsize(fmt)
91*2f2c4c7aSAndroid Build Coastguard Worker  elements = struct.unpack(fmt, b"\x00" * size)
92*2f2c4c7aSAndroid Build Coastguard Worker  return len(elements) + numstructs
93*2f2c4c7aSAndroid Build Coastguard Worker
94*2f2c4c7aSAndroid Build Coastguard Worker
95*2f2c4c7aSAndroid Build Coastguard Workerclass StructMetaclass(type):
96*2f2c4c7aSAndroid Build Coastguard Worker
97*2f2c4c7aSAndroid Build Coastguard Worker  def __len__(cls):
98*2f2c4c7aSAndroid Build Coastguard Worker    return cls._length
99*2f2c4c7aSAndroid Build Coastguard Worker
100*2f2c4c7aSAndroid Build Coastguard Worker  def __init__(cls, unused_name, unused_bases, namespace):
101*2f2c4c7aSAndroid Build Coastguard Worker    # Make the class object have the name that's passed in.
102*2f2c4c7aSAndroid Build Coastguard Worker    type.__init__(cls, namespace["_name"], unused_bases, namespace)
103*2f2c4c7aSAndroid Build Coastguard Worker
104*2f2c4c7aSAndroid Build Coastguard Worker
105*2f2c4c7aSAndroid Build Coastguard Workerdef Struct(name, fmt, fieldnames, substructs={}):
106*2f2c4c7aSAndroid Build Coastguard Worker  """Function that returns struct classes."""
107*2f2c4c7aSAndroid Build Coastguard Worker
108*2f2c4c7aSAndroid Build Coastguard Worker  # Hack to make struct classes use the StructMetaclass class on both python2 and
109*2f2c4c7aSAndroid Build Coastguard Worker  # python3. This is needed because in python2 the metaclass is assigned in the
110*2f2c4c7aSAndroid Build Coastguard Worker  # class definition, but in python3 it's passed into the constructor via
111*2f2c4c7aSAndroid Build Coastguard Worker  # keyword argument. Works by making all structs subclass CStructSuperclass,
112*2f2c4c7aSAndroid Build Coastguard Worker  # whose __new__ method uses StructMetaclass as its metaclass.
113*2f2c4c7aSAndroid Build Coastguard Worker  #
114*2f2c4c7aSAndroid Build Coastguard Worker  # A better option would be to use six.with_metaclass, but the existing python2
115*2f2c4c7aSAndroid Build Coastguard Worker  # VM image doesn't have the six module.
116*2f2c4c7aSAndroid Build Coastguard Worker  CStructSuperclass = type.__new__(StructMetaclass, 'unused', (), {})
117*2f2c4c7aSAndroid Build Coastguard Worker
118*2f2c4c7aSAndroid Build Coastguard Worker  class CStruct(CStructSuperclass):
119*2f2c4c7aSAndroid Build Coastguard Worker    """Class representing a C-like structure."""
120*2f2c4c7aSAndroid Build Coastguard Worker
121*2f2c4c7aSAndroid Build Coastguard Worker    # Name of the struct.
122*2f2c4c7aSAndroid Build Coastguard Worker    _name = name
123*2f2c4c7aSAndroid Build Coastguard Worker    # List of field names.
124*2f2c4c7aSAndroid Build Coastguard Worker    _fieldnames = fieldnames
125*2f2c4c7aSAndroid Build Coastguard Worker    # Dict mapping field indices to nested struct classes.
126*2f2c4c7aSAndroid Build Coastguard Worker    _nested = {}
127*2f2c4c7aSAndroid Build Coastguard Worker    # List of string fields that are ASCII strings.
128*2f2c4c7aSAndroid Build Coastguard Worker    _asciiz = set()
129*2f2c4c7aSAndroid Build Coastguard Worker
130*2f2c4c7aSAndroid Build Coastguard Worker    _fieldnames = _fieldnames.split(" ")
131*2f2c4c7aSAndroid Build Coastguard Worker
132*2f2c4c7aSAndroid Build Coastguard Worker    # Parse fmt into _format, converting any S format characters to "XXs",
133*2f2c4c7aSAndroid Build Coastguard Worker    # where XX is the length of the struct type's packed representation.
134*2f2c4c7aSAndroid Build Coastguard Worker    _format = ""
135*2f2c4c7aSAndroid Build Coastguard Worker    laststructindex = 0
136*2f2c4c7aSAndroid Build Coastguard Worker    for i in range(len(fmt)):
137*2f2c4c7aSAndroid Build Coastguard Worker      if fmt[i] == "S":
138*2f2c4c7aSAndroid Build Coastguard Worker        # Nested struct. Record the index in our struct it should go into.
139*2f2c4c7aSAndroid Build Coastguard Worker        index = CalcNumElements(fmt[:i])
140*2f2c4c7aSAndroid Build Coastguard Worker        _nested[index] = substructs[laststructindex]
141*2f2c4c7aSAndroid Build Coastguard Worker        laststructindex += 1
142*2f2c4c7aSAndroid Build Coastguard Worker        _format += "%ds" % len(_nested[index])
143*2f2c4c7aSAndroid Build Coastguard Worker      elif fmt[i] == "A":
144*2f2c4c7aSAndroid Build Coastguard Worker        # Null-terminated ASCII string. Remove digits before the A, so we don't
145*2f2c4c7aSAndroid Build Coastguard Worker        # call CalcNumElements on an (invalid) format that ends with a digit.
146*2f2c4c7aSAndroid Build Coastguard Worker        start = i
147*2f2c4c7aSAndroid Build Coastguard Worker        while start > 0 and fmt[start - 1].isdigit(): start -= 1
148*2f2c4c7aSAndroid Build Coastguard Worker        index = CalcNumElements(fmt[:start])
149*2f2c4c7aSAndroid Build Coastguard Worker        _asciiz.add(index)
150*2f2c4c7aSAndroid Build Coastguard Worker        _format += "s"
151*2f2c4c7aSAndroid Build Coastguard Worker      else:
152*2f2c4c7aSAndroid Build Coastguard Worker        # Standard struct format character.
153*2f2c4c7aSAndroid Build Coastguard Worker        _format += fmt[i]
154*2f2c4c7aSAndroid Build Coastguard Worker
155*2f2c4c7aSAndroid Build Coastguard Worker    _length = CalcSize(_format)
156*2f2c4c7aSAndroid Build Coastguard Worker
157*2f2c4c7aSAndroid Build Coastguard Worker    offset_list = [0]
158*2f2c4c7aSAndroid Build Coastguard Worker    last_offset = 0
159*2f2c4c7aSAndroid Build Coastguard Worker    for i in range(len(_format)):
160*2f2c4c7aSAndroid Build Coastguard Worker      offset = CalcSize(_format[:i])
161*2f2c4c7aSAndroid Build Coastguard Worker      if offset > last_offset:
162*2f2c4c7aSAndroid Build Coastguard Worker        last_offset = offset
163*2f2c4c7aSAndroid Build Coastguard Worker        offset_list.append(offset)
164*2f2c4c7aSAndroid Build Coastguard Worker
165*2f2c4c7aSAndroid Build Coastguard Worker    # A dictionary that maps field names to their offsets in the struct.
166*2f2c4c7aSAndroid Build Coastguard Worker    _offsets = dict(list(zip(_fieldnames, offset_list)))
167*2f2c4c7aSAndroid Build Coastguard Worker
168*2f2c4c7aSAndroid Build Coastguard Worker    # Check that the number of field names matches the number of fields.
169*2f2c4c7aSAndroid Build Coastguard Worker    numfields = len(struct.unpack(_format, b"\x00" * _length))
170*2f2c4c7aSAndroid Build Coastguard Worker    if len(_fieldnames) != numfields:
171*2f2c4c7aSAndroid Build Coastguard Worker      raise ValueError("Invalid cstruct: \"%s\" has %d elements, \"%s\" has %d."
172*2f2c4c7aSAndroid Build Coastguard Worker                       % (fmt, numfields, fieldnames, len(_fieldnames)))
173*2f2c4c7aSAndroid Build Coastguard Worker
174*2f2c4c7aSAndroid Build Coastguard Worker    def _SetValues(self, values):
175*2f2c4c7aSAndroid Build Coastguard Worker      # Replace self._values with the given list. We can't do direct assignment
176*2f2c4c7aSAndroid Build Coastguard Worker      # because of the __setattr__ overload on this class.
177*2f2c4c7aSAndroid Build Coastguard Worker      super(CStruct, self).__setattr__("_values", list(values))
178*2f2c4c7aSAndroid Build Coastguard Worker
179*2f2c4c7aSAndroid Build Coastguard Worker    def _Parse(self, data):
180*2f2c4c7aSAndroid Build Coastguard Worker      data = data[:self._length]
181*2f2c4c7aSAndroid Build Coastguard Worker      values = list(struct.unpack(self._format, data))
182*2f2c4c7aSAndroid Build Coastguard Worker      for index, value in enumerate(values):
183*2f2c4c7aSAndroid Build Coastguard Worker        if isinstance(value, bytes) and index in self._nested:
184*2f2c4c7aSAndroid Build Coastguard Worker          values[index] = self._nested[index](value)
185*2f2c4c7aSAndroid Build Coastguard Worker      self._SetValues(values)
186*2f2c4c7aSAndroid Build Coastguard Worker
187*2f2c4c7aSAndroid Build Coastguard Worker    def __init__(self, tuple_or_bytes=None, **kwargs):
188*2f2c4c7aSAndroid Build Coastguard Worker      """Construct an instance of this Struct.
189*2f2c4c7aSAndroid Build Coastguard Worker
190*2f2c4c7aSAndroid Build Coastguard Worker      1. With no args, the whole struct is zero-initialized.
191*2f2c4c7aSAndroid Build Coastguard Worker      2. With keyword args, the matching fields are populated; rest are zeroed.
192*2f2c4c7aSAndroid Build Coastguard Worker      3. With one tuple as the arg, the fields are assigned based on position.
193*2f2c4c7aSAndroid Build Coastguard Worker      4. With one bytes arg, the Struct is parsed from bytes.
194*2f2c4c7aSAndroid Build Coastguard Worker      """
195*2f2c4c7aSAndroid Build Coastguard Worker      if tuple_or_bytes and kwargs:
196*2f2c4c7aSAndroid Build Coastguard Worker        raise TypeError(
197*2f2c4c7aSAndroid Build Coastguard Worker            "%s: cannot specify both a tuple and keyword args" % self._name)
198*2f2c4c7aSAndroid Build Coastguard Worker
199*2f2c4c7aSAndroid Build Coastguard Worker      if tuple_or_bytes is None:
200*2f2c4c7aSAndroid Build Coastguard Worker        # Default construct from null bytes.
201*2f2c4c7aSAndroid Build Coastguard Worker        self._Parse(b"\x00" * len(self))
202*2f2c4c7aSAndroid Build Coastguard Worker        # If any keywords were supplied, set those fields.
203*2f2c4c7aSAndroid Build Coastguard Worker        for k, v in kwargs.items():
204*2f2c4c7aSAndroid Build Coastguard Worker          setattr(self, k, v)
205*2f2c4c7aSAndroid Build Coastguard Worker      elif isinstance(tuple_or_bytes, bytes):
206*2f2c4c7aSAndroid Build Coastguard Worker        # Initializing from bytes.
207*2f2c4c7aSAndroid Build Coastguard Worker        if len(tuple_or_bytes) < self._length:
208*2f2c4c7aSAndroid Build Coastguard Worker          raise TypeError("%s requires a bytes object of length %d, got %d" %
209*2f2c4c7aSAndroid Build Coastguard Worker                          (self._name, self._length, len(tuple_or_bytes)))
210*2f2c4c7aSAndroid Build Coastguard Worker        self._Parse(tuple_or_bytes)
211*2f2c4c7aSAndroid Build Coastguard Worker      else:
212*2f2c4c7aSAndroid Build Coastguard Worker        # Initializing from a tuple.
213*2f2c4c7aSAndroid Build Coastguard Worker        if len(tuple_or_bytes) != len(self._fieldnames):
214*2f2c4c7aSAndroid Build Coastguard Worker          raise TypeError("%s has exactly %d fieldnames: (%s), %d given: (%s)" %
215*2f2c4c7aSAndroid Build Coastguard Worker                          (self._name, len(self._fieldnames),
216*2f2c4c7aSAndroid Build Coastguard Worker                           ", ".join(self._fieldnames), len(tuple_or_bytes),
217*2f2c4c7aSAndroid Build Coastguard Worker                           ", ".join(str(x) for x in tuple_or_bytes)))
218*2f2c4c7aSAndroid Build Coastguard Worker        self._SetValues(tuple_or_bytes)
219*2f2c4c7aSAndroid Build Coastguard Worker
220*2f2c4c7aSAndroid Build Coastguard Worker    def _FieldIndex(self, attr):
221*2f2c4c7aSAndroid Build Coastguard Worker      try:
222*2f2c4c7aSAndroid Build Coastguard Worker        return self._fieldnames.index(attr)
223*2f2c4c7aSAndroid Build Coastguard Worker      except ValueError:
224*2f2c4c7aSAndroid Build Coastguard Worker        raise AttributeError("'%s' has no attribute '%s'" %
225*2f2c4c7aSAndroid Build Coastguard Worker                             (self._name, attr))
226*2f2c4c7aSAndroid Build Coastguard Worker
227*2f2c4c7aSAndroid Build Coastguard Worker    def __getattr__(self, name):
228*2f2c4c7aSAndroid Build Coastguard Worker      return self._values[self._FieldIndex(name)]
229*2f2c4c7aSAndroid Build Coastguard Worker
230*2f2c4c7aSAndroid Build Coastguard Worker    def __setattr__(self, name, value):
231*2f2c4c7aSAndroid Build Coastguard Worker      # TODO: check value type against self._format and throw here, or else
232*2f2c4c7aSAndroid Build Coastguard Worker      # callers get an unhelpful exception when they call Pack().
233*2f2c4c7aSAndroid Build Coastguard Worker      self._values[self._FieldIndex(name)] = value
234*2f2c4c7aSAndroid Build Coastguard Worker
235*2f2c4c7aSAndroid Build Coastguard Worker    def offset(self, name):
236*2f2c4c7aSAndroid Build Coastguard Worker      if "." in name:
237*2f2c4c7aSAndroid Build Coastguard Worker        raise NotImplementedError("offset() on nested field")
238*2f2c4c7aSAndroid Build Coastguard Worker      return self._offsets[name]
239*2f2c4c7aSAndroid Build Coastguard Worker
240*2f2c4c7aSAndroid Build Coastguard Worker    @classmethod
241*2f2c4c7aSAndroid Build Coastguard Worker    def __len__(cls):
242*2f2c4c7aSAndroid Build Coastguard Worker      return cls._length
243*2f2c4c7aSAndroid Build Coastguard Worker
244*2f2c4c7aSAndroid Build Coastguard Worker    def __ne__(self, other):
245*2f2c4c7aSAndroid Build Coastguard Worker      return not self.__eq__(other)
246*2f2c4c7aSAndroid Build Coastguard Worker
247*2f2c4c7aSAndroid Build Coastguard Worker    def __eq__(self, other):
248*2f2c4c7aSAndroid Build Coastguard Worker      return (isinstance(other, self.__class__) and
249*2f2c4c7aSAndroid Build Coastguard Worker              self._name == other._name and
250*2f2c4c7aSAndroid Build Coastguard Worker              self._fieldnames == other._fieldnames and
251*2f2c4c7aSAndroid Build Coastguard Worker              self._values == other._values)
252*2f2c4c7aSAndroid Build Coastguard Worker
253*2f2c4c7aSAndroid Build Coastguard Worker    @staticmethod
254*2f2c4c7aSAndroid Build Coastguard Worker    def _MaybePackStruct(value):
255*2f2c4c7aSAndroid Build Coastguard Worker      if isinstance(type(value), StructMetaclass):
256*2f2c4c7aSAndroid Build Coastguard Worker        return value.Pack()
257*2f2c4c7aSAndroid Build Coastguard Worker      else:
258*2f2c4c7aSAndroid Build Coastguard Worker        return value
259*2f2c4c7aSAndroid Build Coastguard Worker
260*2f2c4c7aSAndroid Build Coastguard Worker    def Pack(self):
261*2f2c4c7aSAndroid Build Coastguard Worker      values = [self._MaybePackStruct(v) for v in self._values]
262*2f2c4c7aSAndroid Build Coastguard Worker      return struct.pack(self._format, *values)
263*2f2c4c7aSAndroid Build Coastguard Worker
264*2f2c4c7aSAndroid Build Coastguard Worker    def __str__(self):
265*2f2c4c7aSAndroid Build Coastguard Worker
266*2f2c4c7aSAndroid Build Coastguard Worker      def HasNonPrintableChar(s):
267*2f2c4c7aSAndroid Build Coastguard Worker        for c in s:
268*2f2c4c7aSAndroid Build Coastguard Worker          # Iterating over bytes yields chars in python2 but ints in python3.
269*2f2c4c7aSAndroid Build Coastguard Worker          if isinstance(c, int): c = chr(c)
270*2f2c4c7aSAndroid Build Coastguard Worker          if c not in string.printable: return True
271*2f2c4c7aSAndroid Build Coastguard Worker        return False
272*2f2c4c7aSAndroid Build Coastguard Worker
273*2f2c4c7aSAndroid Build Coastguard Worker      def FieldDesc(index, name, value):
274*2f2c4c7aSAndroid Build Coastguard Worker        if isinstance(value, bytes) or isinstance(value, str):
275*2f2c4c7aSAndroid Build Coastguard Worker          if index in self._asciiz:
276*2f2c4c7aSAndroid Build Coastguard Worker            # TODO: use "backslashreplace" when python 2 is no longer supported.
277*2f2c4c7aSAndroid Build Coastguard Worker            value = value.rstrip(b"\x00").decode(errors="ignore")
278*2f2c4c7aSAndroid Build Coastguard Worker          elif HasNonPrintableChar(value):
279*2f2c4c7aSAndroid Build Coastguard Worker            value = binascii.hexlify(value).decode()
280*2f2c4c7aSAndroid Build Coastguard Worker        return "%s=%s" % (name, str(value))
281*2f2c4c7aSAndroid Build Coastguard Worker
282*2f2c4c7aSAndroid Build Coastguard Worker      descriptions = [
283*2f2c4c7aSAndroid Build Coastguard Worker          FieldDesc(i, n, v) for i, (n, v) in
284*2f2c4c7aSAndroid Build Coastguard Worker          enumerate(zip(self._fieldnames, self._values))]
285*2f2c4c7aSAndroid Build Coastguard Worker
286*2f2c4c7aSAndroid Build Coastguard Worker      return "%s(%s)" % (self._name, ", ".join(descriptions))
287*2f2c4c7aSAndroid Build Coastguard Worker
288*2f2c4c7aSAndroid Build Coastguard Worker    def __repr__(self):
289*2f2c4c7aSAndroid Build Coastguard Worker      return str(self)
290*2f2c4c7aSAndroid Build Coastguard Worker
291*2f2c4c7aSAndroid Build Coastguard Worker    def CPointer(self):
292*2f2c4c7aSAndroid Build Coastguard Worker      """Returns a C pointer to the serialized structure."""
293*2f2c4c7aSAndroid Build Coastguard Worker      buf = ctypes.create_string_buffer(self.Pack())
294*2f2c4c7aSAndroid Build Coastguard Worker      # Store the C buffer in the object so it doesn't get garbage collected.
295*2f2c4c7aSAndroid Build Coastguard Worker      super(CStruct, self).__setattr__("_buffer", buf)
296*2f2c4c7aSAndroid Build Coastguard Worker      return ctypes.addressof(self._buffer)
297*2f2c4c7aSAndroid Build Coastguard Worker
298*2f2c4c7aSAndroid Build Coastguard Worker  return CStruct
299*2f2c4c7aSAndroid Build Coastguard Worker
300*2f2c4c7aSAndroid Build Coastguard Worker
301*2f2c4c7aSAndroid Build Coastguard Workerdef Read(data, struct_type):
302*2f2c4c7aSAndroid Build Coastguard Worker  length = len(struct_type)
303*2f2c4c7aSAndroid Build Coastguard Worker  return struct_type(data), data[length:]
304