bencode 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/bencode.rb +15 -177
- data/lib/bencode/decode.rb +82 -0
- data/lib/bencode/decode_error.rb +4 -0
- data/lib/bencode/encode/array.rb +19 -0
- data/lib/bencode/encode/hash.rb +19 -0
- data/lib/bencode/encode/integer.rb +13 -0
- data/lib/bencode/encode/object.rb +9 -0
- data/lib/bencode/encode/string.rb +14 -0
- data/lib/bencode/encode_error.rb +4 -0
- data/lib/bencode/io.rb +18 -0
- data/test/decode/test_dictionary.rb +18 -0
- data/test/decode/test_integer.rb +32 -0
- data/test/decode/test_invalid.rb +17 -0
- data/test/decode/test_list.rb +24 -0
- data/test/decode/test_string.rb +23 -0
- data/test/encode/test_array.rb +55 -0
- data/test/encode/test_float.rb +11 -0
- data/test/encode/test_hash.rb +27 -0
- data/test/encode/test_integer.rb +27 -0
- data/test/encode/test_string.rb +32 -0
- data/test/suite.rb +5 -0
- data/test/test_decode.rb +4 -0
- data/test/test_encode.rb +4 -0
- metadata +37 -5
- data/test/tc_bdecode.rb +0 -55
- data/test/tc_bencode.rb +0 -57
- data/test/test_suite.rb +0 -3
data/lib/bencode.rb
CHANGED
@@ -5,8 +5,8 @@
|
|
5
5
|
#
|
6
6
|
# == Synopsis
|
7
7
|
#
|
8
|
-
# Bencoding (pronounced <i>bee-encode</i>) is a simple protocol,
|
9
|
-
# only 4 value types.
|
8
|
+
# Bencoding (pronounced <i>bee-encode</i>) is a simple protocol,
|
9
|
+
# consisting of only 4 value types.
|
10
10
|
#
|
11
11
|
# === Integers
|
12
12
|
#
|
@@ -21,8 +21,8 @@
|
|
21
21
|
# === Strings
|
22
22
|
#
|
23
23
|
# Strings are sequences of zero or more bytes. They are encoded as
|
24
|
-
# <i><length>:<contents></i>, where _length_ is the
|
25
|
-
# must be non-negative.
|
24
|
+
# <i><length>:<contents></i>, where _length_ is the
|
25
|
+
# length of _contents_. _length_ must be non-negative.
|
26
26
|
#
|
27
27
|
# "".bencode #=> "0:"
|
28
28
|
# "foo".bencode #=> "3:foo"
|
@@ -36,9 +36,9 @@
|
|
36
36
|
#
|
37
37
|
# === Dictionaries
|
38
38
|
#
|
39
|
-
# Dictionaries are encoded as _d_ followed by a sequence of key-value
|
40
|
-
# Each value must be immediately preceded by
|
41
|
-
# lexicographical order.
|
39
|
+
# Dictionaries are encoded as _d_ followed by a sequence of key-value
|
40
|
+
# pairs, followed by _e_. Each value must be immediately preceded by
|
41
|
+
# a key. Keys must be strings, and must appear in lexicographical order.
|
42
42
|
#
|
43
43
|
# {"foo" => 3, "bar" => 1, "baz" => 2}.bencode
|
44
44
|
# #=> "d3:bari1e3:bazi2e3:fooi3ee"
|
@@ -62,179 +62,17 @@
|
|
62
62
|
# version.
|
63
63
|
#
|
64
64
|
# Bencode is distributed in the hope that it will be useful, but WITHOUT ANY
|
65
|
-
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
66
|
-
# A PARTICULAR PURPOSE. See the GNU General Public License for more
|
67
|
-
#
|
68
|
-
|
69
|
-
class Object
|
70
|
-
#
|
71
|
-
# Raises an exception. Subclasses of Object must themselves
|
72
|
-
# define meaningful #bencode methods.
|
73
|
-
def bencode
|
74
|
-
raise BencodeError, "object cannot be bencoded"
|
75
|
-
end
|
76
|
-
end
|
65
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
66
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
67
|
+
# details.
|
77
68
|
|
78
|
-
class Integer
|
79
|
-
#
|
80
|
-
# Bencodes the Integer object. Bencoded integers are represented
|
81
|
-
# as +ixe+, where +x+ is the integer with an optional
|
82
|
-
# hyphen prepended, indicating negativity.
|
83
|
-
#
|
84
|
-
# 42.bencode #=> "i42e"
|
85
|
-
# -7.bencode #=> "i-7e"
|
86
|
-
def bencode
|
87
|
-
"i#{self}e"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class String
|
92
|
-
#
|
93
|
-
# Bencodes the String object. Bencoded strings are represented
|
94
|
-
# as <code>x</code>:<code>y</code>, where +y+ is the string and +x+ is the length of the
|
95
|
-
# string.
|
96
|
-
#
|
97
|
-
# "foo".bencode #=> "3:foo"
|
98
|
-
# "".bencode #=> "0:"
|
99
|
-
#
|
100
|
-
def bencode
|
101
|
-
"#{length}:#{self}"
|
102
|
-
end
|
103
69
|
|
104
|
-
|
105
|
-
# Bdecodes the String object and returns the data serialized
|
106
|
-
# through bencoding.
|
107
|
-
#
|
108
|
-
# "li1ei2ei3ee".bdecode #=> [1, 2, 3]
|
109
|
-
#
|
110
|
-
def bdecode
|
111
|
-
Bencode.load(self)
|
112
|
-
end
|
70
|
+
prefix = File.dirname(__FILE__)
|
113
71
|
|
114
|
-
|
115
|
-
|
116
|
-
def bencoded?
|
117
|
-
bdecode
|
118
|
-
rescue BdecodeError
|
119
|
-
false
|
120
|
-
else
|
121
|
-
true
|
122
|
-
end
|
72
|
+
%w[object integer string array hash].each do |type|
|
73
|
+
require prefix + '/bencode/encode/' + type
|
123
74
|
end
|
124
75
|
|
125
|
-
|
126
|
-
|
127
|
-
# Bencodes the Array object. Bencoded arrays are represented as
|
128
|
-
# +lxe+, where +x+ is zero or more bencoded objects.
|
129
|
-
#
|
130
|
-
# [1, "foo"].bencode #=> "li1e3:fooe"
|
131
|
-
#
|
132
|
-
def bencode
|
133
|
-
"l#{map{|obj| obj.bencode }.join('')}e"
|
134
|
-
rescue BencodeError
|
135
|
-
raise BencodeError, "list items must be bencodable"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
class Hash
|
140
|
-
#
|
141
|
-
# Bencodes the Hash object. Bencoded hashes are represented as
|
142
|
-
# +dxe+, where +x+ is zero or a power of two bencoded objects.
|
143
|
-
# each key is immediately followed by its associated value.
|
144
|
-
# All keys must be strings. The keys of the bencoded hash will
|
145
|
-
# be in lexicographical order.
|
146
|
-
def bencode
|
147
|
-
pairs = sort.map{|key, val| [key.to_str.bencode, val.bencode] }
|
148
|
-
"d#{pairs.join('')}e"
|
149
|
-
rescue NoMethodError => error
|
150
|
-
if error.name == :to_str
|
151
|
-
raise BencodeError, "dictionary keys must be strings"
|
152
|
-
else
|
153
|
-
raise
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
class IO
|
159
|
-
def self.bdecode(filename)
|
160
|
-
open(filename, 'r').bdecode
|
161
|
-
end
|
162
|
-
|
163
|
-
def self.bencode(filename)
|
164
|
-
open(filename, 'r').bencode
|
165
|
-
end
|
166
|
-
|
167
|
-
def bdecode
|
168
|
-
read.chomp.bdecode
|
169
|
-
end
|
170
|
-
|
171
|
-
def bencode
|
172
|
-
read.chomp.bencode
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
class BencodeError < StandardError; end
|
177
|
-
|
178
|
-
class BdecodeError < StandardError; end
|
179
|
-
|
180
|
-
module Bencode
|
181
|
-
class << self
|
182
|
-
# Bencodes +obj+
|
183
|
-
def dump(obj)
|
184
|
-
obj.bencode
|
185
|
-
end
|
186
|
-
|
187
|
-
# Bdecodes +str+
|
188
|
-
def load(str)
|
189
|
-
require 'strscan'
|
190
|
-
|
191
|
-
scanner = StringScanner.new(str)
|
192
|
-
obj = parse(scanner)
|
193
|
-
raise BdecodeError unless scanner.eos?
|
194
|
-
return obj
|
195
|
-
end
|
196
|
-
|
197
|
-
# Bdecodes the file located at +path+
|
198
|
-
def load_file(path)
|
199
|
-
load(File.open(path).read)
|
200
|
-
end
|
201
|
-
|
202
|
-
def parse(scanner) # :nodoc:
|
203
|
-
case token = scanner.scan(/[ild]|\d+:|\s/)
|
204
|
-
when nil
|
205
|
-
raise BdecodeError
|
206
|
-
when "i"
|
207
|
-
number = scanner.scan(/0|(?:-?[1-9][0-9]*)/)
|
208
|
-
raise BdecodeError unless number and scanner.scan(/e/)
|
209
|
-
return number.to_i
|
210
|
-
when "l"
|
211
|
-
ary = []
|
212
|
-
until scanner.peek(1) == "e"
|
213
|
-
ary.push(parse(scanner))
|
214
|
-
end
|
215
|
-
scanner.pos += 1
|
216
|
-
return ary
|
217
|
-
when "d"
|
218
|
-
hsh = {}
|
219
|
-
until scanner.peek(1) == "e"
|
220
|
-
key, value = parse(scanner), parse(scanner)
|
221
|
-
raise BdecodeError, "key must be a string" unless key.is_a? String
|
222
|
-
hsh.store(key, value)
|
223
|
-
end
|
224
|
-
scanner.pos += 1
|
225
|
-
return hsh
|
226
|
-
when /\d+:/
|
227
|
-
length = token.chop.to_i
|
228
|
-
str = scanner.peek(length)
|
229
|
-
scanner.pos += length
|
230
|
-
return str
|
231
|
-
when /\s/
|
232
|
-
nil
|
233
|
-
else
|
234
|
-
raise BdecodeError
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
private :parse
|
239
|
-
end
|
76
|
+
%w[decode io encode_error decode_error].each do |file|
|
77
|
+
require prefix + '/bencode/' + file
|
240
78
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
module BEncode
|
3
|
+
class << self
|
4
|
+
# BEncodes +obj+
|
5
|
+
def dump(obj)
|
6
|
+
obj.bencode
|
7
|
+
end
|
8
|
+
|
9
|
+
# Bdecodes +str+
|
10
|
+
def load(str)
|
11
|
+
require 'strscan'
|
12
|
+
|
13
|
+
scanner = StringScanner.new(str)
|
14
|
+
obj = parse(scanner)
|
15
|
+
raise BEncode::DecodeError unless scanner.eos?
|
16
|
+
return obj
|
17
|
+
end
|
18
|
+
|
19
|
+
# Bdecodes the file located at +path+
|
20
|
+
def load_file(path)
|
21
|
+
load(File.open(path).read)
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse(scanner) # :nodoc:
|
25
|
+
case token = scanner.scan(/[ild]|\d+:|\s/)
|
26
|
+
when nil
|
27
|
+
raise BEncode::DecodeError
|
28
|
+
when "i"
|
29
|
+
number = scanner.scan(/0|(?:-?[1-9][0-9]*)/)
|
30
|
+
raise BEncode::DecodeError unless number and scanner.scan(/e/)
|
31
|
+
return number.to_i
|
32
|
+
when "l"
|
33
|
+
ary = []
|
34
|
+
until scanner.peek(1) == "e"
|
35
|
+
ary.push(parse(scanner))
|
36
|
+
end
|
37
|
+
scanner.pos += 1
|
38
|
+
return ary
|
39
|
+
when "d"
|
40
|
+
hsh = {}
|
41
|
+
until scanner.peek(1) == "e"
|
42
|
+
key, value = parse(scanner), parse(scanner)
|
43
|
+
|
44
|
+
unless key.is_a? String
|
45
|
+
raise BEncode::DecodeError, "key must be a string"
|
46
|
+
end
|
47
|
+
|
48
|
+
hsh.store(key, value)
|
49
|
+
end
|
50
|
+
scanner.pos += 1
|
51
|
+
return hsh
|
52
|
+
when /\d+:/
|
53
|
+
length = token.chop.to_i
|
54
|
+
str = scanner.peek(length)
|
55
|
+
|
56
|
+
begin
|
57
|
+
scanner.pos += length
|
58
|
+
rescue RangeError
|
59
|
+
raise BEncode::DecodeError, "invalid string length"
|
60
|
+
end
|
61
|
+
|
62
|
+
return str
|
63
|
+
else
|
64
|
+
raise BEncode::DecodeError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private :parse
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class String
|
73
|
+
#
|
74
|
+
# Bdecodes the String object and returns the data serialized
|
75
|
+
# through bencoding.
|
76
|
+
#
|
77
|
+
# "li1ei2ei3ee".bdecode #=> [1, 2, 3]
|
78
|
+
#
|
79
|
+
def bdecode
|
80
|
+
BEncode.load(self)
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
#
|
4
|
+
# Bencodes the Array object. Bencoded arrays are represented as
|
5
|
+
# +lxe+, where +x+ is zero or more bencoded objects.
|
6
|
+
#
|
7
|
+
# [1, "foo"].bencode #=> "li1e3:fooe"
|
8
|
+
#
|
9
|
+
def bencode
|
10
|
+
begin
|
11
|
+
"l#{map{|obj| obj.bencode }.join('')}e"
|
12
|
+
rescue BEncode::EncodeError
|
13
|
+
raise BEncode::EncodeError, "array items must be bencodable"
|
14
|
+
# TODO: This is probably a bad idea...
|
15
|
+
rescue SystemStackError
|
16
|
+
raise BEncode::EncodeError, "cannot encode recursive array"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
class Hash
|
3
|
+
#
|
4
|
+
# Bencodes the Hash object. Bencoded hashes are represented as
|
5
|
+
# +dxe+, where +x+ is zero or a power of two bencoded objects.
|
6
|
+
# each key is immediately followed by its associated value.
|
7
|
+
# All keys must be strings. The keys of the bencoded hash will
|
8
|
+
# be in lexicographical order.
|
9
|
+
def bencode
|
10
|
+
pairs = sort.map{|key, val| [key.to_str.bencode, val.bencode] }
|
11
|
+
"d#{pairs.join('')}e"
|
12
|
+
rescue NoMethodError => error
|
13
|
+
if error.name == :to_str
|
14
|
+
raise BEncode::EncodeError, "dictionary keys must be strings"
|
15
|
+
else
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
class Integer
|
3
|
+
#
|
4
|
+
# Bencodes the Integer object. Bencoded integers are represented
|
5
|
+
# as +ixe+, where +x+ is the integer with an optional
|
6
|
+
# hyphen prepended, indicating negativity.
|
7
|
+
#
|
8
|
+
# 42.bencode #=> "i42e"
|
9
|
+
# -7.bencode #=> "i-7e"
|
10
|
+
def bencode
|
11
|
+
"i#{self}e"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
class String
|
3
|
+
#
|
4
|
+
# Bencodes the String object. Bencoded strings are represented
|
5
|
+
# as <code>x</code>:<code>y</code>, where +y+ is the string and +x+
|
6
|
+
# is the length of the string.
|
7
|
+
#
|
8
|
+
# "foo".bencode #=> "3:foo"
|
9
|
+
# "".bencode #=> "0:"
|
10
|
+
#
|
11
|
+
def bencode
|
12
|
+
"#{length}:#{self}"
|
13
|
+
end
|
14
|
+
end
|
data/lib/bencode/io.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require File.dirname(__FILE__) + "/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BdecodeDictionaryTest < Test::Unit::TestCase
|
6
|
+
def test_empty
|
7
|
+
assert_equal Hash.new, "de".bdecode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_single_pair
|
11
|
+
assert_equal({"a" => 42}, 'd1:ai42ee'.bdecode)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_multi_pair
|
15
|
+
hsh = {"a" => "monkey", "h" => "elephant", "z" => "zebra"}
|
16
|
+
assert_equal hsh, "d1:a6:monkey1:h8:elephant1:z5:zebrae".bdecode
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BdecodeIntegerTest < Test::Unit::TestCase
|
6
|
+
def test_zero
|
7
|
+
assert_equal 0, 'i0e'.bdecode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_single_digit
|
11
|
+
(1..9).each do |digit|
|
12
|
+
assert_equal digit, "i#{digit}e".bdecode
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_multi_digit
|
17
|
+
(1..10).each do |digit|
|
18
|
+
assert_equal digit * 10, "i#{digit * 10}e".bdecode
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_negative
|
23
|
+
(-9..-1).each do |digit|
|
24
|
+
assert_equal digit, "i#{digit}e".bdecode
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_invalid
|
29
|
+
assert_raise(BEncode::DecodeError){ 'i01e'.bdecode }
|
30
|
+
assert_raise(BEncode::DecodeError){ 'i-0e'.bdecode }
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BdecodeInvalidTest < Test::Unit::TestCase
|
6
|
+
errors = {:empty_string => '',
|
7
|
+
:space_string => ' ',
|
8
|
+
:unencoded_string => 'foobar',
|
9
|
+
:multiple_top_items => 'i1ei2e',
|
10
|
+
:string_length => '4:foo'}
|
11
|
+
|
12
|
+
errors.each do |error, data|
|
13
|
+
define_method("test_#{error}") do
|
14
|
+
assert_raise(BEncode::DecodeError){ data.bdecode }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BdecodeListTest < Test::Unit::TestCase
|
6
|
+
def test_empty
|
7
|
+
assert_equal [], 'le'.bdecode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_string_list
|
11
|
+
assert_equal %w[foo], 'l3:fooe'.bdecode
|
12
|
+
assert_equal %w[a b c], 'l1:a1:b1:ce'.bdecode
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_integer_list
|
16
|
+
assert_equal [0], 'li0ee'.bdecode
|
17
|
+
assert_equal [1, 2, 3], 'li1ei2ei3ee'.bdecode
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_list_list
|
21
|
+
assert_equal [[]], 'llee'.bdecode
|
22
|
+
assert_equal [[], [], []], 'llelelee'.bdecode
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BdecodeStringTest < Test::Unit::TestCase
|
6
|
+
def test_empty
|
7
|
+
assert_equal '', '0:'.bdecode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_single_line
|
11
|
+
assert_equal 'foo', '3:foo'.bdecode
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_multi_line
|
15
|
+
assert_equal "a\nb", "3:a\nb".bdecode
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_whitespace
|
19
|
+
[" ", "\t", "\n", "\r"].each do |char|
|
20
|
+
assert_equal char, "1:#{char}".bdecode
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BencodeListTest < Test::Unit::TestCase
|
6
|
+
# Asserts that an empty array is encoded correctly.
|
7
|
+
def test_empty
|
8
|
+
assert_equal 'le', [].bencode
|
9
|
+
end
|
10
|
+
|
11
|
+
# Asserts that arrays of integers are encoded correctly.
|
12
|
+
def test_integer_list
|
13
|
+
assert_equal 'li1ee', [1].bencode
|
14
|
+
assert_equal 'li1ei2ei3ei4ee', [1, 2, 3, 4].bencode
|
15
|
+
end
|
16
|
+
|
17
|
+
# Asserts that trying to encode a list containing a
|
18
|
+
# floating point number raises a +BEncode::EncodeError+.
|
19
|
+
def test_float_list
|
20
|
+
assert_raise BEncode::EncodeError do
|
21
|
+
[13.37].bencode
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_string_list
|
26
|
+
assert_equal 'l3:fooe', %w[foo].bencode
|
27
|
+
assert_equal 'l1:a1:b1:ce', %w[a b c].bencode
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_list_list
|
31
|
+
assert_equal 'llee', [[]].bencode
|
32
|
+
assert_equal 'llleee', [[[]]].bencode
|
33
|
+
assert_equal 'llelelee', [[], [], []].bencode
|
34
|
+
end
|
35
|
+
|
36
|
+
# Asserts that a recursive array is not bencoded.
|
37
|
+
# TODO: Uses waaaaay too much time. What to do?
|
38
|
+
def _test_recursive_list
|
39
|
+
assert_raise BEncode::EncodeError do
|
40
|
+
list = []
|
41
|
+
list << list
|
42
|
+
list.bencode
|
43
|
+
end
|
44
|
+
|
45
|
+
assert_raise BEncode::EncodeError do
|
46
|
+
list = [[]]
|
47
|
+
list.first << list
|
48
|
+
list.bencode
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_dict_list
|
53
|
+
assert_equal 'ldee', [{}].bencode
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BencodeDictionaryTest < Test::Unit::TestCase
|
6
|
+
def test_empty
|
7
|
+
assert_equal 'de', {}.bencode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_integer_hash
|
11
|
+
assert_equal 'd1:ai1ee', {'a' => 1}.bencode
|
12
|
+
assert_equal 'd1:ai1e1:bi2e1:ci3ee',
|
13
|
+
{'a' => 1, 'b' => 2, 'c' => 3}.bencode
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_illegal_keys
|
17
|
+
assert_raise BEncode::EncodeError do
|
18
|
+
{1 => 'foo'}.bencode
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Thank you, Julien
|
23
|
+
def test_key_order
|
24
|
+
assert_equal 'd1:ai1e2:bbi3e1:ci2ee',
|
25
|
+
{'a' => 1, 'c' => 2, 'bb' => 3}.bencode
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BencodeIntegerTest < Test::Unit::TestCase
|
6
|
+
def test_zero
|
7
|
+
assert_equal 'i0e', 0.bencode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_single_digit
|
11
|
+
(1..9).each do |digit|
|
12
|
+
assert_equal "i#{digit}e", digit.bencode
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_multi_digit
|
17
|
+
(1..10).each do |digit|
|
18
|
+
assert_equal "i#{digit * 10}e", (digit * 10).bencode
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_negative
|
23
|
+
(-9..-1).each do |digit|
|
24
|
+
assert_equal "i#{digit}e", digit.bencode
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require "#{File.dirname(__FILE__)}/../../lib/bencode.rb"
|
4
|
+
|
5
|
+
class BencodeStringTest < Test::Unit::TestCase
|
6
|
+
def test_empty
|
7
|
+
assert_equal '0:', ''.bencode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_single_char
|
11
|
+
('a'..'z').each do |char|
|
12
|
+
assert_equal '1:' + char, char.bencode
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_single_line
|
17
|
+
assert_equal '2:ab', 'ab'.bencode
|
18
|
+
assert_equal '3:abc', 'abc'.bencode
|
19
|
+
assert_equal '4:abcd', 'abcd'.bencode
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_whitespace
|
23
|
+
[" ", "\t", "\n", "\r"].each do |char|
|
24
|
+
assert_equal '1:' + char, char.bencode
|
25
|
+
assert_equal '2:' + char * 2, (char * 2).bencode
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_multi_line
|
30
|
+
assert_equal "3:a\nb", "a\nb".bencode
|
31
|
+
end
|
32
|
+
end
|
data/test/suite.rb
ADDED
data/test/test_decode.rb
ADDED
data/test/test_encode.rb
ADDED
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: bencode
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date:
|
6
|
+
version: 0.5.0
|
7
|
+
date: 2007-02-07 00:00:00 +01:00
|
8
8
|
summary: A Ruby implementation of the Bencode encoding used by BitTorrent
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -30,10 +30,42 @@ authors:
|
|
30
30
|
- Daniel Schierbeck
|
31
31
|
files:
|
32
32
|
- lib/bencode.rb
|
33
|
+
- lib/bencode/io.rb
|
34
|
+
- lib/bencode/encode_error.rb
|
35
|
+
- lib/bencode/decode.rb
|
36
|
+
- lib/bencode/decode_error.rb
|
37
|
+
- lib/bencode/encode/object.rb
|
38
|
+
- lib/bencode/encode/integer.rb
|
39
|
+
- lib/bencode/encode/hash.rb
|
40
|
+
- lib/bencode/encode/array.rb
|
41
|
+
- lib/bencode/encode/string.rb
|
42
|
+
- test/test_decode.rb
|
43
|
+
- test/test_encode.rb
|
44
|
+
- test/suite.rb
|
45
|
+
- test/decode/test_invalid.rb
|
46
|
+
- test/decode/test_dictionary.rb
|
47
|
+
- test/decode/test_integer.rb
|
48
|
+
- test/decode/test_string.rb
|
49
|
+
- test/decode/test_list.rb
|
50
|
+
- test/encode/test_float.rb
|
51
|
+
- test/encode/test_hash.rb
|
52
|
+
- test/encode/test_integer.rb
|
53
|
+
- test/encode/test_string.rb
|
54
|
+
- test/encode/test_array.rb
|
33
55
|
test_files:
|
34
|
-
- test/
|
35
|
-
- test/
|
36
|
-
- test/
|
56
|
+
- test/test_decode.rb
|
57
|
+
- test/test_encode.rb
|
58
|
+
- test/suite.rb
|
59
|
+
- test/decode/test_invalid.rb
|
60
|
+
- test/decode/test_dictionary.rb
|
61
|
+
- test/decode/test_integer.rb
|
62
|
+
- test/decode/test_string.rb
|
63
|
+
- test/decode/test_list.rb
|
64
|
+
- test/encode/test_float.rb
|
65
|
+
- test/encode/test_hash.rb
|
66
|
+
- test/encode/test_integer.rb
|
67
|
+
- test/encode/test_string.rb
|
68
|
+
- test/encode/test_array.rb
|
37
69
|
rdoc_options: []
|
38
70
|
|
39
71
|
extra_rdoc_files: []
|
data/test/tc_bdecode.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'test/unit'
|
3
|
-
require "#{File.dirname(__FILE__)}/../lib/bencode"
|
4
|
-
|
5
|
-
class BdecodeTest < Test::Unit::TestCase
|
6
|
-
def test_string
|
7
|
-
assert_equal "", "0:".bdecode
|
8
|
-
assert_equal "foo", "3:foo".bdecode
|
9
|
-
assert_equal "foo bar", "7:foo bar".bdecode
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_integer
|
13
|
-
assert_equal 0, "i0e".bdecode
|
14
|
-
assert_equal 42, "i42e".bdecode
|
15
|
-
assert_equal -7, "i-7e".bdecode
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_invalid_integer
|
19
|
-
assert_raise BdecodeError do
|
20
|
-
"i01e".bdecode
|
21
|
-
end
|
22
|
-
assert_raise BdecodeError do
|
23
|
-
"i-01e".bdecode
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_array
|
28
|
-
assert_equal [], "le".bdecode
|
29
|
-
assert_equal ["foo"], "l3:fooe".bdecode
|
30
|
-
assert_equal [1, 2, 3], "li1ei2ei3ee".bdecode
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_hash
|
34
|
-
assert_equal Hash.new, "de".bdecode
|
35
|
-
|
36
|
-
hsh = {"a" => "monkey", "h" => "elephant", "z" => "zebra"}
|
37
|
-
assert_equal hsh, "d1:a6:monkey1:h8:elephant1:z5:zebrae".bdecode
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_invalid
|
41
|
-
assert_raises BdecodeError do
|
42
|
-
"foobar".bdecode
|
43
|
-
"i1ei2e".bdecode
|
44
|
-
"4:foo".bdecode
|
45
|
-
"2:bar".bdecode
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_valid
|
50
|
-
assert "3:foo".bencoded?
|
51
|
-
assert !"3:foo3:bar".bencoded?
|
52
|
-
assert "i42e".bencoded?
|
53
|
-
assert !"i42ei101e".bencoded?
|
54
|
-
end
|
55
|
-
end
|
data/test/tc_bencode.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'test/unit'
|
3
|
-
require "#{File.dirname(__FILE__)}/../lib/bencode"
|
4
|
-
|
5
|
-
class BencodeTest < Test::Unit::TestCase
|
6
|
-
def test_string
|
7
|
-
assert_equal "3:foo", "foo".bencode
|
8
|
-
assert_equal "0:", "".bencode
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_tab_string
|
12
|
-
assert_equal "1:\t", "\t".bencode
|
13
|
-
assert_equal "2:\t\t", "\t\t".bencode
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_multiline_string
|
17
|
-
assert_equal "1:\n", "\n".bencode
|
18
|
-
assert_equal "2:\n\n", "\n\n".bencode
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_integer
|
22
|
-
assert_equal "i0e", 0.bencode
|
23
|
-
assert_equal "i42e", 42.bencode
|
24
|
-
assert_equal "i-3e", -3.bencode
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_float
|
28
|
-
assert_raise BencodeError do
|
29
|
-
42.4.bencode
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_array
|
34
|
-
assert_equal "le", [].bencode
|
35
|
-
assert_equal "li1ei2ei3ee", [1, 2, 3].bencode
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_hash
|
39
|
-
assert_equal "de", {}.bencode
|
40
|
-
assert_equal "d1:a3:foo1:g3:bar1:z3:baze",
|
41
|
-
{"a" => "foo", "g" => "bar", "z" => "baz"}.bencode
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_illegal_hash_keys
|
45
|
-
assert_raise BencodeError do
|
46
|
-
{1 => 'foo'}.bencode
|
47
|
-
end
|
48
|
-
assert_raise BencodeError do
|
49
|
-
{1 => 'foo', 2 => 'bar'}.bencode
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_hash_julien
|
54
|
-
assert_equal "d1:ai1e2:bbi1e1:ci1ee",
|
55
|
-
{'a' => 1, 'c' => 1, 'bb' => 1}.bencode
|
56
|
-
end
|
57
|
-
end
|
data/test/test_suite.rb
DELETED