FAQ: how to vary the byte offset of a field of a ctypes.Structure
p.lavarre at ieee.org
p.lavarre at ieee.org
Thu May 31 17:36:19 EDT 2007
""" Thomas,
Ouch ouch I must have misunderstood what you meant by "use the dynamic
nature of Python, and (re-)define the data type after the required
size is already known, on a case by case basis".
Do you have an example of what you meant? I searched but did not find.
Are those your words?
Yes, to declare strings of strings in Python would be to express a
familiar idea that I don't know how to say well in C.
These are standard structs that I exchange with third parties, e.g.,
me editing Class files read later by a Java interpreter, so I can't
avoid having to deal with this complexity designed into them. For
discussion I've simplified the problem: back in real life I have a few
dozen variations of structs like this to deal with.
The following Python mostly works, but only at the cost of rewriting
the small part of CTypes that I need for this app.
"""
import binascii
import struct
class Struct:
def __init__(self, declarations = []):
"""Add initial values to self and list the names declared."""
names = []
for (initial, name) in declarations:
names += [name]
python = ' '.join(['self.' + name, '=',
'initial'])
exec python
self._names_ = names
def _fields_(self):
"""List the fields."""
fields = []
for name in self._names_:
python = 'self.' + name
fields += [eval(python)]
return fields
def _pack_(self):
"""Pack a copy of the fields."""
packs = ''
for field in self._fields_():
packs += field._pack_()
return packs
def _size_(self, bytes = None):
"""Count the bytes of the fields."""
return len(self._pack_())
def _unpack_(self, bytes):
"""Count the bytes of a copy of the fields."""
offset = 0
for field in self._fields_():
size = field._size_(bytes[offset:])
field._unpack_(bytes[offset:][:size])
offset += size
if offset != len(bytes):
why = ('_unpack_ requires a string argument'
'of length %d' % offset)
raise struct.error(why)
class Field(Struct):
"""Contain one value."""
def __init__(self, value = 0):
self._value_ = value
def _pack_(self):
raise AttributeError('abstract')
def _unpack_(self, bytes):
raise AttributeError('abstract')
class Byte(Field):
"""Contain one byte."""
def _pack_(self):
return struct.pack('B', self._value_)
def _unpack_(self, bytes):
self._value_ = struct.unpack('B', bytes)[0]
class ByteArray(Field):
"""Contain the same nonnegative number of bytes always."""
def _pack_(self):
return self._value_
def _unpack_(self, bytes):
if len(bytes) == len(self._value_):
self._value_ = bytes
else:
why = ('_unpack_ requires a string argument'
'of length %d' % len(self._value_))
raise struct.error(why)
class Symbol(Struct):
"""Contain a count of bytes."""
def __init__(self, value = ''):
Struct.__init__(self, [
(Byte(), 'length'),
(ByteArray(value), 'bytes')])
self._pack_()
def _size_(self, bytes = None):
return ord(bytes[0])
def _pack_(self):
self.length = Byte(self.length._size_() +
self.bytes._size_())
return Struct._pack_(self)
class TwoSymbols(Struct):
"""Contain two Symbols."""
def __init__(self, values = ['', '']):
Struct.__init__(self, [
(Symbol(values[0]), 'first'),
(Symbol(values[1]), 'second')])
zz = Symbol()
print binascii.hexlify(zz._pack_()).upper()
# 01
zz = Symbol()
zz.bytes = ByteArray('left')
zz._unpack_(zz._pack_()) ; print repr(zz._pack_())
# '\x05left'
zz = Symbol()
zz.bytes = ByteArray('right')
zz._unpack_(zz._pack_()) ; print repr(zz._pack_())
# '\x06right'
zz = TwoSymbols()
zz.first.bytes = ByteArray('hi')
zz.second.bytes = ByteArray('bye')
zz._unpack_(zz._pack_()) ; print repr(zz._pack_())
# '\x03hi\x04bye'
print zz._size_()
# 7
yy = '''
def yyFunc(self):
return [self.length, self.bytes]
'''
exec yy
Symbol._fields_ = yyFunc
zz = TwoSymbols(['alef', 'bet'])
zz._unpack_(zz._pack_()) ; print repr(zz._pack_())
More information about the Python-list
mailing list