public
Last active

Bencoding implementation

  • Download Gist
gistfile1.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
require 'stringio'
 
module EM::Bittorrent
module Benc
def self.decode(input)
Stream.new(input).decode
end
 
class Stream
def initialize(input)
if input.is_a?(String)
@io = ::StringIO.new(input)
elsif input.is_a?(StringIO)
@io = input
elsif input.is_a?(File)
@io = input
else
raise ArgumentError.new "You must supply either a String or an IO object as input"
end
end
 
def decode
begin
@c = @io.getc
if @c == ?i
decode_int
elsif @c == ?l
decode_array
elsif @c == ?d
decode_hash
elsif @c =~ /[0-9]/
decode_string
else # 'e' - end of element
nil
end
rescue TypeError
puts "Died at character #{@c}, position #{@io.pos}"
raise
end
end
 
private
 
def decode_string
::String.new.tap do |str|
len = ::String.new
len << @c
until ((@c = @io.getc) == ?:)
len << @c
end
str << @io.read(len.to_i)
end
end
 
def decode_int
::String.new.tap do |str|
until ((@c = @io.getc) == ?e)
str << @c
end
end.to_i
end
 
def decode_array
::Array.new.tap do |array|
while (value = decode)
array << value
end
end
end
 
def decode_hash
::Hash.new.tap do |hash|
while (key = decode)
value = decode
hash[key] = value
end
end
end
end
 
module Symbol
def to_benc
self.to_s.to_benc
end
end
 
module String
def to_benc
self.class.new.tap do |s|
s << self.length.to_s
s << ?:
s << self
end
end
end
 
module Integer
def to_benc
::String.new.tap do |s|
s << ?i
s << self.to_s
s << ?e
end
end
end
 
module Array
def to_benc
::String.new.tap do |s|
s << ?l
self.each do |item|
s << item.to_benc
end
s << ?e
end
end
end
 
module Hash
def to_benc
::String.new.tap do |s|
s << ?d
self.each do |k, v|
s << k.to_benc
s << v.to_benc
end
s << ?e
end
end
end
end
end
 
Symbol.send(:include, EM::Bittorrent::Benc::Symbol)
String.send(:include, EM::Bittorrent::Benc::String)
Integer.send(:include, EM::Bittorrent::Benc::Integer)
Array.send(:include, EM::Bittorrent::Benc::Array)
Hash.send(:include, EM::Bittorrent::Benc::Hash)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.