view inbus.py @ 6:59a9ca55098c
Changed name of struct_unpacker to pystruct_unpacker to be slightly less confusing (struct and tuple are easily confused).
| author |
Eric Hopper <hopper@omnifarious.org> |
| date |
Tue, 26 Feb 2008 08:29:13 -0800 |
| parents |
8ecb08252c8a |
| children |
7bf7acf4225e |
line source
1 from struct import pack as _pack
2 from struct import unpack as _unpack
3 from functools import partial as _partial
5 def unpack(s, numtypes=1):
6 up, data_offset = create_unpacker(s, numtypes)
7 result = up(([], s, data_offset))
8 return tuple(result[0])
10 def unpack_off(s, numtypes=1):
11 up, data_offset = create_unpacker(s, numtypes)
12 result = up(([], s, data_offset))
13 return tuple(result[0]), result[2]
15 def create_unpacker(s, numtypes=1):
17 numtypes = int(numtypes)
18 except (ValueError, TypeError):
19 raise TypeError("numtypes must be an integer")
21 raise TypeError("numtypes must be > 0")
22 if not isinstance(s, (str, buffer)):
23 raise TypeError("s must be a string or buffer")
24 def nullunpacker(dectup):
26 def composeunpacker(f1, f2):
30 unpacker = nullunpacker
32 for i in xrange(0, numtypes):
36 unpacker = composeunpacker(count_unpacker, unpacker)
38 if (len(s) > offset) and (s[offset] == '<'):
39 raise NotImplementedError("Length modifiers not yet supported for multi-precision integer ('m') type.")
40 unpacker = composeunpacker(multi_int_unpacker, unpacker)
42 if (len(s) > offset) and (s[offset] == '<'):
43 raise NotImplementedError("Length modifiers not yet supported for unsigned multi-precision integer ('w') type.")
44 unpacker = composeunpacker(multi_uint_unpacker, unpacker)
46 unpacker = composeunpacker(bool_unpacker, unpacker)
48 if (len(s) > offset) and (s[offset] == '<'):
49 raise NotImplementedError("Length modifiers not yet supported for binary blob ('b') type.")
50 unpacker = composeunpacker(blob_unpacker, unpacker)
52 if (len(s) > offset) and (s[offset] == '<'):
53 raise NotImplementedError("Length modifiers not yet supported for string ('s') type.")
54 unpacker = composeunpacker(string_unpacker, unpacker)
56 unpacker = composeunpacker(byte_unpacker, unpacker)
58 unpacker = composeunpacker(ubyte_unpacker, unpacker)
60 unpacker = composeunpacker(short_unpacker, unpacker)
62 unpacker = composeunpacker(ushort_unpacker, unpacker)
64 unpacker = composeunpacker(int_unpacker, unpacker)
66 unpacker = composeunpacker(uint_unpacker, unpacker)
68 unpacker = composeunpacker(long_unpacker, unpacker)
70 unpacker = composeunpacker(ulong_unpacker, unpacker)
72 unpacker = composeunpacker(double_unpacker, unpacker)
74 unpacker = composeunpacker(float_unpacker, unpacker)
76 raise NotImplementedError("The arbitrary size floating point ('g') type is not yet supported.")
78 unpacker = composeunpacker(variant_unpacker, unpacker)
79 elif tag in ['t', 'a', 'd']:
81 subunpacker, suboffset = make_tuple_unpacker(buffer(s, offset))
83 subunpacker, suboffset = make_array_unpacker(buffer(s, offset))
85 subunpacker, suboffset = make_dict_unpacker(buffer(s, offset))
87 assert False, "Getting here should be impossible."
89 unpacker = composeunpacker(subunpacker, unpacker)
91 raise ValueError("Unknown type tag '%s' encountered." % (tag,))
92 return unpacker, offset
94 def count_unpacker(dectup):
95 val, newoffset = unpackcount_off(dectup[1], dectup[2])
97 return dectup[0], dectup[1], newoffset
99 def multi_int_unpacker(dectup):
100 result, s, offset = dectup
101 mlen, newoffset = unpackcount_off(dectup[1], dectup[2])
102 if (newoffset + mlen) > len(s):
103 raise ValueError("The string passed in is too short to unpack.")
106 return (result, s, newoffset)
108 mint = unpackl(buffer(s, newoffset, mlen))
109 if ord(s[newoffset]) & 0x80:
110 mint -= 2**(mlen * 8)
112 return (result, s, newoffset + mlen)
114 def multi_uint_unpacker(dectup):
115 result, s, offset = dectup
116 mlen, newoffset = unpackcount_off(dectup[1], dectup[2])
117 if (newoffset + mlen) > len(s):
118 raise ValueError("The string passed in is too short to unpack.")
121 return (result, s, newoffset)
122 mint = unpackl(buffer(s, newoffset, mlen))
124 return (result, s, newoffset + mlen)
126 def blob_unpacker(dectup):
127 result, s, offset = dectup
128 blen, newoffset = unpackcount_off(dectup[1], dectup[2])
129 if (newoffset + blen) > len(s):
130 raise ValueError("The string passed in is too short to unpack.")
133 return result, s, newoffset
134 result.append(buffer(s, newoffset, blen))
135 return result, s, newoffset + blen
137 def string_unpacker(dectup):
138 result, s, offset = dectup
139 slen, newoffset = unpackcount_off(dectup[1], dectup[2])
140 if (newoffset + slen) > len(s):
141 raise ValueError("The string passed in is too short to unpack.")
144 return result, s, newoffset
145 result.append(unicode(buffer(s, newoffset, slen), 'utf-8'))
146 return result, s, newoffset + slen
148 def pystruct_unpacker(spec, len, dectup):
149 result, s, offset = dectup
150 (upresult,) = _unpack(spec, buffer(s, offset, len))
151 result.append(upresult)
152 return result, s, offset + len
154 byte_unpacker = _partial(pystruct_unpacker, '>b', 1)
155 ubyte_unpacker = _partial(pystruct_unpacker, '>b', 1)
156 short_unpacker = _partial(pystruct_unpacker, '>h', 2)
157 ushort_unpacker = _partial(pystruct_unpacker, '>H', 2)
158 int_unpacker = _partial(pystruct_unpacker, '>i', 4)
159 uint_unpacker = _partial(pystruct_unpacker, '>I', 4)
160 long_unpacker = _partial(pystruct_unpacker, '>q', 8)
161 ulong_unpacker = _partial(pystruct_unpacker, '>Q', 8)
162 float_unpacker = _partial(pystruct_unpacker, '>f', 4)
163 double_unpacker = _partial(pystruct_unpacker, '>d', 8)
165 def variant_unpacker(dectup):
166 result, s, offset = dectup
167 val, uplen = unpack_off(buffer(s, offset), 1)
170 return result, s, offset + uplen
172 def make_tuple_unpacker(s):
174 raise ValueError("Tuple type tag ('t') must be followed by '('")
176 def nullunpacker(dectup):
178 def composeunpacker(f1, f2):
180 return f1(f2(dectup))
182 def tuplefinishunpacker(dectup):
183 return [tuple(dectup[0])], dectup[1], dectup[2]
184 unpacker = nullunpacker
185 while (offset < len(s)) and (s[offset] != ')'):
186 elunpacker, consumed = create_unpacker(buffer(s, offset))
188 unpacker = composeunpacker(elunpacker, unpacker)
190 raise ValueError("Ran off the end of the string to unpack while parsing tuple")
191 return composeunpacker(tuplefinishunpacker, unpacker), offset + 1
193 def make_array_unpacker(s):
195 raise ValueError("Array type tag ('a') must be followed by '['")
197 elunpacker, consumed = create_unpacker(buffer(s, offset))
199 if (offset >= len(s)) or (s[offset] != ']'):
200 raise ValueError("Array type tag must end with ']' after one type tag.")
201 def unpack_array(dectup):
202 result, s, offset = dectup
205 raise ValueError("End of string while parsing array.")
206 while s[offset] != '\0':
207 out, s, offset = elunpacker(([], s, offset + 1))
209 raise ValueError("End of string while parsing array.")
212 return result, s, offset + 1
213 return unpack_array, offset + 1
215 def make_dict_unpacker(s):
217 raise ValueError("Dictionary type tag ('d') must be followed by '{'")
219 keyunpacker, consumed = create_unpacker(buffer(s, offset))
222 raise ValueError("Dictionary type tags must contain two subtypes.")
223 valunpacker, consumed = create_unpacker(buffer(s, offset))
225 if (offset >= len(s)) or (s[offset] != '}'):
226 print "offset = %r / consumed = %r / s = %r / len(s) = %r" % \
227 (offset, consumed, s, len(s))
228 raise ValueError("Dictionary type tag must end with '}' after two type tag.")
229 def unpack_dict(dectup):
230 result, s, offset = dectup
233 raise ValueError("End of string while parsing dictionary.")
234 while s[offset] != '\0':
235 out, s, offset = valunpacker(keyunpacker(([], s, offset + 1)))
237 raise ValueError("End of string while parsing dictionary.")
238 dictionary[out[0]] = out[1]
239 result.append(dictionary)
240 return result, s, offset + 1
241 return unpack_dict, offset + 1
243 def packl(lnum, pad = 1):
245 raise RangeError("Cannot use packl to convert a negative integer "
250 l.append(lnum & 0xffffffffffffffffL)
256 lens = 8 * count % pad
257 pad = ((lens != 0) and (pad - lens)) or 0
258 l.append('>' + 'x' * pad + 'Q' * count)
262 l.append('>' + 'Q' * count)
264 s = _pack(*l).lstrip('\0')
266 if (lens % pad) != 0:
267 return '\0' * (pad - lens % pad) + s
277 upfmt = '>' + 'Q' * count8 + 'B' * count1
278 l = _unpack(upfmt, s)
279 for val in l[0:count8]:
282 for val in l[count8:]:
287 def packcount(count):
288 """packcount(non-negative integer) -> string representing the count
290 The count cannot be less than 0. The encoding is designed so that if
291 the count is a message length, a very small percentage of the total
292 message will be consumed by the count.
294 It is suggested that the message length itself not be counted as part
295 of the message length. This means that counts of 0 should be
296 sensible, and indicate a message with no content.
298 This function should NOT be used for values that are really arbitrary
299 sized integers. The maximum sized integer that this function can
300 represent is 4080 bits. Public key values will most likely exceed
301 this. To store a public key (or other arbitrary sized integer) value,
302 encode that value in some other way (perhaps using packl), then use
303 this function to encode the length of that encoding."""
305 raise RangeError("A count cannot be negative!")
308 elif count < (223 + 8192):
313 return chr(upper + 223) + chr(lower)
316 assert len(s) % 2 == 0
317 countlen = len(s) // 2
319 raise RangeError("A count must take fewer than 4080 bits to "
321 return chr(255) + chr(countlen) + s
323 def unpackcount_off(s, off = 0):
324 """unpackcount(a string, offset in string of something created by packcount)
326 the tuple is (count, offset of byte after count).
328 See the documentation for packcount for a little more of an
329 explanation. A ValueError exception will be thrown for strings that
330 don't start with a valid count value."""
335 elif fc < (223 + 32):
337 if 2 > (len(s) - off):
338 raise ValueError("The passed in string is too short, and isn't "
340 upper_5bits = fc - 223
341 lower_8bits = ord(s[off + 1])
342 count = ((upper_5bits << 8) | lower_8bits) + 223
343 return (count, off + 2)
345 # Variable length count, length in second octet
346 if 2 > (len(s) - off):
347 raise ValueError("The passed in string is too short, and isn't "
349 length = ord(s[off + 1]) * 2
351 raise ValueError("The passed in string isn't a valid count!")
352 # Skip past length of count itself
354 # Enough octets for rest of count?
355 if length > (len(s) - off):
356 raise ValueError("The passed in string is too short, and isn't "
358 # Use the handy unpackl function to turn the octet string into a number
359 count = unpackl(s[off:(off + length)])
360 # If count fits into an int, turn it into an int
363 return (count, off + length)
366 """unpackcount(a string beginning with something created by packcount) -> a tuple
367 the tuple is (count, remainder of s).
369 See the documentation for packcount for a little more of an
370 explanation. A ValueError exception will be thrown for strings that
371 don't start with a valid count value."""
372 (count, newoff) = unpackcount_off(s, 0)
373 return (count, s[newoff:])