bcodec 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.
- data/lib/bcodec/decode.rb +95 -0
- data/lib/bcodec/encode.rb +56 -0
- data/lib/bcodec.rb +55 -0
- data/test/test_decoding.rb +37 -0
- data/test/test_encoding.rb +37 -0
- metadata +51 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# bcodec - Ruby library to decode/encode bencoded data
|
|
2
|
+
# Copyright (C) 2006 Thomas <tochiroNO@SPAMrubyforge.org>
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License along
|
|
15
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
16
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
17
|
+
|
|
18
|
+
module BCodec
|
|
19
|
+
|
|
20
|
+
class InvalidInteger < Exception; end
|
|
21
|
+
class InvalidString < Exception; end
|
|
22
|
+
class InvalidKey < Exception; end
|
|
23
|
+
class UnknownData < Exception; end
|
|
24
|
+
class InvalidDataType < Exception; end
|
|
25
|
+
|
|
26
|
+
# Decodes bencoded data from an IO-like object (IO, File, StringIO or similar
|
|
27
|
+
# that has a .getc method).
|
|
28
|
+
#
|
|
29
|
+
# Returns the decoded object which is either of these:
|
|
30
|
+
# * Array
|
|
31
|
+
# * Fixnum (or Bignum)
|
|
32
|
+
# * Hash
|
|
33
|
+
# * String
|
|
34
|
+
def BCodec.decode(io)
|
|
35
|
+
c = io.getc
|
|
36
|
+
|
|
37
|
+
case c.chr
|
|
38
|
+
when 'i'
|
|
39
|
+
i = ''
|
|
40
|
+
while c = io.getc
|
|
41
|
+
if c.chr == 'e'
|
|
42
|
+
if i.match(/^(0|-?[1-9][0-9]*)$/)
|
|
43
|
+
return i.to_i
|
|
44
|
+
else
|
|
45
|
+
raise InvalidInteger
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
i += c.chr
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
raise EOFError
|
|
52
|
+
when '0'..'9'
|
|
53
|
+
n = c.chr
|
|
54
|
+
while b = io.getc
|
|
55
|
+
case b.chr
|
|
56
|
+
when '0'..'9'
|
|
57
|
+
n += b.chr
|
|
58
|
+
when ':'
|
|
59
|
+
strlen = n.to_i
|
|
60
|
+
str = io.read(strlen)
|
|
61
|
+
if str.length == strlen
|
|
62
|
+
return str
|
|
63
|
+
else
|
|
64
|
+
raise EOFError
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
raise InvalidString
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
when 'l'
|
|
71
|
+
list = []
|
|
72
|
+
|
|
73
|
+
while item = decode(io)
|
|
74
|
+
list.push(item)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
return list
|
|
78
|
+
when 'd'
|
|
79
|
+
dict = {}
|
|
80
|
+
|
|
81
|
+
while key = decode(io)
|
|
82
|
+
raise InvalidKey unless key.instance_of?(String)
|
|
83
|
+
val = decode(io)
|
|
84
|
+
dict[key] = val
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
return dict
|
|
88
|
+
when 'e'
|
|
89
|
+
return false
|
|
90
|
+
else
|
|
91
|
+
raise UnknownData
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# BCodec - Ruby module to decode/encode bencoded data
|
|
2
|
+
# Copyright (C) 2006 Thomas <tochiro@freeshell.org>
|
|
3
|
+
#
|
|
4
|
+
# This library is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
6
|
+
# License as published by the Free Software Foundation; either
|
|
7
|
+
# version 2.1 of the License, or (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+
# Lesser General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
|
15
|
+
# License along with this library; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
|
|
18
|
+
module BCodec
|
|
19
|
+
|
|
20
|
+
class InvalidKey < Exception; end
|
|
21
|
+
class InvalidDataType < Exception; end
|
|
22
|
+
|
|
23
|
+
# Bencodes a Ruby object which must be either of these:
|
|
24
|
+
#
|
|
25
|
+
# * Array
|
|
26
|
+
# * Fixnum (or Bignum)
|
|
27
|
+
# * Hash
|
|
28
|
+
# * String
|
|
29
|
+
#
|
|
30
|
+
# Returns the encoded object(s) as a String
|
|
31
|
+
def BCodec.encode(obj)
|
|
32
|
+
str = ""
|
|
33
|
+
if obj.class == String
|
|
34
|
+
str << "%d:%s" % [ obj.length, obj ]
|
|
35
|
+
elsif obj.class == Fixnum or obj.class == Integer or obj.class == Bignum
|
|
36
|
+
str << "i" << obj.to_s << "e"
|
|
37
|
+
elsif obj.class == Array
|
|
38
|
+
str << "l"
|
|
39
|
+
obj.each { |i| str += encode(i) }
|
|
40
|
+
str << "e"
|
|
41
|
+
elsif obj.class == Hash
|
|
42
|
+
str << "d"
|
|
43
|
+
obj.sort.each do |k, v|
|
|
44
|
+
raise InvalidKey unless k.instance_of?(String)
|
|
45
|
+
str << encode(k)
|
|
46
|
+
str << encode(v)
|
|
47
|
+
end
|
|
48
|
+
str << "e"
|
|
49
|
+
else
|
|
50
|
+
raise InvalidDataType
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
return str
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
data/lib/bcodec.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# bcodec - Ruby library to decode/encode bencoded data
|
|
2
|
+
# Copyright (C) 2006 Thomas <tochiroNO@SPAMrubyforge.org>
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License along
|
|
15
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
16
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
17
|
+
|
|
18
|
+
#=bcodec
|
|
19
|
+
#
|
|
20
|
+
#==Description
|
|
21
|
+
#bcodec is a Ruby library for encoding and decoding bencoded data used in the
|
|
22
|
+
#BitTorrent protocol (see http://www.bittorrent.org/).
|
|
23
|
+
#
|
|
24
|
+
#
|
|
25
|
+
#==Encoding example:
|
|
26
|
+
#
|
|
27
|
+
# require 'bcodec'
|
|
28
|
+
# BCodec.encode("abcd") # returns "4:abcd"
|
|
29
|
+
# BCodec.encode(1000) # returns "i1000e"
|
|
30
|
+
# BCodec.encode([1,2,3]) # returns "li1ei2ei3ee"
|
|
31
|
+
# BCodec.encode({"key" => "value"}) # returns "d3:key5:valuee"
|
|
32
|
+
#
|
|
33
|
+
#
|
|
34
|
+
#==Decoding example:
|
|
35
|
+
#
|
|
36
|
+
# require 'bcodec'
|
|
37
|
+
# BCodec.decode(StringIO.new("4:abcd")) # returns "abcd"
|
|
38
|
+
# BCodec.decode(StringIO.new("i666e")) # returns 666
|
|
39
|
+
# BCodec.decode(StringIO.new("l3abci666ee")) # returns [ "abc", 666 ]
|
|
40
|
+
# BCodec.decode(StringIO.new("d3abci666ee")) # returns { "abc" => 666 }
|
|
41
|
+
#
|
|
42
|
+
#To decode a BitTorrent metafile you could do something like:
|
|
43
|
+
#
|
|
44
|
+
# hash = BCodec.decode(open("some.torrent", "r"))
|
|
45
|
+
#
|
|
46
|
+
#The only legal objects are String, Fixnum (or Bignum), Hash and Array which
|
|
47
|
+
#corresponds to String, Integer, Dictionary and List in Python as
|
|
48
|
+
#specified in the BitTorrent protocol specification at:
|
|
49
|
+
#http://www.bittorrent.org/protocol.html
|
|
50
|
+
|
|
51
|
+
module BCodec
|
|
52
|
+
require 'bcodec/decode'
|
|
53
|
+
require 'bcodec/encode'
|
|
54
|
+
end
|
|
55
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
require 'lib/bcodec/decode'
|
|
4
|
+
|
|
5
|
+
class TestDecoding < Test::Unit::TestCase
|
|
6
|
+
|
|
7
|
+
def setup
|
|
8
|
+
@tests = []
|
|
9
|
+
@tests << [ "4:test", "test" ]
|
|
10
|
+
@tests << [ "i666e", 666 ]
|
|
11
|
+
@tests << [ "l4:testi666ee", [ "test", 666 ] ]
|
|
12
|
+
@tests << [ "d4:testi666ee", { "test" => 666 } ]
|
|
13
|
+
@tests << [ "d4:testli1ei2ei3ed1:Ai1e1:Bl1:a1:b1:ceeee",
|
|
14
|
+
{ "test" => [ 1, 2, 3, { "A" => 1, "B" => [ 'a', 'b', 'c' ] } ] } ]
|
|
15
|
+
|
|
16
|
+
@etests = []
|
|
17
|
+
@etests << [ BCodec::InvalidInteger, "i1.0e" ]
|
|
18
|
+
@etests << [ BCodec::InvalidInteger, "iAe" ]
|
|
19
|
+
@etests << [ BCodec::UnknownData, "f" ]
|
|
20
|
+
@etests << [ BCodec::InvalidKey, "di666e:3:keye" ]
|
|
21
|
+
@etests << [ EOFError, "i1" ]
|
|
22
|
+
@etests << [ EOFError, "4:ab" ]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_decoding
|
|
26
|
+
@tests.each do |test, result|
|
|
27
|
+
assert_equal BCodec.decode(StringIO.new(test)), result
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_decoding_exceptions
|
|
32
|
+
@etests.each do |exception, test|
|
|
33
|
+
assert_raise(exception) { BCodec.decode(StringIO.new(test)) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
require 'lib/bcodec/encode'
|
|
4
|
+
|
|
5
|
+
include BCodec
|
|
6
|
+
|
|
7
|
+
class TestEncoding < Test::Unit::TestCase
|
|
8
|
+
|
|
9
|
+
def setup
|
|
10
|
+
@tests = []
|
|
11
|
+
@tests << [ "test", "4:test" ]
|
|
12
|
+
@tests << [ 666, "i666e" ]
|
|
13
|
+
@tests << [ [ "test", 666 ], "l4:testi666ee" ]
|
|
14
|
+
@tests << [ { "test" => 666 }, "d4:testi666ee" ]
|
|
15
|
+
@tests << [ { "test" => [ 1, 2, 3, { "A" => 1, "B" => [ 'a', 'b', 'c' ] } ] },
|
|
16
|
+
"d4:testli1ei2ei3ed1:Ai1e1:Bl1:a1:b1:ceeee" ]
|
|
17
|
+
|
|
18
|
+
@etests = []
|
|
19
|
+
@etests << [ BCodec::InvalidDataType, :symbol ]
|
|
20
|
+
@etests << [ BCodec::InvalidDataType, [1.0, "float"] ]
|
|
21
|
+
@etests << [ BCodec::InvalidKey, {1 => "key"} ]
|
|
22
|
+
@etests << [ BCodec::InvalidDataType, 1..666 ]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_basic_encoding
|
|
26
|
+
@tests.each do |test, result|
|
|
27
|
+
assert_equal(BCodec.encode(test), result)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_encoding_exceptions
|
|
32
|
+
@etests.each do |exception, test|
|
|
33
|
+
assert_raise(exception) { BCodec.encode(test) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
rubygems_version: 0.8.11
|
|
3
|
+
specification_version: 1
|
|
4
|
+
name: bcodec
|
|
5
|
+
version: !ruby/object:Gem::Version
|
|
6
|
+
version: 0.1.0
|
|
7
|
+
date: 2006-11-18 00:00:00 +01:00
|
|
8
|
+
summary: Ruby library for encoding and decoding bencoded data used in the BitTorrent protocol
|
|
9
|
+
require_paths:
|
|
10
|
+
- lib
|
|
11
|
+
email:
|
|
12
|
+
homepage: http://bcodec.rubyforge.org/
|
|
13
|
+
rubyforge_project:
|
|
14
|
+
description:
|
|
15
|
+
autorequire: bcodec
|
|
16
|
+
default_executable:
|
|
17
|
+
bindir: bin
|
|
18
|
+
has_rdoc: false
|
|
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
|
+
|
|
30
|
+
files:
|
|
31
|
+
- lib/bcodec
|
|
32
|
+
- lib/bcodec.rb
|
|
33
|
+
- lib/bcodec/encode.rb
|
|
34
|
+
- lib/bcodec/decode.rb
|
|
35
|
+
- test/test_encoding.rb
|
|
36
|
+
- test/test_decoding.rb
|
|
37
|
+
test_files:
|
|
38
|
+
- test/test_decoding.rb
|
|
39
|
+
- test/test_encoding.rb
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
|
|
42
|
+
extra_rdoc_files: []
|
|
43
|
+
|
|
44
|
+
executables: []
|
|
45
|
+
|
|
46
|
+
extensions: []
|
|
47
|
+
|
|
48
|
+
requirements: []
|
|
49
|
+
|
|
50
|
+
dependencies: []
|
|
51
|
+
|