lib/bencode.rb in bencode-0.1.0 vs lib/bencode.rb in bencode-0.3.0

- old
+ new

@@ -1,17 +1,55 @@ -# Thanks to Daniel Martin and all the other lads at ruby-lang for -# helping me out. - -# bencode is a Ruby implementation of the Bencode data serialization +# = bencode.rb - Bencode Library +# +# Bencode is a Ruby implementation of the Bencode data serialization # format used in the BitTorrent protocol. # # == Synopsis # # "foobar".bencode #=> "6:foobar" # 42.bencode #=> "i42e" # [1, 2, 3].bencode #=> "li1ei2ei3ee" # +# Bencoding (pronounced _bee-encode_) is a simple protocol, consiting of +# only 4 value types. +# +# === Integers === +# +# An integer is encoded an _i_ followed by the numeral itself, followed +# by an _e_. Leading zeros are not allowed. Negative values are prefixed +# with a minus sign. +# +# 42.bencode #=> "i42e" +# -2.bencode #=> "i-2e" +# 0.bencode #=> "i0e" +# +# === Strings === +# +# Strings are a sequence of zero or more bytes. It is encoded as +# _<length>:<contents>_, where _length_ is the lenth of _contents_. _length_ +# must be non-negative. +# +# "".bencode #=> "0:" +# "foo".bencode #=> "3:foo" +# +# === Lists === +# +# Lists are encoded as _l_ followed by the elements, followed by _e_. +# There is no element seperator. +# +# [1, 2, 3].bencode #=> "li1ei2ei3ee" +# +# === Dictionaries === +# +# Dictionaries are encoded as _d<contents>e_, where _contents_ is a sequence +# of keys and values. Each value is immediately preceded by a key. Keys must +# be strings, and will appear in lexicographical order. +# +# {"foo" => 3, "bar" => 1, "baz" => 2}.bencode +# #=> "d3:bari1e3:bazi2e3:fooi3ee" +# +# # == Authors # # * Daniel Schierbeck # # == Contributors @@ -27,10 +65,11 @@ # version. # # Bencode is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# class Object # Raises an exception. Subclasses of Object must themselves # define meaningful #bencode methods. def bencode @@ -131,54 +170,41 @@ def to_s # :nodoc: "could not bencode #{@object_class}" end end -class BdecodeError < StandardError - attr_reader :pos +class BdecodeError < StandardError; end - def initialize(pos = nil) # :nodoc: - @pos = pos - end - - def to_s # :nodoc: - if pos.nil? - "syntax error" - else - "syntax error near position #{pos}" - end - end -end - module Bencode class << self + # Bencodes +obj+ def dump(obj) obj.bencode end + # Bdecodes +str+ def load(str) require 'strscan' scanner = StringScanner.new(str) obj = parse(scanner) raise BdecodeError unless scanner.eos? return obj end + # Bdecodes the file located at +path+ def load_file(path) load(File.open(path).read) end def parse(scanner) # :nodoc: case token = scanner.scan(/[ild]|\d+:|\s/) when nil - raise BdecodeError, scanner.pos + raise BdecodeError when "i" number = scanner.scan(/0|(?:-?[1-9][0-9]*)/) - unless number and scanner.scan(/e/) - raise BdecodeError, scanner.pos - end + raise BdecodeError unless number and scanner.scan(/e/) return number.to_i when "l" ary = [] until scanner.peek(1) == "e" ary.push(parse(scanner)) @@ -187,14 +213,11 @@ return ary when "d" hsh = {} until scanner.peek(1) == "e" key, value = parse(scanner), parse(scanner) - unless key.is_a? String - raise BdecodeError, "error at #{scanner.pos}: " + - "key must be a string" - end + raise BdecodeError, "key must be a string" unless key.is_a? String hsh.store(key, value) end scanner.pos += 1 return hsh when /\d+:/ @@ -203,10 +226,10 @@ scanner.pos += length return str when /\s/ nil else - raise BdecodeError, scanner.pos + raise BdecodeError end end private :parse end