- Author Allen Madsen (blatyo)
- My Site http://www.allenmadsen.com
- Gem http://gemcutter.org/gems/bencodr
- Source http://github.com/blatyo/bencodr
- Issue Tracker http://github.com/blatyo/bencodr/issues
- Build Status
This gem provides a way to encode and parse bencodings used by the Bit Torrent protocol.
Install the gem:
gem install bencodr
Require it in your ruby files:
require 'bencodr'
Most of the functionality of this library can be accessed directly on the BEncodr class.
# encoding is just like calling bencode on the object
BEncodr.bencode("string") #=> "6:string"
# decoding is just like calling bdecode on a bencoding
BEncodr.bdecode("6:string") #=> "string"
# you can work directly with files too
BEncodr.bencode_file("my_awesome.torrent", {:announce => "http://www.sometracker.com/announce:80"})
BEncodr.bdecode_file("my_awesome.torrent") #=> {:announce => "http://www.sometracker.com/announce:80"}
In order to get this functionality on the objects described below, you can call:
BEncodr.include!
This will extend:
- BEncodr::String
- String
- Symbol
- URI::Generic
- URI::FTP
- URI::HTTP
- URI::HTTPS
- URI::LDAP
- URI::LDAPS
- BEncodr::Integer
- Numeric
- Time
- BEncodr::List
- Array
- BEncodr::Dictionary
- Hash
- BEncodr::IO
- IO
- File
BEncoded strings are length-prefixed base ten followed by a colon and the string.
# strings
"".bencode #=> "0:"
"string".bencode #=> "6:string"
# symbols
:symbol.bencode #=> "6:symbol"
# URIs
uri = URI.parse("http://github.com/blatyo/bencode")
uri.bencode #=> "32:http://github.com/blatyo/bencode"
Bencoded integers are represented by an 'i' followed by the number in base 10 followed by an 'e'.
# integers
1.bencode #=> "i1e"
-1.bencode #=> "i-1e"
10_000_000_000.bencode #=> "i10000000000e"
# other numerics
1.1.bencode #=> "i1e"
-1e10.bencode #=> "i-10000000000e"
# times
Time.at(4).bencode #=> "i4e"
Bencoded lists are encoded as an 'l' followed by their elements (also bencoded) followed by an 'e'.
# arrays
[].bencode #=> "le"
[:e, "a", 1, Time.at(11)].bencode #=> "l1:e1:ai1ei11ee"
Bencoded dictionaries are encoded as a 'd' followed by a list of alternating keys and their corresponding values followed by an 'e'. Keys appear in sorted order (sorted as raw strings, not alphanumerics) and are always strings.
# hashes
{}.bencode #=> "de"
{"string" => "string"}.bencode #=> "d6:string6:stringe"
{:symbol => :symbol}.bencode #=> "d6:symbol6:symbole"
{1 => 1}.bencode #=> "d1:1i1ee"
{1.1 => 1.1}.bencode #=> "d3:1.1i1ee"
{{} => {}}.bencode #=> "d2:{}dee"
time = Time.utc(0)
{time => time}.bencode #=> "d23:2000-01-01 00:00:00 UTCi946684800ee"
array = (1..4).to_a
{array => array}.bencode #=> "d12:[1, 2, 3, 4]li1ei2ei3ei4eee"
# Note: keys are sorted as raw strings.
{:a => 1, "A" => 1, 1=> 1}.bencode #=> "d1:1i1e1:Ai1e1:ai1ee"
You can decode a bencoding by calling bdecode on the string.
"6:string".bdecode #=> "string"
"i1e".bdecode #=> 1
"le".bdecode #=> []
"de".bdecode #=> {}
You can also write and read bencodings.
# write to standard out
IO.bencode(1, "string") #=> "6:string" to stdout
$stdout.bencode("string") #=> "6:string" to stdout
# write to file
File.bencode("a.bencode", "string") #=> "6:string" to a.bencode
file = File.open("a.bencode", "wb")
file.bencode("string") #=> "6:string" to a.bencode
# read from standard in
IO.bdecode(0) #=> "string"
$stdin.bdecode #=> "string"
# read from file
File.bdecode("a.bencode") #=> "string"
file = File.open("a.bencode", "rb")
file.bdecode #=> "string"
When using bencodings it may be useful to translate your own objects into bencoded strings.
# register string type
Range.send :include, BEncodr::String
(1..2).bencode #=> "4:1..2"
# register integer type
NilClass.send :include, BEncodr::Integer
nil.bencode #=> "i0e"
# register list type
Range.send :include, BEncodr::List
(1..2).bencode #=> "li1ei2ee"
#register dictionary type
MyClass = Class.new do
include BEncodr::Dictionary
def to_h
{:a => "a", :b => "b"}
end
end
MyClass.new.bencode #=> "d1:a1:a1:b1:be"
- Andrew Nikolaev - Fixed encoding issue
- Tarangp - Fixed parsing of long bencoded strings
- Try to make a failing test case
- Tell me which version of ruby you're using
- Tell me which OS you are using
- Provide me with any extra files if necessary
- Fork the project.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don't break it in a future version unintentionally.
- Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
- Send me a pull request. Bonus points for topic branches.
Copyright (c) 2010 Allen Madsen. See LICENSE for details.