BEncode

Synopsis

This gem provides a way to encode and parse bencodings used by the Bit Torrent protocol.

Installation

# install the gem
> gem install bencode_blatyo
Successfully installed bencode_blatyo
1 gem installed
Installing ri documentation for bencode_blatyo...
Building YARD (yri) index for bencode_blatyo...
Installing RDoc documentation for bencode_blatyo

# somefile.rb
require 'bencode'

Examples

String

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"

Integer

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"

List

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"

Dictionary

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"

Encoding and Decoding

# encoding is just like calling bencode on the object
BEncode.encode("string")    #=> "6:string"

# decoding takes a string and return either a String, Integer, Array, or Hash
BEncode.decode("6:string")  #=> "string"
BEncode.decode("i1e")       #=> 1
BEncode.decode("le")        #=> []
BEncode.decode("de")        #=> {}

# you can work directly with files too
BEncode.encode_file("my_awesome.torrent", {:announce => "http://www.sometracker.com/announce:80"})
BEncode.decode_file("my_awesome.torrent") #=> {:announce => "http://www.sometracker.com/announce:80"}

Registering Types

When using bencodings it may be useful to translate your own objects into bencoded strings. You can do that with the register methods on each BEncode type. All register does is define a bencode instance method for the class that internally uses type conversion. That means if you want to specify a String type then your class must have a to_s or to_str instance method. The same goes for all the other types.

Keep in mind when registering that if you register a class for two separate types it will only retain the bencode method of the last type registered for.

# register string type
BEncode::String.register Range
(1..2).bencode      #=> "4:1..2"

# register integer type
BEncode::Integer.register NilClass
nil.bencode         #=> "i0e"

# register list type
BEncode::List.register Range
(1..2).bencode      #=> "li1ei2ee"

#register dictionary type
MyClass = Class.new do
  def to_h
    {:a => "a", :b => "b"}
  end
end
BEncode::Dictionary.register MyClass
MyClass.new.bencode #=> "d1:a1:a1:b1:be"

Note on Reporting Issues

  • 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

Note on Patches/Pull Requests

  • 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 © 2010 blatyo. See LICENSE for details.