/
bencoding.py
95 lines (91 loc) · 3.5 KB
/
bencoding.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import utilities
class Decoder:
offset = 0
beginCallback = None
endCallback = None
def __begin( self, what, offset ):
if self.beginCallback is not None:
self.beginCallback( what, offset )
def __end( self, what, offset, value ):
if self.endCallback is not None:
self.endCallback( what, offset, value )
def setCallback( self, begin, end ):
self.beginCallback = begin
self.endCallback = end
def decode( self, data ):
self.offset = 0
def decode_one( data ):
if data[ 0 ] == 'i':
# data is an integer
self.__begin( 'integer', self.offset )
self.offset += 1
data = data[ 1: ]
pos = data.index( 'e' )
value = int( data[ 0:pos ] )
self.offset += pos + 1
data = data[ ( pos + 1 ): ]
self.__end( 'integer', self.offset, value )
return ( value, data )
if data[ 0 ] == 'l':
# data is a list
self.__begin( 'list', self.offset )
self.offset += 1
data = data[ 1: ]
value = []
while data[ 0 ] != 'e':
( item, data ) = decode_one( data )
value.append( item )
self.offset += 1
data = data[ 1: ]
self.__end( 'list', self.offset, value )
return ( value, data )
if data[ 0 ] == 'd':
# data is a dictionary
self.__begin( 'dictionary', self.offset )
self.offset += 1
data = data[ 1: ]
d = {}
while data[ 0 ] != 'e':
self.__begin( 'key', self.offset )
( key, data ) = decode_one( data )
self.__end( 'key', self.offset, key )
self.__begin( 'value', self.offset )
( value, data ) = decode_one( data )
self.__end( 'value', self.offset, value )
d[ key ] = value
self.offset += 1
data = data[ 1: ]
self.__end( 'dictionary', self.offset, d )
return ( d, data )
# default case: data is a string
self.__begin( 'string', self.offset )
pos = data.index( ':' )
length = int( data[ :pos ] )
self.offset += pos + 1
data = data[ ( pos + 1 ): ]
value = data[ 0:length ]
self.offset += length
data = data[ length: ]
self.__end( 'string', self.offset, value )
return ( value, data )
return decode_one( data )[ 0 ]
def decode( data ):
# def begin( what, offset ):
# print( "Starting %s at %i." % ( what, offset ) )
# def end( what, offset, value ):
# print( "Ending %s with value '%s' at %i." % ( what, value, offset ) )
decoder = Decoder()
# decoder.setCallback( begin, end )
return decoder.decode( data )
def encode( data ):
if type( data ) is str:
return str( len( data ) ) + ':' + data
if type( data ) is int:
return 'i' + str( data ) + 'e'
if type( data ) is list:
return 'l' + ''.join( map( encode, data ) ) + 'e'
if type( data ) is dict:
flattened = utilities.flatten( data.items() )
encoded = map( encode, flattened )
joined = ''.join( encoded )
return 'd' + joined + 'e'