wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

fields.py (4084B)


      1 from datetime import datetime
      2 import operator
      3 
      4 class Field(object):
      5     idx = 0
      6 
      7     def __init__(self):
      8         Field.idx += 1
      9         self.id = Field.idx
     10 
     11     def repr(self):
     12         return self.__class__.__name__
     13 
     14     def __repr__(self):
     15         if hasattr(self, 'name'):
     16             return '<Field %s "%s">' % (self.repr(), self.name)
     17         return super(Field, self).__repr__()
     18 
     19 class Int(Field):
     20     encoders = {}
     21     decoders = {}
     22     @classmethod
     23     def encoder(cls, n):
     24         if n not in cls.encoders:
     25             exec ('def enc(n):\n' +
     26                   '    assert n == n & 0x%s, "Arithmetic overflow"\n' +
     27                   '    return "".join((%s,))'
     28                  ) % ('ff' * n,
     29                       ','.join('chr((n >> %d) & 0xff)' % (i * 8)
     30                                for i in range(0, n)))
     31 
     32             cls.encoders[n] = enc
     33         return cls.encoders[n]
     34     @classmethod
     35     def decoder(cls, n):
     36         if n not in cls.decoders:
     37             cls.decoders[n] = eval('lambda data, offset: ' +
     38                                    '|'.join('ord(data[offset + %d]) << %d' % (i, i * 8)
     39                                             for i in range(0, n)))
     40         return cls.decoders[n]
     41 
     42     def __init__(self, size):
     43         super(Int, self).__init__()
     44         self.size = size
     45         self.encode = self.encoder(size)
     46         self.decode = self.decoder(size)
     47         if self.__class__ == Int:
     48             self.marshall = self.encode
     49 
     50     def unmarshall(self, data, offset):
     51         return self.size, self.decode(data, offset)
     52     def marshall(self, val):
     53         return self.encode(val)
     54 
     55     def repr(self):
     56         return '%s(%d)' % (self.__class__.__name__, self.size)
     57 
     58 class Size(Int):
     59     def __init__(self, size, extra=0):
     60         super(Size, self).__init__(size)
     61         self.extra = extra
     62 
     63     def marshall(self, val):
     64         return lambda vals, i: self.encode(
     65             reduce(lambda n, i: n + len(vals[i]),
     66                    range(i + 1, len(vals)),
     67                    self.extra))
     68 
     69 class Date(Int):
     70     def __init__(self):
     71         super(Date, self).__init__(4)
     72 
     73     def unmarshall(self, data, offset):
     74         val = self.decode(data, offset)
     75         return 4, datetime.fromtimestamp(val)
     76     def marshall(self, val):
     77         return self.encode(int(val.strftime('%s')))
     78 
     79 class Data(Int):
     80     def __init__(self, size=2):
     81         super(Data, self).__init__(size)
     82     def unmarshall(self, data, offset):
     83         n = self.decode(data, offset)
     84         offset += self.size
     85         assert offset + n <= len(data), "String too long to unpack"
     86         return self.size + n, data[offset:offset + n]
     87     def marshall(self, val):
     88         if isinstance(val, unicode):
     89             val = val.encode('UTF-8')
     90         return [self.encode(len(val)), val]
     91 
     92 # Note: Py3K strings are Unicode by default. They can't store binary
     93 #       data.
     94 class String(Data):
     95     def unmarshall(self, data, offset):
     96         off, val = super(String, self).unmarshall(data, offset)
     97         return off, val.decode('UTF-8')
     98     def marshall(self, val):
     99         if isinstance(val, str):
    100             # Check for valid UTF-8
    101             str.decode('UTF-8')
    102         else:
    103             val = val.encode('UTF-8')
    104         return super(String, self).marshall(val)
    105 
    106 class Array(Int):
    107     def __init__(self, size, spec):
    108         super(Array, self).__init__(size)
    109         self.spec = spec
    110 
    111     def unmarshall(self, data, offset):
    112         start = offset
    113         n = self.decode(data, offset)
    114         offset += self.size
    115         res = []
    116         for i in range(0, n):
    117             size, val = self.spec.unmarshall(data, offset)
    118             if isinstance(val, list):
    119                 res += val
    120             else:
    121                 res.append(val)
    122             offset += size
    123         return offset - start, res
    124     def marshall(self, vals):
    125         res = [self.encode(len(vals))]
    126         for val in vals:
    127             val = self.spec.marshall(val)
    128             if isinstance(val, list):
    129                 res += val
    130             else:
    131                 res.append(val)
    132         return res
    133 
    134 # vim:se sts=4 sw=4 et: