3 from logging
import debug
8 class RecordsException(Exception):
11 class FormatError(RecordsException
):
14 class UnknownSizeException(RecordsException
):
19 def set_bigendian(self
, ignore
): pass
20 def set_reclen(self
, ignore
): pass
21 def put_default(self
, dict): pass
23 ###### Skip is a dummy field that isn't read
24 class Skip(BaseField
):
25 def __init__(self
, n
):
26 self
.get_size
= (lambda:n
)
27 self
.write
= self
.read
28 def read(self
, file, dict):
29 file.seek(self
.get_size(), os
.SEEK_CUR
)
31 ###### Field is a composite field object factory
32 class Field(BaseField
):
33 # Value handlers - know what to do with read values and
34 # where to get values to be written
36 def __init__(self
, name
, default
):
38 self
.default
= default
40 return dict[self
.name
] if dict.has_key(self
.name
) else self
.default
41 def put(self
, dict, value
):
42 dict[self
.name
] = value
43 def put_default(self
, dict):
44 dict[self
.name
] = self
.default
45 def __str__(self
): return self
.name
48 re
= re
.compile("len\(([^)]+)\)")
49 def __init__(self
, expr
):
51 self
.array_name
= self
.re
.match(expr
).group(1)
53 return len( dict[self
.array_name
] )
54 def put(self
, dict, value
):
55 dict[self
.expr
] = value
56 def put_default(self
, dict): pass
57 def __str__(self
): return self
.expr
60 def __init__(self
, const
, check
):
66 def check(self
, dict, value
):
67 if value
!= self
.const
:
68 raise FormatError("Expected %s, got %s" % (self
.const
, value
))
69 def put(self
, dict, value
): pass
70 def put_default(self
, dict): pass
71 def __str__(self
): return "Constant(%s)" % self
.const
73 # Field factory - composes field from packer and value handler
74 def __init__(self
, packer
, name
=None, default
=None, const
=None, check
= False):
76 def set_value_handler(vh
):
79 self
.put_default
= vh
.put_default
80 self
.__str
__ = vh
.__str
__
81 def const_later(const
):
82 set_value_handler(Field
.Const(const
, check
))
84 if name
== '%reclen%':
85 self
.set_reclen
= const_later
86 elif name
is not None:
87 if Field
.LenLookup
.re
.match(name
) is not None:
88 set_value_handler(Field
.LenLookup(name
))
90 set_value_handler(Field
.Named(name
,default
))
91 elif const
is not None:
92 set_value_handler(Field
.Const(const
, check
))
94 raise RecordsException("Bad field parameters")
96 self
.get_size
= packer
.get_size
97 self
.pack
= packer
.pack
98 self
.unpack
= packer
.unpack
99 if packer
.__class
__.__dict
__.has_key('set_bigendian'):
100 self
.set_bigendian
= packer
.set_bigendian
102 def read(self
, file, dict):
103 value
= self
.unpack( file.read( self
.get_size() ))
104 debug("read %s: %s", self
, value
)
105 self
.put(dict, value
)
106 def write(self
, file, dict):
107 value
= self
.get(dict)
108 debug("write %s: %s", self
, value
)
109 file.write( self
.pack( value
))
111 class ListField(BaseField
):
112 def __init__(self
, name
, cell
):
116 def read(self
, file, dict):
117 lst
= dict[self
.name
] if dict.has_key(self
.name
) else []
118 n
= dict["len(%s)" % self
.name
]
119 for i
in xrange(0, n
):
121 lst
[i
] = self
.cell
.read(file, lst
[i
])
123 lst
.append(self
.cell
.read(file, {}))
124 dict[self
.name
] = lst
126 def write(self
, file, dict):
127 lst
= dict[ self
.name
]
129 self
.cell
.write( file, c
)
132 raise UnknownSizeException()
134 def set_bigendian(self
, bigendian
):
135 self
.cell
.set_bigendian(bigendian
)
139 def __init__(self
, fmt
):
141 self
.size
= struct
.calcsize(fmt
)
142 def pack(self
,val
): return struct
.pack(self
.fmt
,val
)
143 def unpack(self
,str): return struct
.unpack(self
.fmt
,str)[0]
144 def get_size(self
): return self
.size
146 class Uint8(SimplePacker
):
147 def __init__(self
): SimplePacker
.__init
__(self
,"B")
150 def pack(self
, val
): return Uint8
.pack(self
, (1 if val
else 0))
151 def unpack(self
, str): return Uint8
.unpack(self
, str) != 0
154 def __init__(self
, bigendian
= LITTLE_ENDIAN
):
155 self
.bigendian
= bigendian
156 def get_size(self
): return 3
159 return struct
.pack(">I",i
)[1:4]
161 return struct
.pack("<I",i
)[0:3]
164 return struct
.unpack('>I','\x00' + s
[0:3])[0]
166 return struct
.unpack('<I',s
[0:3] + '\x00')[0]
167 def set_bigendian(self
, bigendian
):
168 self
.bigendian
= bigendian
171 def __init__(self
, bigendian
= LITTLE_ENDIAN
):
172 self
.bigendian
= bigendian
175 return struct
.pack(">i",i
)[1:4]
177 return struct
.pack("<i",i
)[0:3]
179 u
= Uint24
.unpack(self
,s
)
181 return - ((~u
+ 1) & 0xfff)
186 def __init__(self
): Int24
.__init__(self
)
187 def pack(self
, val
): return Int24
.pack(self
, (-1 if val
else 0))
188 def unpack(self
, str): return Int24
.unpack(self
, str) != 0
190 class ZeroPaddedString
:
191 def __init__(self
, len, enc
):
195 return val
.encode(self
.enc
).ljust(self
.size
,'\x00')
196 def unpack(self
, str):
197 return str.decode(self
.enc
).rstrip('\x00')
198 def get_size(self
): return self
.size
200 ### Record - an ordered list of fields
201 class Record(BaseField
):
202 def __init__(self
, fields
, bigendian
=None):
205 reclen
= self
.get_size()
208 except UnknownSizeException
:
210 if bigendian
is not None:
211 self
.set_bigendian(bigendian
)
213 def read(self
, file, dict=None):
214 if dict is None: dict = {}
215 for f
in self
.fields
:
219 def write(self
, file, dict):
220 for f
in self
.fields
:
223 def make_default(self
, dict = None):
226 for f
in self
.fields
:
232 for f
in self
.fields
:
236 def set_bigendian(self
, bigendian
):
237 for f
in self
.fields
:
238 f
.set_bigendian(bigendian
)