# 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
    403 def self.make_variant(value)
    404   # TODO: mix in _make_variant to String, Integer...
    405   if value == true
    406     ["b", true]
    407   elsif value == false
    408     ["b", false]
    409   elsif value.nil?
    410     ["b", nil]
    411   elsif value.is_a? Float
    412     ["d", value]
    413   elsif value.is_a? Symbol
    414     ["s", value.to_s]
    415   elsif value.is_a? Array
    416     ["av", value.map { |i| make_variant(i) }]
    417   elsif value.is_a? Hash
    418     h = {}
    419     value.each_key { |k| h[k] = make_variant(value[k]) }
    420     ["a{sv}", h]
    421   elsif value.respond_to? :to_str
    422     ["s", value.to_str]
    423   elsif value.respond_to? :to_int
    424     i = value.to_int
    425     if -2_147_483_648 <= i && i < 2_147_483_648
    426       ["i", i]
    427     else
    428       ["x", i]
    429     end
    430   end
    431 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
    241 def initialize(offset = 0)
    242   @packet = ""
    243   @offset = offset # for correct alignment of nested marshallers
    244 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
    258 def align(a)
    259   @packet = @packet.ljust(num_align(@offset + @packet.bytesize, a) - @offset, 0.chr)
    260 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 `Message#marshall`

        # File lib/dbus/marshall.rb
    297 def append(type, val)
    298   raise TypeException, "Cannot send nil" if val.nil?
    299
    300   type = type.chr if type.is_a?(Integer)
    301   type = Type::Parser.new(type).parse[0] if type.is_a?(String)
    302   case type.sigtype
    303   when Type::BYTE
    304     @packet += val.chr
    305   when Type::UINT32, Type::UNIX_FD
    306     align(4)
    307     @packet += [val].pack("L")
    308   when Type::UINT64
    309     align(8)
    310     @packet += [val].pack("Q")
    311   when Type::INT64
    312     align(8)
    313     @packet += [val].pack("q")
    314   when Type::INT32
    315     align(4)
    316     @packet += [val].pack("l")
    317   when Type::UINT16
    318     align(2)
    319     @packet += [val].pack("S")
    320   when Type::INT16
    321     align(2)
    322     @packet += [val].pack("s")
    323   when Type::DOUBLE
    324     align(8)
    325     @packet += [val].pack("d")
    326   when Type::BOOLEAN
    327     align(4)
    328     @packet += if val
    329                  [1].pack("L")
    330                else
    331                  [0].pack("L")
    332                end
    333   when Type::OBJECT_PATH
    334     append_string(val)
    335   when Type::STRING
    336     append_string(val)
    337   when Type::SIGNATURE
    338     append_signature(val)
    339   when Type::VARIANT
    340     vartype = nil
    341     if val.is_a?(Array) && val.size == 2
    342       if val[0].is_a?(DBus::Type::Type)
    343         vartype, vardata = val
    344       elsif val[0].is_a?(String)
    345         begin
    346           parsed = Type::Parser.new(val[0]).parse
    347           vartype = parsed[0] if parsed.size == 1
    348           vardata = val[1]
    349         rescue Type::SignatureException
    350           # no assignment
    351         end
    352       end
    353     end
    354     if vartype.nil?
    355       vartype, vardata = PacketMarshaller.make_variant(val)
    356       vartype = Type::Parser.new(vartype).parse[0]
    357     end
    358
    359     append_signature(vartype.to_s)
    360     align(vartype.alignment)
    361     sub = PacketMarshaller.new(@offset + @packet.bytesize)
    362     sub.append(vartype, vardata)
    363     @packet += sub.packet
    364   when Type::ARRAY
    365     if val.is_a?(Hash)
    366       raise TypeException, "Expected an Array but got a Hash" if type.child.sigtype != Type::DICT_ENTRY
    367       # Damn ruby rocks here
    368       val = val.to_a
    369     end
    370     # If string is recieved and ay is expected, explode the string
    371     if val.is_a?(String) && type.child.sigtype == Type::BYTE
    372       val = val.bytes
    373     end
    374     if !val.is_a?(Enumerable)
    375       raise TypeException, "Expected an Enumerable of #{type.child.inspect} but got a #{val.class}"
    376     end
    377     array(type.child) do
    378       val.each do |elem|
    379         append(type.child, elem)
    380       end
    381     end
    382   when Type::STRUCT, Type::DICT_ENTRY
    383     # TODO: use duck typing, val.respond_to?
    384     raise TypeException, "Struct/DE expects an Array" if !val.is_a?(Array)
    385     if type.sigtype == Type::DICT_ENTRY && val.size != 2
    386       raise TypeException, "Dict entry expects a pair"
    387     end
    388     if type.members.size != val.size
    389       raise TypeException, "Struct/DE has #{val.size} elements but type info for #{type.members.size}"
    390     end
    391     struct do
    392       type.members.zip(val).each do |t, v|
    393         append(t, v)
    394       end
    395     end
    396   else
    397     raise NotImplementedError,
    398           "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
    399   end
    400 end

append_signature(str) click to toggle source

Append the the signature signature itself to the packet.

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

append_string(str) click to toggle source

Append the the string str itself to the packet.

        # File lib/dbus/marshall.rb
    263 def append_string(str)
    264   align(4)
    265   @packet += [str.bytesize].pack("L") + [str].pack("Z*")
    266 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
    275 def array(type)
    276   # Thanks to Peter Rullmann for this line
    277   align(4)
    278   sizeidx = @packet.bytesize
    279   @packet += "ABCD"
    280   align(type.alignment)
    281   contentidx = @packet.bytesize
    282   yield
    283   sz = @packet.bytesize - contentidx
    284   raise InvalidPacketException if sz > 67_108_864
    285   @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
    286 end

num_align(n, a) click to toggle source

Round n up to the specified power of two, a

        # File lib/dbus/marshall.rb
    247 def num_align(n, a)
    248   case a
    249   when 1, 2, 4, 8
    250     bits = a - 1
    251     n + bits & ~bits
    252   else
    253     raise "Unsupported alignment"
    254   end
    255 end

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

Align and allow for appending struct fields.

        # File lib/dbus/marshall.rb
    289 def struct
    290   align(8)
    291   yield
    292 end