bencode 0.0.1
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 +183 -0
- data/test/tc_bdecode.rb +42 -0
- data/test/tc_bencode.rb +30 -0
- metadata +48 -0
data/lib/bencode.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
# Thanks to Daniel Martin and all the other lads at ruby-lang for
|
2
|
+
# helping me out.
|
3
|
+
|
4
|
+
|
5
|
+
class Object
|
6
|
+
# Raises an exception. Subclasses of Object must themselves
|
7
|
+
# define meaningful #bencode methods.
|
8
|
+
def bencode
|
9
|
+
raise BencodeError, self.class
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Integer
|
14
|
+
# Bencodes the Integer object. Bencoded integers are represented
|
15
|
+
# as +ixe+, where +x+ is the integer with an optional
|
16
|
+
# hyphen prepended, indicating negativity.
|
17
|
+
#
|
18
|
+
# 42.bencode #=> "i42e"
|
19
|
+
# -7.bencode #=> "i-7e"
|
20
|
+
def bencode
|
21
|
+
"i#{self}e"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class String
|
26
|
+
# Bencodes the String object. Bencoded strings are represented
|
27
|
+
# as +x:y+, where +y+ is the string and x is the length of the
|
28
|
+
# string.
|
29
|
+
#
|
30
|
+
# "foo".bencode #=> "3:foo"
|
31
|
+
# "".bencode #=> "0:"
|
32
|
+
def bencode
|
33
|
+
"#{length}:#{self}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Bdecodes the String object and returns the data serialized
|
37
|
+
# through bencoding.
|
38
|
+
#
|
39
|
+
# "li1ei2ei3ee".bdecode #=> [1, 2, 3]
|
40
|
+
def bdecode
|
41
|
+
Bencode.load(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Tests whether the String object is a valid bencoded string.
|
45
|
+
def bencoded?
|
46
|
+
bdecode
|
47
|
+
rescue BdecodeError
|
48
|
+
false
|
49
|
+
else
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Array
|
55
|
+
# Bencodes the Array object. Bencoded arrays are represented as
|
56
|
+
# +lxe+, where x is zero or more bencoded objects.
|
57
|
+
#
|
58
|
+
# [1, "foo"].bencode #=> "li1e3:fooe"
|
59
|
+
def bencode
|
60
|
+
"l#{map{|obj| obj.bencode}.join('') }e"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Hash
|
65
|
+
# Bencodes the Hash object. Bencoded hashes are represented as
|
66
|
+
# +dxe+, where x is zero or a power of two bencoded objects.
|
67
|
+
# each key is immediately followed by its associated value.
|
68
|
+
# All keys must be strings. The keys of the bencoded hash will
|
69
|
+
# be in lexicographical order.
|
70
|
+
def bencode
|
71
|
+
pairs = map{|key, val| [key.to_str.bencode, val.bencode] }
|
72
|
+
pairs.sort!{|a, b| a.first <=> b.first }
|
73
|
+
"d#{pairs.join('')}e"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class IO
|
78
|
+
def self.bdecode(*args)
|
79
|
+
new(*args).bdecode
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.bencode(*args)
|
83
|
+
new(*args).bencode
|
84
|
+
end
|
85
|
+
|
86
|
+
def bdecode
|
87
|
+
read.chomp.bdecode
|
88
|
+
end
|
89
|
+
|
90
|
+
def bencode
|
91
|
+
read.chomp.bdecode
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class BencodeError < StandardError
|
96
|
+
def initialize(object_class = nil) # :nodoc:
|
97
|
+
@object_class = object_class
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_s # :nodoc:
|
101
|
+
"could not bencode #{@object_class}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class BdecodeError < StandardError
|
106
|
+
attr_reader :pos
|
107
|
+
|
108
|
+
def initialize(pos = nil) # :nodoc:
|
109
|
+
@pos = pos
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_s # :nodoc:
|
113
|
+
if pos.nil?
|
114
|
+
"syntax error"
|
115
|
+
else
|
116
|
+
"syntax error near position #{pos}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
module Bencode
|
122
|
+
class << self
|
123
|
+
def dump(obj)
|
124
|
+
obj.bencode
|
125
|
+
end
|
126
|
+
|
127
|
+
def load(str)
|
128
|
+
require 'strscan'
|
129
|
+
|
130
|
+
scanner = StringScanner.new(str)
|
131
|
+
obj = parse(scanner)
|
132
|
+
raise BdecodeError unless scanner.eos?
|
133
|
+
return obj
|
134
|
+
end
|
135
|
+
|
136
|
+
def load_file(path)
|
137
|
+
load(File.open(path).read)
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse(scanner) # :nodoc:
|
141
|
+
case token = scanner.scan(/[ild]|\d+:|\s/)
|
142
|
+
when nil
|
143
|
+
raise BdecodeError, scanner.pos
|
144
|
+
when "i"
|
145
|
+
number = scanner.scan(/0|(?:-?[1-9][0-9]*)/)
|
146
|
+
unless number and scanner.scan(/e/)
|
147
|
+
raise BdecodeError, scanner.pos
|
148
|
+
end
|
149
|
+
return number.to_i
|
150
|
+
when "l"
|
151
|
+
ary = []
|
152
|
+
until scanner.peek(1) == "e"
|
153
|
+
ary.push(parse(scanner))
|
154
|
+
end
|
155
|
+
scanner.pos += 1
|
156
|
+
return ary
|
157
|
+
when "d"
|
158
|
+
hsh = {}
|
159
|
+
until scanner.peek(1) == "e"
|
160
|
+
key, value = parse(scanner), parse(scanner)
|
161
|
+
unless key.is_a? String
|
162
|
+
raise BdecodeError, "error at #{scanner.pos}: " +
|
163
|
+
"key must be a string"
|
164
|
+
end
|
165
|
+
hsh.store(key, value)
|
166
|
+
end
|
167
|
+
scanner.pos += 1
|
168
|
+
return hsh
|
169
|
+
when /\d+:/
|
170
|
+
length = token.chop.to_i
|
171
|
+
str = scanner.peek(length)
|
172
|
+
scanner.pos += length
|
173
|
+
return str
|
174
|
+
when /\s/
|
175
|
+
nil
|
176
|
+
else
|
177
|
+
raise BdecodeError, scanner.pos
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
private :parse
|
182
|
+
end
|
183
|
+
end
|
data/test/tc_bdecode.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'bencode'
|
4
|
+
|
5
|
+
class BdecodeTest < Test::Unit::TestCase
|
6
|
+
def test_string
|
7
|
+
assert_equal "foo", "3:foo".bdecode
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_integer
|
11
|
+
assert_equal 42, "i42e".bdecode
|
12
|
+
assert_raise BdecodeError do
|
13
|
+
"i01e".bdecode
|
14
|
+
end
|
15
|
+
assert_raise BdecodeError do
|
16
|
+
"i-01e".bdecode
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_array
|
21
|
+
assert_equal [1, 2, 3], "li1ei2ei3ee".bdecode
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_hash
|
25
|
+
hsh = {"a" => "monkey", "h" => "elephant", "z" => "zebra"}
|
26
|
+
assert_equal hsh, "d1:a6:monkey1:h8:elephant1:z5:zebrae".bdecode
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_invalid
|
30
|
+
assert_raises BdecodeError do
|
31
|
+
"foobar".bdecode
|
32
|
+
"i1ei2e".bdecode
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_valid
|
37
|
+
assert "3:foo".bencoded?
|
38
|
+
assert !"3:foo3:bar".bencoded?
|
39
|
+
assert "i42e".bencoded?
|
40
|
+
assert !"i42ei101e".bencoded?
|
41
|
+
end
|
42
|
+
end
|
data/test/tc_bencode.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
require 'unit/test'
|
3
|
+
require '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_integer
|
12
|
+
assert_equal "i42e", 42.bencode
|
13
|
+
assert_equal "i-3e", -3.bencode
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_float
|
17
|
+
assert_raise BencodeError do
|
18
|
+
42.4.bencode
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_array
|
23
|
+
assert_equal "li1ei2ei3ee", [1, 2, 3].bencode
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_hash
|
27
|
+
assert_equal "d1:a3:foo1:g3:bar1:z3:baze",
|
28
|
+
{"a" => "foo", "g" => "bar", "z" => "baz"}.bencode
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: bencode
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2006-09-17 00:00:00 +02:00
|
8
|
+
summary: Bencode and -decode data
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email:
|
12
|
+
homepage:
|
13
|
+
rubyforge_project: bencode
|
14
|
+
description:
|
15
|
+
autorequire: bencode
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Daniel Schierbeck
|
30
|
+
files:
|
31
|
+
- lib/bencode.rb
|
32
|
+
- test/tc_bdecode.rb
|
33
|
+
- test/tc_bencode.rb
|
34
|
+
test_files:
|
35
|
+
- test/tc_bdecode.rb
|
36
|
+
- test/tc_bencode.rb
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
executables: []
|
42
|
+
|
43
|
+
extensions: []
|
44
|
+
|
45
|
+
requirements: []
|
46
|
+
|
47
|
+
dependencies: []
|
48
|
+
|