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.
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
+