93af40744a6365578f7aa4b1589a98c451fe49f4
[quix0rs-apt-p2p.git] / bencode.py
1 # Written by Petru Paler
2 # see LICENSE.txt for license information
3
4 from types import IntType, LongType, StringType, ListType, TupleType, DictType
5 import re
6 from cStringIO import StringIO
7
8 int_filter = re.compile('(0|-?[1-9][0-9]*)e')
9
10 def decode_int(x, f):
11     m = int_filter.match(x, f)
12     if m is None:
13         raise ValueError
14     return (long(m.group(1)), m.end())
15
16 string_filter = re.compile('(0|[1-9][0-9]*):')
17
18 def decode_string(x, f):
19     m = string_filter.match(x, f)
20     if m is None:
21         raise ValueError
22     l = int(m.group(1))
23     s = m.end()
24     return (x[s:s+l], s + l)
25
26 def decode_list(x, f):
27     r = []
28     while x[f] != 'e':
29         v, f = bdecode_rec(x, f)
30         r.append(v)
31     return (r, f + 1)
32
33 def decode_dict(x, f):
34     r = {}
35     lastkey = None
36     while x[f] != 'e':
37         k, f = decode_string(x, f)
38         if lastkey is not None and lastkey >= k:
39             raise ValueError
40         lastkey = k
41         v, f = bdecode_rec(x, f)
42         r[k] = v
43     return (r, f + 1)
44
45 def bdecode_rec(x, f):
46     t = x[f]
47     if t == 'i':
48         return decode_int(x, f + 1)
49     elif t == 'l':
50         return decode_list(x, f + 1)
51     elif t == 'd':
52         return decode_dict(x, f + 1)
53     else:
54         return decode_string(x, f)
55
56 def bdecode(x):
57     try:
58         r, l = bdecode_rec(x, 0)
59     except IndexError:
60         raise ValueError
61     if l != len(x):
62         raise ValueError
63     return r
64
65 def test_bdecode():
66     try:
67         bdecode('0:0:')
68         assert 0
69     except ValueError:
70         pass
71     try:
72         bdecode('ie')
73         assert 0
74     except ValueError:
75         pass
76     try:
77         bdecode('i341foo382e')
78         assert 0
79     except ValueError:
80         pass
81     assert bdecode('i4e') == 4L
82     assert bdecode('i0e') == 0L
83     assert bdecode('i123456789e') == 123456789L
84     assert bdecode('i-10e') == -10L
85     try:
86         bdecode('i-0e')
87         assert 0
88     except ValueError:
89         pass
90     try:
91         bdecode('i123')
92         assert 0
93     except ValueError:
94         pass
95     try:
96         bdecode('')
97         assert 0
98     except ValueError:
99         pass
100     try:
101         bdecode('i6easd')
102         assert 0
103     except ValueError:
104         pass
105     try:
106         bdecode('35208734823ljdahflajhdf')
107         assert 0
108     except ValueError:
109         pass
110     try:
111         bdecode('2:abfdjslhfld')
112         assert 0
113     except ValueError:
114         pass
115     assert bdecode('0:') == ''
116     assert bdecode('3:abc') == 'abc'
117     assert bdecode('10:1234567890') == '1234567890'
118     try:
119         bdecode('02:xy')
120         assert 0
121     except ValueError:
122         pass
123     try:
124         bdecode('l')
125         assert 0
126     except ValueError:
127         pass
128     assert bdecode('le') == []
129     try:
130         bdecode('leanfdldjfh')
131         assert 0
132     except ValueError:
133         pass
134     assert bdecode('l0:0:0:e') == ['', '', '']
135     try:
136         bdecode('relwjhrlewjh')
137         assert 0
138     except ValueError:
139         pass
140     assert bdecode('li1ei2ei3ee') == [1, 2, 3]
141     assert bdecode('l3:asd2:xye') == ['asd', 'xy']
142     assert bdecode('ll5:Alice3:Bobeli2ei3eee') == [['Alice', 'Bob'], [2, 3]]
143     try:
144         bdecode('d')
145         assert 0
146     except ValueError:
147         pass
148     try:
149         bdecode('defoobar')
150         assert 0
151     except ValueError:
152         pass
153     assert bdecode('de') == {}
154     assert bdecode('d3:agei25e4:eyes4:bluee') == {'age': 25, 'eyes': 'blue'}
155     assert bdecode('d8:spam.mp3d6:author5:Alice6:lengthi100000eee') == {'spam.mp3': {'author': 'Alice', 'length': 100000}}
156     try:
157         bdecode('d3:fooe')
158         assert 0
159     except ValueError:
160         pass
161     try:
162         bdecode('di1e0:e')
163         assert 0
164     except ValueError:
165         pass
166     try:
167         bdecode('d1:b0:1:a0:e')
168         assert 0
169     except ValueError:
170         pass
171     try:
172         bdecode('d1:a0:1:a0:e')
173         assert 0
174     except ValueError:
175         pass
176     try:
177         bdecode('i03e')
178         assert 0
179     except ValueError:
180         pass
181     try:
182         bdecode('l01:ae')
183         assert 0
184     except ValueError:
185         pass
186     try:
187         bdecode('9999:x')
188         assert 0
189     except ValueError:
190         pass
191     try:
192         bdecode('l0:')
193         assert 0
194     except ValueError:
195         pass
196     try:
197         bdecode('d0:0:')
198         assert 0
199     except ValueError:
200         pass
201     try:
202         bdecode('d0:')
203         assert 0
204     except ValueError:
205         pass
206
207 def bencode_rec(x, b):
208     t = type(x)
209     if t in (IntType, LongType):
210         b.write('i%de' % x)
211     elif t is StringType:
212         b.write('%d:%s' % (len(x), x))
213     elif t in (ListType, TupleType):
214         b.write('l')
215         for e in x:
216             bencode_rec(e, b)
217         b.write('e')
218     elif t is DictType:
219         b.write('d')
220         keylist = x.keys()
221         keylist.sort()
222         for k in keylist:
223             assert type(k) is StringType
224             bencode_rec(k, b)
225             bencode_rec(x[k], b)
226         b.write('e')
227     else:
228         assert 0
229
230 def bencode(x):
231     b = StringIO()
232     bencode_rec(x, b)
233     return b.getvalue()
234
235 def test_bencode():
236     assert bencode(4) == 'i4e'
237     assert bencode(0) == 'i0e'
238     assert bencode(-10) == 'i-10e'
239     assert bencode(12345678901234567890L) == 'i12345678901234567890e'
240     assert bencode('') == '0:'
241     assert bencode('abc') == '3:abc'
242     assert bencode('1234567890') == '10:1234567890'
243     assert bencode([]) == 'le'
244     assert bencode([1, 2, 3]) == 'li1ei2ei3ee'
245     assert bencode([['Alice', 'Bob'], [2, 3]]) == 'll5:Alice3:Bobeli2ei3eee'
246     assert bencode({}) == 'de'
247     assert bencode({'age': 25, 'eyes': 'blue'}) == 'd3:agei25e4:eyes4:bluee'
248     assert bencode({'spam.mp3': {'author': 'Alice', 'length': 100000}}) == 'd8:spam.mp3d6:author5:Alice6:lengthi100000eee'
249     try:
250         bencode({1: 'foo'})
251         assert 0
252     except AssertionError:
253         pass
254