您的位置:首页 > 其它

一个更高级的struct模块

2012-09-06 11:39 141 查看
import struct

class Format(object):
"""Endianness and size format for structures."""
Native          = "@"       # Native format, native size
StandardNative  = "="       # Native format, standard size
LittleEndian    = "<"       # Standard size
BigEndian       = ">"       # Standard size

class Element(object):
"""A single element in a struct."""
id=0
def __init__(self, typecode):
Element.id+=1           # Note: not thread safe
self.id = Element.id
self.typecode = typecode
self.size = struct.calcsize(typecode)

def __len__(self):
return self.size

def decode(self, format, s):
"""Additional decode steps once converted via struct.unpack"""
return s

def encode(self, format, val):
"""Additional encode steps to allow packing with struct.pack"""
return val

def __str__(self):
return self.typecode

def __call__(self, num):
"""Define this as an array of elements."""
# Special case - strings already handled as one blob.
if self.typecode in 'sp':
# Strings handled specially - only one item
return Element('%ds' % num)
else:
return ArrayElement(self, num)

def __getitem__(self, num): return self(num)

class ArrayElement(Element):
def __init__(self, basic_element, num):
Element.__init__(self, '%ds' % (len(basic_element) * num))
self.num = num
self.basic_element = basic_element

def decode(self, format, s):
# NB. We use typecode * size, not %s%s' % (size, typecode),
# so we deal with typecodes that already have numbers,
# ie 2*'4s' != '24s'
return [self.basic_element.decode(format, x) for x in
struct.unpack('%s%s' % (format,
self.num * self.basic_element.typecode),s)]

def encode(self, format, vals):
fmt = format + (self.basic_element.typecode * self.num)
return struct.pack(fmt, *[self.basic_element.encode(format,v)
for v in vals])

class EmbeddedStructElement(Element):
def __init__(self, structure):
Element.__init__(self, '%ds' % structure._struct_size)
self.struct = structure

# Note: Structs use their own endianness format, not their parent's
def decode(self, format, s):
return self.struct(s)

def encode(self, format, s):
return self.struct._pack(s)

name_to_code = {
'Char'             : 'c',
'Byte'             : 'b',
'UnsignedByte'     : 'B',
'Int'              : 'i',
'UnsignedInt'      : 'I',
'Short'            : 'h',
'UnsignedShort'    : 'H',
'Long'             : 'l',
'UnsignedLong'     : 'L',
'String'           : 's',
'PascalString'     : 'p',
'Pointer'          : 'P',
'Float'            : 'f',
'Double'           : 'd',
'LongLong'         : 'q',
'UnsignedLongLong' : 'Q',
}

class Type(object):
def __getattr__(self, name):
return Element(name_to_code[name])

def Struct(self, struct):
return EmbeddedStructElement(struct)

Type=Type()

class MetaStruct(type):
def __init__(cls, name, bases, d):
type.__init__(cls, name, bases, d)
if hasattr(cls, '_struct_data'):  # Allow extending by inheritance
cls._struct_info = list(cls._struct_info) # use copy.
else:
cls._struct_data=''
cls._struct_info=[]     # name / element pairs

# Get each Element field, sorted by id.
elems = sorted(((k,v) for (k,v) in d.iteritems()
if isinstance(v, Element)),
key=lambda x:x[1].id)

cls._struct_data += ''.join(str(v) for (k,v) in elems)
cls._struct_info += elems
cls._struct_size = struct.calcsize(cls._format + cls._struct_data)

class Struct(object):
"""Represent a binary structure."""
__metaclass__=MetaStruct
_format = Format.Native  # Default to native format, native size

def __init__(self, _data=None, **kwargs):
if _data is None:
_data ='\0' * self._struct_size

fieldvals = zip(self._struct_info, struct.unpack(self._format +
self._struct_data, _data))
for (name, elem), val in fieldvals:
setattr(self, name, elem.decode(self._format, val))

for k,v in kwargs.iteritems():
setattr(self, k, v)

def _pack(self):
return struct.pack(self._format + self._struct_data,
*[elem.encode(self._format, getattr(self, name))
for (name,elem) in self._struct_info])

def __str__(self):
return self._pack()

def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self._pack())

###################################################################
#  End of implementation - usage examples follow:
###################################################################

###################################################################
#
# Usage
#
# Using the above code, we can now define structures in a
# more readable class based syntax.  For example:
###################################################################

class Point(Struct):
_format = Format.LittleEndian
x = Type.Short
y = Type.Short

p = Point('\x01\x00\x02\x00')

print p.x, p.y   # Prints 1,2
p.x, p.y = 100,200
print repr(p)     # Prints "Point('d\x00\xc8\x00')

assert(struct.pack('<hh',100,200) == str(p))

###################################################################
#
# Arrays and Embedded structures
#
# You can also embed arrays, (and arrays of arrays), as well
# as other structures within your struct definition.
###################################################################

class Shape(Struct):
_format = Format.BigEndian
name      = Type.String[8]
numpoints = Type.Int
points    = Type.Struct(Point)[4] # Array of 4 points.

s=Shape('Triangle\x00\x00\x00\x03\x00\x00\x00\x00\x05\x00\x05\x00\n\x00'
'\x00\x00\x00\x00\x00\x00')

# This will print "Triangle [(0,0), (5,5), (10,0)]"
print s.name, [(p.x, p.y) for p in s.points[:s.numpoints]]

# The same structure could be created as:
s2=Shape(name='Triangle', numpoints=3, points=[
Point(x=0,y=0),
Point(x=5,y=5),
Point(x=10,y=0),
Point(x=0,y=0)])

assert str(s2) == str(s)

# Note that even though Shape is in BigEndian format, the Points
# keep their LittleEndian setting, so mixing formats is possible,
# and the same struct will always have the same representation
# regardless of its context.  Hence the following is true:

assert str(s.points[1]) == str( Point(x=5, y=5))

# It is also possible to define multi-dimensional arrays,
# which will be unpacked as lists of lists.
# In addition, it is possible to add methods and non-struct
# instance variables without interfering with the structure
# (Unless you overwrite structure field names of course)

class TicTacToe(Struct):
board = Type.Char[3][3] # 3x3 array of chars

ignored = 'This is not packed / unpacked by the structure'

def display(self):
print '\n'.join(''.join(row) for row in self.board)

game = TicTacToe('X.O.X...O')
print game.board  # [['X', '.', 'O'], ['.', 'X', '.'], ['.', '.', 'O']]

game.display()
# Prints: X.O
#         .X.
#         ..O

game.board[0][1] = 'X'
game.display()
# Prints: XXO
#         .X.
#         ..O
print str(game) # prints 'XXO.X...O'

###################################################################
#
# Inheritance
#
# Structures may also be inherited from, in which case, additional
# fields will occur after the existing ones.
#
###################################################################

class Point3D(Point):
z = Type.Short

p = Point3D(x=1, y=2, z=3)

print repr(p)   # prints Point3D('\x01\x00\x02\x00\x03\x00')
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: