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