lib/bencode.rb in bencode-0.4.0 vs lib/bencode.rb in bencode-0.5.0
- old
+ new
@@ -3,12 +3,12 @@
# Bencode is a Ruby implementation of the Bencode data serialization
# format used in the BitTorrent protocol.
#
# == Synopsis
#
-# Bencoding (pronounced <i>bee-encode</i>) is a simple protocol, consisting of
-# only 4 value types.
+# Bencoding (pronounced <i>bee-encode</i>) is a simple protocol,
+# consisting of only 4 value types.
#
# === Integers
#
# An integer is encoded as an _i_ followed by the numeral itself, followed
# by an _e_. Leading zeros are not allowed. Negative values are prefixed
@@ -19,12 +19,12 @@
# 0.bencode #=> "i0e"
#
# === Strings
#
# Strings are sequences of zero or more bytes. They are encoded as
-# <i><length>:<contents></i>, where _length_ is the length of _contents_. _length_
-# must be non-negative.
+# <i><length>:<contents></i>, where _length_ is the
+# length of _contents_. _length_ must be non-negative.
#
# "".bencode #=> "0:"
# "foo".bencode #=> "3:foo"
#
# === Lists
@@ -34,13 +34,13 @@
#
# [1, 2, 3].bencode #=> "li1ei2ei3ee"
#
# === Dictionaries
#
-# Dictionaries are encoded as _d_ followed by a sequence of key-value pairs, followed by _e_.
-# Each value must be immediately preceded by a key. Keys must be strings, and must appear in
-# lexicographical order.
+# Dictionaries are encoded as _d_ followed by a sequence of key-value
+# pairs, followed by _e_. Each value must be immediately preceded by
+# a key. Keys must be strings, and must appear in lexicographical order.
#
# {"foo" => 3, "bar" => 1, "baz" => 2}.bencode
# #=> "d3:bari1e3:bazi2e3:fooi3ee"
#
#
@@ -60,181 +60,19 @@
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# 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.
-#
+# 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
- raise BencodeError, "object cannot be bencoded"
- end
-end
-class Integer
- #
- # Bencodes the Integer object. Bencoded integers are represented
- # as +ixe+, where +x+ is the integer with an optional
- # hyphen prepended, indicating negativity.
- #
- # 42.bencode #=> "i42e"
- # -7.bencode #=> "i-7e"
- def bencode
- "i#{self}e"
- end
-end
+prefix = File.dirname(__FILE__)
-class String
- #
- # Bencodes the String object. Bencoded strings are represented
- # as <code>x</code>:<code>y</code>, where +y+ is the string and +x+ is the length of the
- # string.
- #
- # "foo".bencode #=> "3:foo"
- # "".bencode #=> "0:"
- #
- def bencode
- "#{length}:#{self}"
- end
-
- #
- # Bdecodes the String object and returns the data serialized
- # through bencoding.
- #
- # "li1ei2ei3ee".bdecode #=> [1, 2, 3]
- #
- def bdecode
- Bencode.load(self)
- end
-
- #
- # Tests whether the String object is a valid bencoded string.
- def bencoded?
- bdecode
- rescue BdecodeError
- false
- else
- true
- end
+%w[object integer string array hash].each do |type|
+ require prefix + '/bencode/encode/' + type
end
-class Array
- #
- # Bencodes the Array object. Bencoded arrays are represented as
- # +lxe+, where +x+ is zero or more bencoded objects.
- #
- # [1, "foo"].bencode #=> "li1e3:fooe"
- #
- def bencode
- "l#{map{|obj| obj.bencode }.join('')}e"
- rescue BencodeError
- raise BencodeError, "list items must be bencodable"
- end
-end
-
-class Hash
- #
- # Bencodes the Hash object. Bencoded hashes are represented as
- # +dxe+, where +x+ is zero or a power of two bencoded objects.
- # each key is immediately followed by its associated value.
- # All keys must be strings. The keys of the bencoded hash will
- # be in lexicographical order.
- def bencode
- pairs = sort.map{|key, val| [key.to_str.bencode, val.bencode] }
- "d#{pairs.join('')}e"
- rescue NoMethodError => error
- if error.name == :to_str
- raise BencodeError, "dictionary keys must be strings"
- else
- raise
- end
- end
-end
-
-class IO
- def self.bdecode(filename)
- open(filename, 'r').bdecode
- end
-
- def self.bencode(filename)
- open(filename, 'r').bencode
- end
-
- def bdecode
- read.chomp.bdecode
- end
-
- def bencode
- read.chomp.bencode
- end
-end
-
-class BencodeError < StandardError; end
-
-class BdecodeError < StandardError; 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
- when "i"
- number = scanner.scan(/0|(?:-?[1-9][0-9]*)/)
- raise BdecodeError unless number and scanner.scan(/e/)
- return number.to_i
- when "l"
- ary = []
- until scanner.peek(1) == "e"
- ary.push(parse(scanner))
- end
- scanner.pos += 1
- return ary
- when "d"
- hsh = {}
- until scanner.peek(1) == "e"
- key, value = parse(scanner), parse(scanner)
- raise BdecodeError, "key must be a string" unless key.is_a? String
- hsh.store(key, value)
- end
- scanner.pos += 1
- return hsh
- when /\d+:/
- length = token.chop.to_i
- str = scanner.peek(length)
- scanner.pos += length
- return str
- when /\s/
- nil
- else
- raise BdecodeError
- end
- end
-
- private :parse
- end
+%w[decode io encode_error decode_error].each do |file|
+ require prefix + '/bencode/' + file
end