Tuesday, August 10, 2010

RPython Struct

There is no drop in replacement for the struct module in RPython. In Rlib there is a "rstruct" folder, but only "unpack" is implemented with a warning in the header that it is incomplete; what it aparently lacks is proper endian support. There was never any pure RPython example made for how to implement "pack", there is however one example how to implement pack/unpack for the interpreter level. Looking at those examples it is clear we need to use the FormatIterator found in rlib/rstruct. The runpack example uses meta-programming to generate tuples for returning different types, but this seems like overkill, at least it is in the case of OSC where we know excatly what types we are dealing with. The two classes below (RStructUnpacker and RStructPacker) are simple and restricted RPython replacements for the struct module. They can not be used as drop in replacements; the Packer expects to be packing the same type, and the Unpacker does not return what it has unpacked and instead sorts it into lists by type.




class RStructUnpacker( pypy.rlib.rstruct.formatiterator.FormatIterator):
def __init__( self, fmt, data ):
self.strings = []
self.ints = []
self.floats = []
self.longs = []
self.input = data
self.inputpos = 0
self.interpret( fmt )

def operate(self, fmtdesc, repetitions):
if fmtdesc.needcount:
fmtdesc.unpack(self, repetitions)
else:
for i in range(repetitions):
fmtdesc.unpack(self)
operate._annspecialcase_ = 'specialize:arg(1)'
_operate_is_specialized_ = True

def align(self, mask):
self.inputpos = (self.inputpos + mask) & ~mask


def read(self, count):
end = self.inputpos + count
if end > len(self.input): raise SyntaxError
s = self.input[self.inputpos : end]
self.inputpos = end
return s


@specialize.argtype(1)
def appendobj( self, ob ):
if isinstance(ob, str): self.strings.append( ob )
elif isinstance(ob, int): self.ints.append( ob )
elif isinstance(ob, float): self.floats.append( ob )
#elif isinstance(ob, long): self.longs.append( ob ) isinstance(ob, long) not RPython, how do we check for long?


class RStructPacker(pypy.rlib.rstruct.formatiterator.FormatIterator):
# not a drop in replacement for struct.pack, but as close as RPython can get (easily).
def __init__(self, fmt, strings=[], ints=[], floats=[], longs=[], unicodes=[], uints=[] ):
self.strings = strings
self.ints = ints
self.floats = floats
self.longs = longs
self.unicodes = unicodes
self.uints = uints
self.args_index = 0
self.result = [] # list of characters
self.interpret( fmt )

def operate(self, fmtdesc, repetitions):
if fmtdesc.needcount:
fmtdesc.pack(self, repetitions)
else:
for i in range(repetitions):
fmtdesc.pack(self)
operate._annspecialcase_ = 'specialize:arg(1)'
_operate_is_specialized_ = True

def align(self, mask):
pad = (-len(self.result)) & mask
for i in range(pad):
self.result.append('\x00')

def finished(self): pass

def accept_str_arg(self):
assert self.strings
a = self.strings[ self.args_index ]
self.args_index += 1
return a
def accept_int_arg(self):
assert self.ints
a = self.ints[ self.args_index ]
self.args_index += 1
return a
def accept_float_arg(self):
assert self.floats
a = self.floats[ self.args_index ]
self.args_index += 1
return a
def accept_unicode_arg(self):
assert self.unicodes
a = self.unicodes[ self.args_index ]
self.args_index += 1
return a
def accept_uint_arg(self):
assert self.uints
a = self.uints[ self.args_index ]
self.args_index += 1
return a

No comments:

Post a Comment