bencode 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/lib/bencode.rb +183 -0
  2. data/test/tc_bdecode.rb +42 -0
  3. data/test/tc_bencode.rb +30 -0
  4. metadata +48 -0
@@ -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
@@ -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
@@ -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
+