Sha256: 4e12f5687e5ca8e9b97a7fa1db68f21786a2308663cb56123fdf07415116e060

Contents?: true

Size: 1.64 KB

Versions: 2

Compression:

Stored size: 1.64 KB

Contents

require 'stringio'

module BEncode
  class Parser
    attr_reader :stream

    def initialize(stream)
      @stream =
        if stream.kind_of?(IO) || stream.kind_of?(StringIO)
          stream
        elsif stream.respond_to? :string
          StringIO.new stream.string
        elsif stream.respond_to? :to_s
          StringIO.new stream.to_s
        end
    end

    def parse!
      case peek
        when ?i then parse_integer!
        when ?l then parse_list!
        when ?d then parse_dict!
        when ?0 .. ?9 then parse_string!
      end
    end
    
    def eos?
      stream.eof?
    end

    private

    def parse_integer!
      stream.getc
      num = stream.gets("e") or raise BEncode::DecodeError
      num.chop.to_i
    end

    def parse_list!
      stream.getc
      ary = []
      ary.push(parse!) until peek == ?e
      stream.getc
      ary
    end

    def parse_dict!
      stream.getc
      hsh = {}
      until peek == ?e
        key = parse!

        unless key.is_a? String or key.is_a? Fixnum
          raise BEncode::DecodeError, "key must be a string or number"
        end
          
        val = parse!

        hsh.store(key.to_s, val)
      end
      stream.getc
      hsh
    end

    def parse_string!
      num = stream.gets(":") or
        raise BEncode::DecodeError, "invalid string length (no colon)"

      begin
        length = num.chop.to_i
        return "" if length == 0 # Workaround for Rubinius bug
        str = stream.read(length)
      rescue
        raise BEncode::DecodeError, "invalid string length"
      end

      str
    end
    
    def peek
      c = stream.getc
      stream.ungetc(c)
      c
    end
  end
end

Version data entries

2 entries across 2 versions & 1 rubygems

Version Path
bencode-0.8.2 lib/bencode/parser.rb
bencode-0.8.1 lib/bencode/parser.rb