# class DBus::PacketUnmarshaller

# D-Bus packet unmarshaller class¶ ↑

Class that handles the conversion (unmarshalling) of payload data to Array.

### Attributes

idx[R]

Index pointer that points to the byte in the data that is currently being processed.

Used to kown what part of the buffer has been consumed by unmarshalling. FIXME: Maybe should be accessed with a “consumed_size” method.

### Public Class Methods

new(buffer, endianness) click to toggle source

Create a new unmarshaller for the given data buffer and endianness.

    # File lib/dbus/marshall.rb, line 34
    def initialize(buffer, endianness)
      @buffy, @endianness = buffer.dup, endianness
      if @endianness == BIG_END
        @uint32 = "N"
        @uint16 = "n"
        @double = "G"
      elsif @endianness == LIL_END
        @uint32 = "V"
        @uint16 = "v"
        @double = "E"
      else
        raise InvalidPacketException, "Incorrect endianness #{@endianness}"
      end
      @idx = 0
    end

### Public Instance Methods

align(a) click to toggle source

Align the pointer index on a byte index of a, where a must be 1, 2, 4 or 8.

    # File lib/dbus/marshall.rb, line 68
    def align(a)
      case a
      when 1
      when 2, 4, 8
        bits = a - 1
        @idx = @idx + bits & ~bits
        raise IncompleteBufferException if @idx > @buffy.bytesize
      else
        raise "Unsupported alignment #{a}"
      end
    end

unmarshall(signature, len = nil) click to toggle source

Unmarshall the buffer for a given signature and length len. Return an array of unmarshalled objects

    # File lib/dbus/marshall.rb, line 52
    def unmarshall(signature, len = nil)
      if len != nil
        if @buffy.bytesize < @idx + len
          raise IncompleteBufferException
        end
      end
      sigtree = Type::Parser.new(signature).parse
      ret = Array.new
      sigtree.each do |elem|
        ret << do_parse(elem)
      end
      ret
    end

### Private Instance Methods

do_parse(signature) click to toggle source

Based on the signature type, retrieve a packet from the buffer and return it.

    # File lib/dbus/marshall.rb, line 126
    def do_parse(signature)
      packet = nil
      case signature.sigtype
      when Type::BYTE
        packet = get(1).unpack("C")[0]
      when Type::UINT16
        align(2)
        packet = get(2).unpack(@uint16)[0]
      when Type::INT16
        align(4)
        packet = get(4).unpack(@uint16)[0]
        if (packet & 0x8000) != 0
          packet -= 0x10000
        end
      when Type::UINT32, Type::UNIX_FD
        align(4)
        packet = get(4).unpack(@uint32)[0]
      when Type::INT32
        align(4)
        packet = get(4).unpack(@uint32)[0]
        if (packet & 0x80000000) != 0
          packet -= 0x100000000
        end
      when Type::UINT64
        align(8)
        packet_l = get(4).unpack(@uint32)[0]
        packet_h = get(4).unpack(@uint32)[0]
        if @endianness == LIL_END
          packet = packet_l + packet_h * 2**32
        else
          packet = packet_l * 2**32 + packet_h
        end
      when Type::INT64
        align(8)
        packet_l = get(4).unpack(@uint32)[0]
        packet_h = get(4).unpack(@uint32)[0]
        if @endianness == LIL_END
          packet = packet_l + packet_h * 2**32
        else
          packet = packet_l * 2**32 + packet_h
        end
        if (packet & 0x8000000000000000) != 0
          packet -= 0x10000000000000000
        end
      when Type::DOUBLE
        align(8)
        packet = get(8).unpack(@double)[0]
      when Type::BOOLEAN
        align(4)
        v = get(4).unpack(@uint32)[0]
        raise InvalidPacketException if not [0, 1].member?(v)
        packet = (v == 1)
      when Type::ARRAY
        align(4)
        # checks please
        array_sz = get(4).unpack(@uint32)[0]
        raise InvalidPacketException if array_sz > 67108864

        align(signature.child.alignment)
        raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize

        packet = Array.new
        start_idx = @idx
        while @idx - start_idx < array_sz
          packet << do_parse(signature.child)
        end

        if signature.child.sigtype == Type::DICT_ENTRY then
          packet = packet.inject(Hash.new) do |hash, pair|
            hash[pair[0]] = pair[1]
            hash
          end
        end
      when Type::STRUCT
        align(8)
        packet = Array.new
        signature.members.each do |elem|
          packet << do_parse(elem)
        end
      when Type::VARIANT
        string = get_signature
        # error checking please
        sig = Type::Parser.new(string).parse[0]
        align(sig.alignment)
        packet = do_parse(sig)
      when Type::OBJECT_PATH
        packet = get_string
      when Type::STRING
        packet = get_string
        packet.force_encoding('UTF-8')
      when Type::SIGNATURE
        packet = get_signature
      when Type::DICT_ENTRY
        align(8)
        key = do_parse(signature.members[0])
        value = do_parse(signature.members[1])
        packet = [key, value]
      else
        raise NotImplementedError,
          "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
      end
      packet
    end

get(nbytes) click to toggle source

Retrieve the next nbytes number of bytes from the buffer.

    # File lib/dbus/marshall.rb, line 86
    def get(nbytes)
      raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
      ret = @buffy.slice(@idx, nbytes)
      @idx += nbytes
      ret
    end

get_signature() click to toggle source

Get the signature length and signature itself from the buffer. Return the signature.

    # File lib/dbus/marshall.rb, line 111
    def get_signature
      str_sz = get(1).unpack('C')[0]
      ret = @buffy.slice(@idx, str_sz)
      raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.bytesize
      @idx += str_sz
      if @buffy[@idx].ord != 0
        raise InvalidPacketException, "Type is not nul-terminated"
      end
      @idx += 1
      # no exception, see check above
      ret
    end

get_string() click to toggle source

Get the string length and string itself from the buffer. Return the string.

    # File lib/dbus/marshall.rb, line 95
    def get_string
      align(4)
      str_sz = get(4).unpack(@uint32)[0]
      ret = @buffy.slice(@idx, str_sz)
      raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
      @idx += str_sz
      if @buffy[@idx].ord != 0
        raise InvalidPacketException, "String is not nul-terminated"
      end
      @idx += 1
      # no exception, see check above
      ret
    end