]>
Commit | Line | Data |
---|---|---|
879aa787 MT |
1 | # Written by Petru Paler, Uoti Urpala, Ross Cohen and John Hoffman |
2 | # see LICENSE.txt for license information | |
3 | ||
4 | from types import IntType, LongType, StringType, ListType, TupleType, DictType | |
5 | try: | |
6 | from types import BooleanType | |
7 | except ImportError: | |
8 | BooleanType = None | |
9 | try: | |
10 | from types import UnicodeType | |
11 | except ImportError: | |
12 | UnicodeType = None | |
13 | from cStringIO import StringIO | |
14 | ||
15 | def decode_int(x, f): | |
16 | f += 1 | |
17 | newf = x.index('e', f) | |
18 | try: | |
19 | n = int(x[f:newf]) | |
20 | except: | |
21 | n = long(x[f:newf]) | |
22 | if x[f] == '-': | |
23 | if x[f + 1] == '0': | |
24 | raise ValueError | |
25 | elif x[f] == '0' and newf != f+1: | |
26 | raise ValueError | |
27 | return (n, newf+1) | |
28 | ||
29 | def decode_string(x, f): | |
30 | colon = x.index(':', f) | |
31 | try: | |
32 | n = int(x[f:colon]) | |
33 | except (OverflowError, ValueError): | |
34 | n = long(x[f:colon]) | |
35 | if x[f] == '0' and colon != f+1: | |
36 | raise ValueError | |
37 | colon += 1 | |
38 | return (x[colon:colon+n], colon+n) | |
39 | ||
40 | def decode_unicode(x, f): | |
41 | s, f = decode_string(x, f+1) | |
42 | return (s.decode('UTF-8'),f) | |
43 | ||
44 | def decode_list(x, f): | |
45 | r, f = [], f+1 | |
46 | while x[f] != 'e': | |
47 | v, f = decode_func[x[f]](x, f) | |
48 | r.append(v) | |
49 | return (r, f + 1) | |
50 | ||
51 | def decode_dict(x, f): | |
52 | r, f = {}, f+1 | |
53 | lastkey = None | |
54 | while x[f] != 'e': | |
55 | k, f = decode_string(x, f) | |
56 | if lastkey >= k: | |
57 | raise ValueError | |
58 | lastkey = k | |
59 | r[k], f = decode_func[x[f]](x, f) | |
60 | return (r, f + 1) | |
61 | ||
62 | decode_func = {} | |
63 | decode_func['l'] = decode_list | |
64 | decode_func['d'] = decode_dict | |
65 | decode_func['i'] = decode_int | |
66 | decode_func['0'] = decode_string | |
67 | decode_func['1'] = decode_string | |
68 | decode_func['2'] = decode_string | |
69 | decode_func['3'] = decode_string | |
70 | decode_func['4'] = decode_string | |
71 | decode_func['5'] = decode_string | |
72 | decode_func['6'] = decode_string | |
73 | decode_func['7'] = decode_string | |
74 | decode_func['8'] = decode_string | |
75 | decode_func['9'] = decode_string | |
76 | #decode_func['u'] = decode_unicode | |
77 | ||
78 | def bdecode(x, sloppy = 0): | |
79 | try: | |
80 | r, l = decode_func[x[0]](x, 0) | |
81 | # except (IndexError, KeyError): | |
82 | except (IndexError, KeyError, ValueError): | |
83 | raise ValueError, "bad bencoded data" | |
84 | if not sloppy and l != len(x): | |
85 | raise ValueError, "bad bencoded data" | |
86 | return r | |
87 | ||
88 | def test_bdecode(): | |
89 | try: | |
90 | bdecode('0:0:') | |
91 | assert 0 | |
92 | except ValueError: | |
93 | pass | |
94 | try: | |
95 | bdecode('ie') | |
96 | assert 0 | |
97 | except ValueError: | |
98 | pass | |
99 | try: | |
100 | bdecode('i341foo382e') | |
101 | assert 0 | |
102 | except ValueError: | |
103 | pass | |
104 | assert bdecode('i4e') == 4L | |
105 | assert bdecode('i0e') == 0L | |
106 | assert bdecode('i123456789e') == 123456789L | |
107 | assert bdecode('i-10e') == -10L | |
108 | try: | |
109 | bdecode('i-0e') | |
110 | assert 0 | |
111 | except ValueError: | |
112 | pass | |
113 | try: | |
114 | bdecode('i123') | |
115 | assert 0 | |
116 | except ValueError: | |
117 | pass | |
118 | try: | |
119 | bdecode('') | |
120 | assert 0 | |
121 | except ValueError: | |
122 | pass | |
123 | try: | |
124 | bdecode('i6easd') | |
125 | assert 0 | |
126 | except ValueError: | |
127 | pass | |
128 | try: | |
129 | bdecode('35208734823ljdahflajhdf') | |
130 | assert 0 | |
131 | except ValueError: | |
132 | pass | |
133 | try: | |
134 | bdecode('2:abfdjslhfld') | |
135 | assert 0 | |
136 | except ValueError: | |
137 | pass | |
138 | assert bdecode('0:') == '' | |
139 | assert bdecode('3:abc') == 'abc' | |
140 | assert bdecode('10:1234567890') == '1234567890' | |
141 | try: | |
142 | bdecode('02:xy') | |
143 | assert 0 | |
144 | except ValueError: | |
145 | pass | |
146 | try: | |
147 | bdecode('l') | |
148 | assert 0 | |
149 | except ValueError: | |
150 | pass | |
151 | assert bdecode('le') == [] | |
152 | try: | |
153 | bdecode('leanfdldjfh') | |
154 | assert 0 | |
155 | except ValueError: | |
156 | pass | |
157 | assert bdecode('l0:0:0:e') == ['', '', ''] | |
158 | try: | |
159 | bdecode('relwjhrlewjh') | |
160 | assert 0 | |
161 | except ValueError: | |
162 | pass | |
163 | assert bdecode('li1ei2ei3ee') == [1, 2, 3] | |
164 | assert bdecode('l3:asd2:xye') == ['asd', 'xy'] | |
165 | assert bdecode('ll5:Alice3:Bobeli2ei3eee') == [['Alice', 'Bob'], [2, 3]] | |
166 | try: | |
167 | bdecode('d') | |
168 | assert 0 | |
169 | except ValueError: | |
170 | pass | |
171 | try: | |
172 | bdecode('defoobar') | |
173 | assert 0 | |
174 | except ValueError: | |
175 | pass | |
176 | assert bdecode('de') == {} | |
177 | assert bdecode('d3:agei25e4:eyes4:bluee') == {'age': 25, 'eyes': 'blue'} | |
178 | assert bdecode('d8:spam.mp3d6:author5:Alice6:lengthi100000eee') == {'spam.mp3': {'author': 'Alice', 'length': 100000}} | |
179 | try: | |
180 | bdecode('d3:fooe') | |
181 | assert 0 | |
182 | except ValueError: | |
183 | pass | |
184 | try: | |
185 | bdecode('di1e0:e') | |
186 | assert 0 | |
187 | except ValueError: | |
188 | pass | |
189 | try: | |
190 | bdecode('d1:b0:1:a0:e') | |
191 | assert 0 | |
192 | except ValueError: | |
193 | pass | |
194 | try: | |
195 | bdecode('d1:a0:1:a0:e') | |
196 | assert 0 | |
197 | except ValueError: | |
198 | pass | |
199 | try: | |
200 | bdecode('i03e') | |
201 | assert 0 | |
202 | except ValueError: | |
203 | pass | |
204 | try: | |
205 | bdecode('l01:ae') | |
206 | assert 0 | |
207 | except ValueError: | |
208 | pass | |
209 | try: | |
210 | bdecode('9999:x') | |
211 | assert 0 | |
212 | except ValueError: | |
213 | pass | |
214 | try: | |
215 | bdecode('l0:') | |
216 | assert 0 | |
217 | except ValueError: | |
218 | pass | |
219 | try: | |
220 | bdecode('d0:0:') | |
221 | assert 0 | |
222 | except ValueError: | |
223 | pass | |
224 | try: | |
225 | bdecode('d0:') | |
226 | assert 0 | |
227 | except ValueError: | |
228 | pass | |
229 | ||
230 | bencached_marker = [] | |
231 | ||
232 | class Bencached: | |
233 | def __init__(self, s): | |
234 | self.marker = bencached_marker | |
235 | self.bencoded = s | |
236 | ||
237 | BencachedType = type(Bencached('')) # insufficient, but good as a filter | |
238 | ||
239 | def encode_bencached(x,r): | |
240 | assert x.marker == bencached_marker | |
241 | r.append(x.bencoded) | |
242 | ||
243 | def encode_int(x,r): | |
244 | r.extend(('i',str(x),'e')) | |
245 | ||
246 | def encode_bool(x,r): | |
247 | encode_int(int(x),r) | |
248 | ||
249 | def encode_string(x,r): | |
250 | r.extend((str(len(x)),':',x)) | |
251 | ||
252 | def encode_unicode(x,r): | |
253 | #r.append('u') | |
254 | encode_string(x.encode('UTF-8'),r) | |
255 | ||
256 | def encode_list(x,r): | |
257 | r.append('l') | |
258 | for e in x: | |
259 | encode_func[type(e)](e, r) | |
260 | r.append('e') | |
261 | ||
262 | def encode_dict(x,r): | |
263 | r.append('d') | |
264 | ilist = x.items() | |
265 | ilist.sort() | |
266 | for k,v in ilist: | |
267 | r.extend((str(len(k)),':',k)) | |
268 | encode_func[type(v)](v, r) | |
269 | r.append('e') | |
270 | ||
271 | encode_func = {} | |
272 | encode_func[BencachedType] = encode_bencached | |
273 | encode_func[IntType] = encode_int | |
274 | encode_func[LongType] = encode_int | |
275 | encode_func[StringType] = encode_string | |
276 | encode_func[ListType] = encode_list | |
277 | encode_func[TupleType] = encode_list | |
278 | encode_func[DictType] = encode_dict | |
279 | if BooleanType: | |
280 | encode_func[BooleanType] = encode_bool | |
281 | if UnicodeType: | |
282 | encode_func[UnicodeType] = encode_unicode | |
283 | ||
284 | def bencode(x): | |
285 | r = [] | |
286 | try: | |
287 | encode_func[type(x)](x, r) | |
288 | except: | |
289 | print "*** error *** could not encode type %s (value: %s)" % (type(x), x) | |
290 | assert 0 | |
291 | return ''.join(r) | |
292 | ||
293 | def test_bencode(): | |
294 | assert bencode(4) == 'i4e' | |
295 | assert bencode(0) == 'i0e' | |
296 | assert bencode(-10) == 'i-10e' | |
297 | assert bencode(12345678901234567890L) == 'i12345678901234567890e' | |
298 | assert bencode('') == '0:' | |
299 | assert bencode('abc') == '3:abc' | |
300 | assert bencode('1234567890') == '10:1234567890' | |
301 | assert bencode([]) == 'le' | |
302 | assert bencode([1, 2, 3]) == 'li1ei2ei3ee' | |
303 | assert bencode([['Alice', 'Bob'], [2, 3]]) == 'll5:Alice3:Bobeli2ei3eee' | |
304 | assert bencode({}) == 'de' | |
305 | assert bencode({'age': 25, 'eyes': 'blue'}) == 'd3:agei25e4:eyes4:bluee' | |
306 | assert bencode({'spam.mp3': {'author': 'Alice', 'length': 100000}}) == 'd8:spam.mp3d6:author5:Alice6:lengthi100000eee' | |
307 | try: | |
308 | bencode({1: 'foo'}) | |
309 | assert 0 | |
310 | except AssertionError: | |
311 | pass | |
312 | ||
313 | ||
314 | try: | |
315 | import psyco | |
316 | psyco.bind(bdecode) | |
317 | psyco.bind(bencode) | |
318 | except ImportError: | |
319 | pass |