Module: BEncodr::Parser

Defined in:
lib/bencodr/parser.rb

Class Method Summary collapse

Class Method Details

.parse_dictionary(scanner) ⇒ ::Hash

This method parases a bencoded dictionary.

scanner = StringScanner.new("de")
BEncodr::Parser.parse_dictionary(scanner) #=> {}

Parameters:

  • scanner (StringScanner)

    the scanner of a bencoded dictionary

Returns:

  • (::Hash)

    the parsed hash



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/bencodr/parser.rb', line 93

def parse_dictionary(scanner)
  dictionary = {}

  scanner.scan(/d/) or raise BEncodeError, "Invalid dictionary: missing opening d. #{scanner.pos}"
  while true
    key_value = parse_key_value(scanner)
    break unless key_value
    dictionary.store(*key_value)
  end
  scanner.scan(/e/) or raise BEncodeError, "Invalid dictionary: missing closing e. #{scanner.pos}"

  dictionary
end

.parse_integer(scanner) ⇒ ::Integer

This method parases a bencoded integer.

scanner = StringScanner.new("i1e")
BEncodr::Parser.parse_integer(scanner) #=> 1

Parameters:

  • scanner (StringScanner)

    the scanner of a bencoded integer

Returns:

  • (::Integer)

    the parsed integer



58
59
60
61
62
63
# File 'lib/bencodr/parser.rb', line 58

def parse_integer(scanner)
  scanner.scan(/i/)                         or raise BEncodeError, "Invalid integer: missing opening i. #{scanner.pos}"
  integer = scanner.scan(/-?[1-9][0-9]*|0/) or raise BEncodeError, "Invalid integer: valid integer not found. #{scanner.pos}"
  scanner.scan(/e/)                         or raise BEncodeError, "Invalid integer: missing closing e. #{scanner.pos}"
  integer.to_i
end

.parse_key_value(scanner) ⇒ Object

:nodoc:

Raises:



108
109
110
111
112
113
114
115
116
117
# File 'lib/bencodr/parser.rb', line 108

def parse_key_value(scanner) # :nodoc:
  key = parse_object(scanner)
  return key unless key
  raise BEncodeError, "Invalid dictionary: key is not a string. #{scanner.pos}" unless key.is_a?(::String)

  value = parse_object(scanner)
  raise BEncodeError, "Invalid dictionary: missing value for key (#{key}). #{scanner.pos}" unless value

  [key, value]
end

.parse_list(scanner) ⇒ ::Array

This method parases a bencoded list.

scanner = StringScanner.new("le")
BEncodr::Parser.parse_list(scanner) #=> []

Parameters:

  • scanner (StringScanner)

    the scanner of a bencoded list

Returns:

  • (::Array)

    the parsed array



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/bencodr/parser.rb', line 72

def parse_list(scanner)
  list = []

  scanner.scan(/l/) or raise BEncodeError, "Invalid list: missing opening l. #{scanner.pos}"
  while true
    object = parse_object(scanner)
    break unless object
    list << object
  end
  scanner.scan(/e/) or raise BEncodeError, "Invalid list: missing closing e. #{scanner.pos}"

  list
end

.parse_object(scanner) ⇒ ::String, ...

This method parases a bencoded object.

scanner = StringScanner.new("6:string")
BEncodr::Parser.parse_object(scanner) #=> "string"

Parameters:

  • scanner (StringScanner)

    the scanner of a bencoded object

Returns:

  • (::String, ::Integer, ::Hash, ::Array, nil)

    an object if type is recognized or nil



15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/bencodr/parser.rb', line 15

def parse_object(scanner)
  case scanner.peek(1)[0]
    when ?0..?9
      parse_string(scanner)
    when ?i
      parse_integer(scanner)
    when ?l
      parse_list(scanner)
    when ?d
      parse_dictionary(scanner)
    else
      nil
  end
end

.parse_string(scanner) ⇒ ::String

This method parases a bencoded string.

scanner = StringScanner.new("6:string")
BEncodr::Parser.parse_string(scanner) #=> "string"

Parameters:

  • scanner (StringScanner)

    the scanner of a bencoded string

Returns:

  • (::String)

    the parsed string



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/bencodr/parser.rb', line 37

def parse_string(scanner)
  length = scanner.scan(/[1-9][0-9]*|0/).to_i   or raise BEncodeError, "Invalid string: length invalid. #{scanner.pos}"
  scanner.scan(/:/)                             or raise BEncodeError, "Invalid string: missing colon(:). #{scanner.pos}"
  byte_string = scanner.peek(length)  
  byte_string.length == length                  or raise BEncodeError, "Invalid string: length too long(#{length}) #{scanner.pos}."
  scanner.pos = scanner.pos + length

  if RUBY_VERSION =~ /1\.9/
    byte_string.encode('UTF-8') rescue byte_string.force_encoding('UTF-8')
  else
    byte_string
  end
end