# class DBus::PacketMarshaller

D-Bus packet marshaller class

Class that handles the conversion (marshalling) of Ruby objects to (binary) payload data.

### Attributes

packet[R]

The current or result packet. FIXME: allow access only when marshalling is finished

### Public Class Methods

make_variant(value) click to toggle source

Make a [signature, value] pair for a variant

    # File lib/dbus/marshall.rb, line 404
    def self.make_variant(value)
      # TODO: mix in _make_variant to String, Integer...
      if value == true
        ["b", true]
      elsif value == false
        ["b", false]
      elsif value.nil?
        ["b", nil]
      elsif value.is_a? Float
        ["d", value]
      elsif value.is_a? Symbol
        ["s", value.to_s]
      elsif value.is_a? Array
        ["av", value.map {|i| make_variant(i) }]
      elsif value.is_a? Hash
        h = {}
        value.each_key {|k| h[k] = make_variant(value[k]) }
        ["a{sv}", h]
      elsif value.respond_to? :to_str
        ["s", value.to_str]
      elsif value.respond_to? :to_int
        i = value.to_int
        if -2_147_483_648 <= i && i < 2_147_483_648
          ["i", i]
        else
          ["x", i]
        end
      end
    end

new(offset = 0) click to toggle source

Create a new marshaller, setting the current packet to the empty packet.

    # File lib/dbus/marshall.rb, line 242
    def initialize(offset = 0)
      @packet = ""
      @offset = offset          # for correct alignment of nested marshallers
    end

### Public Instance Methods

align(a) click to toggle source

Align the buffer with NULL (0) bytes on a byte length of a.

    # File lib/dbus/marshall.rb, line 259
    def align(a)
      @packet = @packet.ljust(num_align(@offset + @packet.bytesize, a) - @offset, 0.chr)
    end

append(type, val) click to toggle source

Append a value val to the packet based on its type.

Host native endianness is used, declared in DBus::Message#marshall

    # File lib/dbus/marshall.rb, line 298
    def append(type, val)
      raise TypeException, "Cannot send nil" if val.nil?

      type = type.chr if type.kind_of?(Fixnum)
      type = Type::Parser.new(type).parse[0] if type.kind_of?(String)
      case type.sigtype
      when Type::BYTE
        @packet += val.chr
      when Type::UINT32, Type::UNIX_FD
        align(4)
        @packet += [val].pack("L")
      when Type::UINT64
        align(8)
        @packet += [val].pack("Q")
      when Type::INT64
        align(8)
        @packet += [val].pack("q")
      when Type::INT32
        align(4)
        @packet += [val].pack("l")
      when Type::UINT16
        align(2)
        @packet += [val].pack("S")
      when Type::INT16
        align(2)
        @packet += [val].pack("s")
      when Type::DOUBLE
        align(8)
        @packet += [val].pack("d")
      when Type::BOOLEAN
        align(4)
        if val
          @packet += [1].pack("L")
        else
          @packet += [0].pack("L")
        end
      when Type::OBJECT_PATH
        append_string(val)
      when Type::STRING
        append_string(val)
      when Type::SIGNATURE
        append_signature(val)
      when Type::VARIANT
        vartype = nil
        if val.is_a?(Array) and val.size == 2
          if val[0].is_a?(DBus::Type::Type)
            vartype, vardata = val
          elsif val[0].is_a?(String)
            begin
              parsed = Type::Parser.new(val[0]).parse
              vartype = parsed[0] if parsed.size == 1
              vardata = val[1]
            rescue Type::SignatureException
              # no assignment
            end
          end
        end
        if vartype.nil?
          vartype, vardata = PacketMarshaller.make_variant(val)
          vartype = Type::Parser.new(vartype).parse[0]
        end

        append_signature(vartype.to_s)
        align(vartype.alignment)
        sub = PacketMarshaller.new(@offset + @packet.bytesize)
        sub.append(vartype, vardata)
        @packet += sub.packet
      when Type::ARRAY
        if val.kind_of?(Hash)
          raise TypeException, "Expected an Array but got a Hash" if type.child.sigtype != Type::DICT_ENTRY
          # Damn ruby rocks here
          val = val.to_a
        end
        # If string is recieved and ay is expected, explode the string
        if val.kind_of?(String) && type.child.sigtype == Type::BYTE
          val = val.bytes
        end
        if not val.kind_of?(Enumerable)
          raise TypeException, "Expected an Enumerable of #{type.child.inspect} but got a #{val.class}"
        end
        array(type.child) do
          val.each do |elem|
            append(type.child, elem)
          end
        end
      when Type::STRUCT, Type::DICT_ENTRY
        # TODO use duck typing, val.respond_to?
        raise TypeException, "Struct/DE expects an Array" if not val.kind_of?(Array)
        if type.sigtype == Type::DICT_ENTRY and val.size != 2
          raise TypeException, "Dict entry expects a pair"
        end
        if type.members.size != val.size
          raise TypeException, "Struct/DE has #{val.size} elements but type info for #{type.members.size}"
        end
        struct do
          type.members.zip(val).each do |t, v|
            append(t, v)
          end
        end
      else
        raise NotImplementedError,
          "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
      end
    end

append_signature(str) click to toggle source

Append the the signature signature itself to the packet.

    # File lib/dbus/marshall.rb, line 270
    def append_signature(str)
      @packet += str.bytesize.chr + str + "\0"
    end

append_string(str) click to toggle source

Append the the string str itself to the packet.

    # File lib/dbus/marshall.rb, line 264
    def append_string(str)
      align(4)
      @packet += [str.bytesize].pack("L") + [str].pack("Z*")
    end

array(type) { || ... } click to toggle source

Append the array type type to the packet and allow for appending the child elements.

    # File lib/dbus/marshall.rb, line 276
    def array(type)
      # Thanks to Peter Rullmann for this line
      align(4)
      sizeidx = @packet.bytesize
      @packet += "ABCD"
      align(type.alignment)
      contentidx = @packet.bytesize
      yield
      sz = @packet.bytesize - contentidx
      raise InvalidPacketException if sz > 67108864
      @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
    end

num_align(n, a) click to toggle source

Round n up to the specified power of two, a

    # File lib/dbus/marshall.rb, line 248
    def num_align(n, a)
      case a
      when 1, 2, 4, 8
        bits = a - 1
        n + bits & ~bits
      else
        raise "Unsupported alignment"
      end
    end

struct() { || ... } click to toggle source

Align and allow for appending struct fields.

    # File lib/dbus/marshall.rb, line 290
    def struct
      align(8)
      yield
    end