class Mmdb::Decoder

Constants

METADATA_BEGIN
Node
POINTER_BASE_VALUES
SIZE_BASE_VALUES
TYPES

Attributes

data[R]

Public Class Methods

new(data) click to toggle source
# File lib/mmdb/decoder.rb, line 5
def initialize(data)
  @data = data
end

Public Instance Methods

decode(position:, base:) click to toggle source
# File lib/mmdb/decoder.rb, line 56
def decode(position:, base:)
  ctrl = data[position + base].ord
  type = ctrl >> 5
  position += 1

  if type == POINTER
    decode_pointer(position, base, ctrl)
  else
    if type == EXTENDED_TYPE
      type = 7 + data[position + base].ord
      position += 1
    end

    size = ctrl & 0x1f
    if size >= 29
      byte_size = size - 29 + 1
      val = fetch(position, base, byte_size)
      position += byte_size
      size = val + SIZE_BASE_VALUES[byte_size]
    end

    case type
    when UTF8
      val = data[position + base, size].encode('utf-8', 'utf-8')
      Node.new(position + size, val)
    when DOUBLE
      val = data[position + base, size].unpack('G')[0]
      Node.new(position + size, val)
    when BYTE
      val = data[position + base, size]
      Node.new(position + size, val)
    when UINT16, UINT32, UINT64, UINT128
      val = fetch(position, base, size)
      Node.new(position + size, val)
    when MAP
      val = size.times.each_with_object({}) do |_, map|
        key_node = decode(position: position, base: base)
        val_node = decode(position: key_node.position, base: base)
        position = val_node.position
        map[key_node.value] = val_node.value
      end
      Node.new(position, val)
    when INT32
      v1 = data[position + base, size].unpack('N')[0]
      bits = size * 8
      val = (v1 & ~(1 << bits)) - (v1 & (1 << bits))
      Node.new(position + size, val)
    when ARRAY
      val = Array.new(size) do
        node = decode(position: position, base: base)
        position = node.position
        node.value
      end
      Node.new(position, val)
    when DATA_CACHE_CONTAINER
      raise 'TODO:'
    when END_MARKER
      Node.new(position, nil)
    when BOOLEAN
      Node.new(position, !size.zero?)
    when FLOAT
      val = data[position + base, size].unpack('g')[0]
      Node.new(position + size, val)
    end
  end
end
ip_version() click to toggle source
# File lib/mmdb/decoder.rb, line 23
def ip_version
  metadata['ip_version']
end
metadata() click to toggle source
# File lib/mmdb/decoder.rb, line 9
def metadata
  @metadata ||=
    begin
      index = data.rindex(METADATA_BEGIN)
      raise InvalidFileFormat if index.nil?

      decode(position: index + METADATA_BEGIN.size, base: 0).value
    end
end
node_byte_size() click to toggle source
# File lib/mmdb/decoder.rb, line 31
def node_byte_size
  @node_byte_size ||= metadata['record_size'] * 2 / 8
end
node_count() click to toggle source
# File lib/mmdb/decoder.rb, line 27
def node_count
  metadata['node_count']
end
read(node:, flag:) click to toggle source
# File lib/mmdb/decoder.rb, line 43
def read(node:, flag:)
  position = node_byte_size * node
  middle = data[position + record_byte_size].ord if node_byte_size.odd?
  if flag.zero? # LEFT node
    val = fetch(position, 0)
    val += ((middle & 0xf0) << 20) if middle
  else # RIGHT node
    val = fetch(position + node_byte_size - record_byte_size, 0)
    val += ((middle & 0xf) << 24) if middle
  end
  val
end
record_byte_size() click to toggle source
# File lib/mmdb/decoder.rb, line 39
def record_byte_size
  @record_byte_size ||= node_byte_size / 2
end
search_tree_size() click to toggle source
# File lib/mmdb/decoder.rb, line 35
def search_tree_size
  @search_tree_size ||= node_count * node_byte_size
end
start_index() click to toggle source
# File lib/mmdb/decoder.rb, line 19
def start_index
  @start_index ||= ip_version == 4 ? 96 : 0
end

Private Instance Methods

decode_pointer(position, base, ctrl) click to toggle source
# File lib/mmdb/decoder.rb, line 150
def decode_pointer(position, base, ctrl)
  size = ((ctrl >> 3) & 0x3) + 1
  v1 = ctrl & 0x7
  v2 = fetch(position, base, size)
  pointer = (v1 << (8 * size)) + v2 + POINTER_BASE_VALUES[size]
  Node.new(position + size, decode(position: pointer, base: base).value)
end
fetch(position, base, size = record_byte_size) click to toggle source
# File lib/mmdb/decoder.rb, line 158
def fetch(position, base, size = record_byte_size)
  bytes = data[position + base, size].unpack('C*')
  bytes.inject(0) { |r, v| (r << 8) + v }
end