import struct, copy, cStringIO class DataItem(object): default = None fixedwidth = False def set_name(self, name): self.name = name def read(self, ob, data, i): raise NotImplementedError def write(self, ob): raise NotImplementedError class SimpleDataItem(DataItem): def __mul__(self, count): return RepeatedDataItem(self, count) def read(self, ob, data, i): v, i = self.extract(ob, data, i) ob.__dict__[self.name + '_data'] = v return i def extract(self, ob, data, i): raise NotImplementedError def write(self, ob): return self.serialize(ob.__dict__[self.name + '_data']) def serialize(self, v): raise NotImplementedError class StructDataItem(SimpleDataItem): def extract(self, ob, data, i): s = self.width return struct.unpack(self.code, data[i:i+s])[0], i+s def serialize(self, v): return struct.pack(self.code, v)[0] class BYTE(StructDataItem): fixedwidth = True width = 1 code = ' 255: raise Exception, "erk" class Extend(DataItem): def __init__(self, sub): self.sub = sub self.fixedwidth = sub.fixedwidth if self.fixedwidth: self.width = sub.width def read(self, ob, data, i): v, i = self.sub.extract(ob, data, i) getattr(ob, self.name).extend(v) return i class DataItemDescr(object): def __init__(self, name, dataitem): self.name = name self.dataitem = dataitem def __get__(self, ob, cls=None): try: return ob.__dict__[self.name + '_data'] except KeyError: ob._load_from_data() return ob.__dict__[self.name + '_data'] class MetaBinary(type): def __new__(cls, name, bases, ns): layout = ns.pop('layout', []) fw = True w = 0 dataitems = [] for k, v in layout: v.set_name(k) if fw: v.fixedoffset = w if not v.fixedwidth: fw = False else: w += v.width if k not in ns: ns[k] = DataItemDescr(k, v) dataitems.append(v) else: assert isinstance(ns[k].dataitem, RepeatedDataItem) assert isinstance(v, Extend) assert isinstance(v.sub, RepeatedDataItem) v.sub.start = ns[k].dataitem.mcount ns[k].dataitem.mcount += v.sub.count ns[k].dataitem.extensions.append(v.sub) r = super(MetaBinary, cls).__new__(cls, name, bases, ns) r.layout = getattr(r, 'layout', []) + layout r.dataitems = getattr(r, 'dataitems', []) + dataitems r.fixedwidth = getattr(r, 'fixedwidth', True) and fw if r.fixedwidth: r.width = getattr(r, 'width', 0) + w r.name = name return r class Binary(object): __metaclass__ = MetaBinary def __init__(self, **kw): return for n, v in self.layout: setattr(self, n + '_data', kw.pop(n, copy.copy(v.default))) if kw: raise TypeError, "extra kw args %r"%kw def load_from_data(self, data, i): if self.fixedwidth: self._data_ = data[i:i+self.width] return i+self.width else: i0 = i for n, l in self.layout: i = l.read(self, data, i) self._data_ = data[i0:i] return i def _load_from_data(self): i = 0 for n, l in self.layout: i = l.read(self, self._data_, i) def __repr__(self): kws = [] for k in self.dataitems[1:]: v = getattr(self, k.name) if isinstance(v, list): v = tuple(v) kws.append('%s=%r'%(k.name, v)) return "%s(%s)"%(self.__class__.__name__, ', '.join(kws))