2 """Functions for bencoding and bdecoding data.
4 @type decode_func: C{dictionary} of C{function}
5 @var decode_func: a dictionary of function calls to be made, based on data,
6 the keys are the first character of the data and the value is the
7 function to use to decode that data
8 @type bencached_marker: C{list}
9 @var bencached_marker: mutable type to ensure class origination
10 @type encode_func: C{dictionary} of C{function}
11 @var encode_func: a dictionary of function calls to be made, based on data,
12 the keys are the type of the data and the value is the
13 function to use to encode that data
14 @type BencachedType: C{type}
15 @var BencachedType: the L{Bencached} type
18 from types import IntType, LongType, StringType, ListType, TupleType, DictType, BooleanType
20 from types import UnicodeType
23 from cStringIO import StringIO
25 from twisted.python import log
28 """Bdecode an integer.
31 @param x: the data to decode
33 @param f: the offset in the data to start at
34 @rtype: C{int}, C{int}
35 @return: the bdecoded integer, and the offset to read next
36 @raise ValueError: if the data is improperly encoded
41 newf = x.index('e', f)
49 elif x[f] == '0' and newf != f+1:
53 def decode_string(x, f):
57 @param x: the data to decode
59 @param f: the offset in the data to start at
60 @rtype: C{string}, C{int}
61 @return: the bdecoded string, and the offset to read next
62 @raise ValueError: if the data is improperly encoded
66 colon = x.index(':', f)
69 except (OverflowError, ValueError):
71 if x[f] == '0' and colon != f+1:
74 return (x[colon:colon+n], colon+n)
76 def decode_unicode(x, f):
77 """Bdecode a unicode string.
80 @param x: the data to decode
82 @param f: the offset in the data to start at
83 @rtype: C{int}, C{int}
84 @return: the bdecoded unicode string, and the offset to read next
88 s, f = decode_string(x, f+1)
89 return (s.decode('UTF-8'),f)
91 def decode_list(x, f):
95 @param x: the data to decode
97 @param f: the offset in the data to start at
98 @rtype: C{list}, C{int}
99 @return: the bdecoded list, and the offset to read next
105 v, f = decode_func[x[f]](x, f)
109 def decode_dict(x, f):
110 """Bdecode a dictionary.
113 @param x: the data to decode
115 @param f: the offset in the data to start at
116 @rtype: C{dictionary}, C{int}
117 @return: the bdecoded dictionary, and the offset to read next
118 @raise ValueError: if the data is improperly encoded
125 k, f = decode_string(x, f)
129 r[k], f = decode_func[x[f]](x, f)
133 decode_func['l'] = decode_list
134 decode_func['d'] = decode_dict
135 decode_func['i'] = decode_int
136 decode_func['0'] = decode_string
137 decode_func['1'] = decode_string
138 decode_func['2'] = decode_string
139 decode_func['3'] = decode_string
140 decode_func['4'] = decode_string
141 decode_func['5'] = decode_string
142 decode_func['6'] = decode_string
143 decode_func['7'] = decode_string
144 decode_func['8'] = decode_string
145 decode_func['9'] = decode_string
146 decode_func['u'] = decode_unicode
148 def bdecode(x, sloppy = 0):
149 """Bdecode a string of data.
152 @param x: the data to decode
153 @type sloppy: C{boolean}
154 @param sloppy: whether to allow errors in the decoding
156 @return: the bdecoded data
157 @raise ValueError: if the data is improperly encoded
162 r, l = decode_func[x[0]](x, 0)
163 # except (IndexError, KeyError):
164 except (IndexError, KeyError, ValueError):
166 raise ValueError, "bad bencoded data"
167 if not sloppy and l != len(x):
168 raise ValueError, "bad bencoded data"
172 """A test routine for the bdecoding functions."""
184 bdecode('i341foo382e')
188 assert bdecode('i4e') == 4L
189 assert bdecode('i0e') == 0L
190 assert bdecode('i123456789e') == 123456789L
191 assert bdecode('i-10e') == -10L
213 bdecode('35208734823ljdahflajhdf')
218 bdecode('2:abfdjslhfld')
222 assert bdecode('0:') == ''
223 assert bdecode('3:abc') == 'abc'
224 assert bdecode('10:1234567890') == '1234567890'
235 assert bdecode('le') == []
237 bdecode('leanfdldjfh')
241 assert bdecode('l0:0:0:e') == ['', '', '']
243 bdecode('relwjhrlewjh')
247 assert bdecode('li1ei2ei3ee') == [1, 2, 3]
248 assert bdecode('l3:asd2:xye') == ['asd', 'xy']
249 assert bdecode('ll5:Alice3:Bobeli2ei3eee') == [['Alice', 'Bob'], [2, 3]]
260 assert bdecode('de') == {}
261 assert bdecode('d3:agei25e4:eyes4:bluee') == {'age': 25, 'eyes': 'blue'}
262 assert bdecode('d8:spam.mp3d6:author5:Alice6:lengthi100000eee') == {'spam.mp3': {'author': 'Alice', 'length': 100000}}
274 bdecode('d1:b0:1:a0:e')
279 bdecode('d1:a0:1:a0:e')
314 bencached_marker = []
317 """Dummy data structure for storing bencoded data in memory.
319 @type marker: C{list}
320 @ivar marker: mutable type to make sure the data was encoded by this class
321 @type bencoded: C{string}
322 @ivar bencoded: the bencoded data stored in a string
326 def __init__(self, s):
330 @param s: the new bencoded data to store
334 self.marker = bencached_marker
337 BencachedType = type(Bencached('')) # insufficient, but good as a filter
339 def encode_bencached(x,r):
340 """Bencode L{Bencached} data.
342 @type x: L{Bencached}
343 @param x: the data to encode
345 @param r: the currently bencoded data, to which the bencoding of x
350 assert x.marker == bencached_marker
354 """Bencode an integer.
357 @param x: the data to encode
359 @param r: the currently bencoded data, to which the bencoding of x
364 r.extend(('i',str(x),'e'))
366 def encode_bool(x,r):
367 """Bencode a boolean.
370 @param x: the data to encode
372 @param r: the currently bencoded data, to which the bencoding of x
379 def encode_string(x,r):
383 @param x: the data to encode
385 @param r: the currently bencoded data, to which the bencoding of x
390 r.extend((str(len(x)),':',x))
392 def encode_unicode(x,r):
393 """Bencode a unicode string.
396 @param x: the data to encode
398 @param r: the currently bencoded data, to which the bencoding of x
404 encode_string(x.encode('UTF-8'),r)
406 def encode_list(x,r):
410 @param x: the data to encode
412 @param r: the currently bencoded data, to which the bencoding of x
419 encode_func[type(e)](e, r)
422 def encode_dict(x,r):
423 """Bencode a dictionary.
425 @type x: C{dictionary}
426 @param x: the data to encode
428 @param r: the currently bencoded data, to which the bencoding of x
437 r.extend((str(len(k)),':',k))
438 encode_func[type(v)](v, r)
442 encode_func[BencachedType] = encode_bencached
443 encode_func[IntType] = encode_int
444 encode_func[LongType] = encode_int
445 encode_func[StringType] = encode_string
446 encode_func[ListType] = encode_list
447 encode_func[TupleType] = encode_list
448 encode_func[DictType] = encode_dict
450 encode_func[BooleanType] = encode_bool
452 encode_func[UnicodeType] = encode_unicode
455 """Bencode some data.
458 @param x: the data to encode
460 @return: the bencoded data
461 @raise ValueError: if the data contains a type that cannot be encoded
466 encode_func[type(x)](x, r)
473 """A test routine for the bencoding functions."""
474 assert bencode(4) == 'i4e'
475 assert bencode(0) == 'i0e'
476 assert bencode(-10) == 'i-10e'
477 assert bencode(12345678901234567890L) == 'i12345678901234567890e'
478 assert bencode('') == '0:'
479 assert bencode('abc') == '3:abc'
480 assert bencode('1234567890') == '10:1234567890'
481 assert bencode([]) == 'le'
482 assert bencode([1, 2, 3]) == 'li1ei2ei3ee'
483 assert bencode([['Alice', 'Bob'], [2, 3]]) == 'll5:Alice3:Bobeli2ei3eee'
484 assert bencode({}) == 'de'
485 assert bencode({'age': 25, 'eyes': 'blue'}) == 'd3:agei25e4:eyes4:bluee'
486 assert bencode({'spam.mp3': {'author': 'Alice', 'length': 100000}}) == 'd8:spam.mp3d6:author5:Alice6:lengthi100000eee'
490 except AssertionError: