bencoder 0.0.4 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +7 -1
- data/lib/bencoder.rb +15 -11
- data/test/test_bencoder.rb +40 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5112fd1ec7dec234a855cf2a997e3ffe733f0ecb
|
4
|
+
data.tar.gz: 11393fc29fec7354b685cddea158df8f878e0a46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0dff892b6d46411990a480a3a4d91db34e8ebe099d52317d7ffa94d38a043013ac78eedf7d0ee9d5c5010a26c1f56893905e330045e8730e7db059000f9b616
|
7
|
+
data.tar.gz: 42f0060c74935ab46d58419d3f9f6f50eb50b4d20b62765f4d538784679fe3f8099eb522308586b9edeb0c4d6263e4c17ecc08314edd30e7ffe0a011f80938ee
|
data/README.md
CHANGED
@@ -36,4 +36,10 @@ BEncoder.encode ['what', 'strange', { data: 'I', have: 'here' }, 666]
|
|
36
36
|
BEncoder.decode 'l4:what7:stranged4:data1:I4:have4:hereei666ee'
|
37
37
|
=> ['what', 'strange', { 'data' => 'I', 'have' => 'here' }, 666]
|
38
38
|
```
|
39
|
-
|
39
|
+
|
40
|
+
Since .torrent files are bencode dicts, you can parse them out of the box
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
BEncoder.decode File.read('sample.torrent')
|
44
|
+
=> {"announce"=>"udp://tracker.openbittorrent.com:80", "creation date"=>1327049827, "info"=>{"length"=>20, "name"=>"sample.txt", "piece length"=>65536, "pieces"=>"\\\xC5\xE6R\xBE\r\xE6\xF2x\x05\xB3\x04d\xFF\x9B\x00\xF4\x89\xF0\xC9", "private"=>1}}
|
45
|
+
```
|
data/lib/bencoder.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
require 'stringio'
|
2
|
+
|
3
|
+
class InvalidEncodingError < StandardError; end
|
4
|
+
class UnencodableTypeError < StandardError; end
|
5
|
+
|
2
6
|
class BEncoder
|
3
7
|
class << self
|
4
8
|
def encode(object)
|
@@ -13,6 +17,8 @@ class BEncoder
|
|
13
17
|
encode_array object
|
14
18
|
when Hash
|
15
19
|
encode_hash object
|
20
|
+
else
|
21
|
+
raise UnencodableTypeError, "Cannot encode instance of type #{object.class}"
|
16
22
|
end
|
17
23
|
end
|
18
24
|
|
@@ -24,8 +30,6 @@ class BEncoder
|
|
24
30
|
|
25
31
|
private
|
26
32
|
|
27
|
-
class InvalidEncodingError < StandardError; end
|
28
|
-
|
29
33
|
def parse(string)
|
30
34
|
case string[0]
|
31
35
|
when 'i'
|
@@ -46,7 +50,7 @@ class BEncoder
|
|
46
50
|
elsif string[0] == 'l' && string[-1] == 'e'
|
47
51
|
str = StringIO.new string[1..-2]
|
48
52
|
else
|
49
|
-
raise InvalidEncodingError
|
53
|
+
raise InvalidEncodingError, 'List does not have a closing e'
|
50
54
|
end
|
51
55
|
parse_io_list str
|
52
56
|
end
|
@@ -56,9 +60,9 @@ class BEncoder
|
|
56
60
|
string.getc if peek(string) == 'd'
|
57
61
|
list_of_keys_and_values = parse_list(string)
|
58
62
|
elsif string[0] == 'd' && string[-1] == 'e'
|
59
|
-
list_of_keys_and_values = parse_list("l#{string[1..-2]}e")
|
63
|
+
list_of_keys_and_values = parse_list("l#{ string[1..-2] }e")
|
60
64
|
else
|
61
|
-
raise InvalidEncodingError
|
65
|
+
raise InvalidEncodingError, 'Dict does not have a closing e'
|
62
66
|
end
|
63
67
|
make_hash_from_array list_of_keys_and_values
|
64
68
|
end
|
@@ -77,7 +81,7 @@ class BEncoder
|
|
77
81
|
length = io.gets(sep=':').to_i
|
78
82
|
list << io.gets(length)
|
79
83
|
else
|
80
|
-
raise InvalidEncodingError
|
84
|
+
raise InvalidEncodingError, "Encountered unexpected identifier #{ peek io }"
|
81
85
|
end
|
82
86
|
end
|
83
87
|
io.getc
|
@@ -96,7 +100,7 @@ class BEncoder
|
|
96
100
|
if string[0] == 'i' && string[-1] == 'e'
|
97
101
|
string[1..-2].to_i
|
98
102
|
else
|
99
|
-
raise InvalidEncodingError
|
103
|
+
raise InvalidEncodingError, 'Integer does not have closing e'
|
100
104
|
end
|
101
105
|
end
|
102
106
|
|
@@ -105,16 +109,16 @@ class BEncoder
|
|
105
109
|
if content.length == length.to_i
|
106
110
|
content
|
107
111
|
else
|
108
|
-
raise InvalidEncodingError
|
112
|
+
raise InvalidEncodingError, "String length declared as #{length.to_i}, but was #{content.length} "
|
109
113
|
end
|
110
114
|
end
|
111
115
|
|
112
116
|
def encode_string(string)
|
113
|
-
"#{string.length}:#{string}"
|
117
|
+
"#{ string.length }:#{ string }"
|
114
118
|
end
|
115
119
|
|
116
120
|
def encode_int(int)
|
117
|
-
"i#{int}e"
|
121
|
+
"i#{ int }e"
|
118
122
|
end
|
119
123
|
|
120
124
|
def encode_array(array)
|
@@ -122,7 +126,7 @@ class BEncoder
|
|
122
126
|
end
|
123
127
|
|
124
128
|
def encode_hash(hash)
|
125
|
-
hash.inject("d") { |result, (k,v)| result += "#{encode(k.to_s)}#{encode(v)}" } + 'e'
|
129
|
+
hash.inject("d") { |result, (k,v)| result += "#{ encode(k.to_s) }#{ encode(v) }" } + 'e'
|
126
130
|
end
|
127
131
|
|
128
132
|
def peek(io)
|
data/test/test_bencoder.rb
CHANGED
@@ -13,6 +13,10 @@ class TestBencoder < Minitest::Test
|
|
13
13
|
|
14
14
|
#encoding
|
15
15
|
|
16
|
+
def test_symbol_encoding
|
17
|
+
assert_equal '6:hellou', @be.encode(:hellou)
|
18
|
+
end
|
19
|
+
|
16
20
|
def test_string_encoding
|
17
21
|
assert_equal '7:Someday', @be.encode('Someday')
|
18
22
|
end
|
@@ -33,6 +37,12 @@ class TestBencoder < Minitest::Test
|
|
33
37
|
assert_equal 'ld4:somel5:times3:youe5:gotta3:let2:go1:!ei2e5:wooooli1e1:21:3ee', @be.encode([{'some' => ['times', 'you'], gotta: 'let', go: '!'}, 2, "woooo", [1, '2', "3"]])
|
34
38
|
end
|
35
39
|
|
40
|
+
def test_should_not_encode_strange_types
|
41
|
+
assert_raises UnencodableTypeError do
|
42
|
+
@be.encode 5.6
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
36
46
|
#decoding
|
37
47
|
|
38
48
|
def test_string_decoding
|
@@ -54,4 +64,34 @@ class TestBencoder < Minitest::Test
|
|
54
64
|
def test_nested_decoding
|
55
65
|
assert_equal [{'some' => ['times', 'you'], 'gotta' => 'let', 'go' => '!'}, 2, "woooo", [1, '2', "3"]], @be.decode('ld4:somel5:times3:youe5:gotta3:let2:go1:!ei2e5:wooooli1e1:21:3ee')
|
56
66
|
end
|
67
|
+
|
68
|
+
def test_throws_error_on_wrong_list_encoding
|
69
|
+
assert_raises InvalidEncodingError do
|
70
|
+
@be.decode 'li4ed'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_throws_error_on_wrong_dict_encoding
|
75
|
+
assert_raises InvalidEncodingError do
|
76
|
+
@be.decode 'd3:abc'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_throws_error_on_wrong_int_encoding
|
81
|
+
assert_raises InvalidEncodingError do
|
82
|
+
@be.decode 'i14'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_throws_error_on_wrong_string_encoding
|
87
|
+
assert_raises InvalidEncodingError do
|
88
|
+
@be.decode '4:cat'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_throws_error_on_unknown_encoding_identifier
|
93
|
+
assert_raises InvalidEncodingError do
|
94
|
+
@be.decode 'li3ej5e'
|
95
|
+
end
|
96
|
+
end
|
57
97
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bencoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kasper Holbek Jensen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -72,7 +72,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 1.9.3
|
76
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
77
|
requirements:
|
78
78
|
- - ">="
|
@@ -80,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
80
|
version: '0'
|
81
81
|
requirements: []
|
82
82
|
rubyforge_project:
|
83
|
-
rubygems_version: 2.
|
83
|
+
rubygems_version: 2.4.2
|
84
84
|
signing_key:
|
85
85
|
specification_version: 4
|
86
86
|
summary: Bittorrent encoding
|