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: