# class DBus::PacketMarshaller

D-Bus packet marshaller class

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

### Attributes

endianness[R]

@return [:little,:big]

packet[R]

The current or result packet. FIXME: allow access only when marshalling is finished @return [String]

### Public Class Methods

make_variant(value) click to toggle source

Make a [signature, value] pair for a variant

        # File lib/dbus/marshall.rb
    346 def self.make_variant(value)
    347   # TODO: mix in _make_variant to String, Integer...
    348   if value == true
    349     ["b", true]
    350   elsif value == false
    351     ["b", false]
    352   elsif value.nil?
    353     ["b", nil]
    354   elsif value.is_a? Float
    355     ["d", value]
    356   elsif value.is_a? Symbol
    357     ["s", value.to_s]
    358   elsif value.is_a? Array
    359     ["av", value.map { |i| make_variant(i) }]
    360   elsif value.is_a? Hash
    361     h = {}
    362     value.each_key { |k| h[k] = make_variant(value[k]) }
    363     key_type = if value.empty?
    364                  "s"
    365                else
    366                  t, = make_variant(value.first.first)
    367                  t
    368                end
    369     ["a{#{key_type}v}", h]
    370   elsif value.respond_to? :to_str
    371     ["s", value.to_str]
    372   elsif value.respond_to? :to_int
    373     i = value.to_int
    374     if Data::Int32.range.cover?(i)
    375       ["i", i]
    376     elsif Data::Int64.range.cover?(i)
    377       ["x", i]
    378     else
    379       ["t", i]
    380     end
    381   end
    382 end

new(offset = 0, endianness: HOST_ENDIANNESS) click to toggle source

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

        # File lib/dbus/marshall.rb
    172 def initialize(offset = 0, endianness: HOST_ENDIANNESS)
    173   @endianness = endianness
    174   @packet = ""
    175   @offset = offset # for correct alignment of nested marshallers
    176 end

### Public Instance Methods

align(alignment) click to toggle source

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

        # File lib/dbus/marshall.rb
    190 def align(alignment)
    191   pad_count = num_align(@offset + @packet.bytesize, alignment) - @offset
    192   @packet = @packet.ljust(pad_count, 0.chr)
    193 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`

@param type [SingleCompleteType] (or Integer or {Type}) @param val [::Object]

        # File lib/dbus/marshall.rb
    224 def append(type, val)
    225   raise TypeException, "Cannot send nil" if val.nil?
    226
    227   type = type.chr if type.is_a?(Integer)
    228   type = Type::Parser.new(type).parse[0] if type.is_a?(String)
    229   # type is [Type] now
    230   data_class = Data::BY_TYPE_CODE[type.sigtype]
    231   if data_class.nil?
    232     raise NotImplementedError,
    233           "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
    234   end
    235
    236   if data_class.fixed?
    237     align(data_class.alignment)
    238     data = data_class.new(val)
    239     @packet += data.marshall(endianness)
    240   elsif data_class.basic?
    241     val = val.value if val.is_a?(Data::Basic)
    242     align(data_class.size_class.alignment)
    243     size_data = data_class.size_class.new(val.bytesize)
    244     @packet += size_data.marshall(endianness)
    245     # Z* makes a binary string, as opposed to interpolation
    246     @packet += [val].pack("Z*")
    247   else
    248     case type.sigtype
    249
    250     when Type::VARIANT
    251       append_variant(val)
    252     when Type::ARRAY
    253       val = val.exact_value if val.is_a?(Data::Array)
    254       append_array(type.child, val)
    255     when Type::STRUCT, Type::DICT_ENTRY
    256       val = val.exact_value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry)
    257       unless val.is_a?(Array) || val.is_a?(Struct)
    258         type_name = Type::TYPE_MAPPING[type.sigtype].first
    259         raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
    260       end
    261
    262       if type.sigtype == Type::DICT_ENTRY && val.size != 2
    263         raise TypeException, "DICT_ENTRY expects a pair"
    264       end
    265
    266       if type.members.size != val.size
    267         type_name = Type::TYPE_MAPPING[type.sigtype].first
    268         raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}"
    269       end
    270
    271       struct do
    272         type.members.zip(val).each do |t, v|
    273           append(t, v)
    274         end
    275       end
    276     else
    277       raise NotImplementedError,
    278             "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
    279     end
    280   end
    281 end

append_array(child_type, val) click to toggle source

@param child_type [Type]

        # File lib/dbus/marshall.rb
    323 def append_array(child_type, val)
    324   if val.is_a?(Hash)
    325     raise TypeException, "Expected an Array but got a Hash" if child_type.sigtype != Type::DICT_ENTRY
    326
    327     # Damn ruby rocks here
    328     val = val.to_a
    329   end
    330   # If string is received and ay is expected, explode the string
    331   if val.is_a?(String) && child_type.sigtype == Type::BYTE
    332     val = val.bytes
    333   end
    334   if !val.is_a?(Enumerable)
    335     raise TypeException, "Expected an Enumerable of #{child_type.inspect} but got a #{val.class}"
    336   end
    337
    338   array(child_type) do
    339     val.each do |elem|
    340       append(child_type, elem)
    341     end
    342   end
    343 end

append_variant(val) click to toggle source

        # File lib/dbus/marshall.rb
    283 def append_variant(val)
    284   vartype = nil
    285   if val.is_a?(DBus::Data::Variant)
    286     vartype = val.member_type
    287     vardata = val.exact_value
    288   elsif val.is_a?(DBus::Data::Container)
    289     vartype = val.type
    290     vardata = val.exact_value
    291   elsif val.is_a?(DBus::Data::Base)
    292     vartype = val.type
    293     vardata = val.value
    294   elsif val.is_a?(Array) && val.size == 2
    295     case val[0]
    296     when Type
    297       vartype, vardata = val
    298     # Ambiguous but easy to use, because Type
    299     # cannot construct "as" "a{sv}" easily
    300     when String
    301       begin
    302         parsed = Type::Parser.new(val[0]).parse
    303         vartype = parsed[0] if parsed.size == 1
    304         vardata = val[1]
    305       rescue Type::SignatureException
    306         # no assignment
    307       end
    308     end
    309   end
    310   if vartype.nil?
    311     vartype, vardata = PacketMarshaller.make_variant(val)
    312     vartype = Type::Parser.new(vartype).parse[0]
    313   end
    314
    315   append(Data::Signature.type, vartype.to_s)
    316   align(vartype.alignment)
    317   sub = PacketMarshaller.new(@offset + @packet.bytesize, endianness: endianness)
    318   sub.append(vartype, vardata)
    319   @packet += sub.packet
    320 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
    197 def array(type)
    198   # Thanks to Peter Rullmann for this line
    199   align(4)
    200   sizeidx = @packet.bytesize
    201   @packet += "ABCD"
    202   align(type.alignment)
    203   contentidx = @packet.bytesize
    204   yield
    205   sz = @packet.bytesize - contentidx
    206   raise InvalidPacketException if sz > 67_108_864
    207
    208   sz_data = Data::UInt32.new(sz)
    209   @packet[sizeidx...sizeidx + 4] = sz_data.marshall(endianness)
    210 end

num_align(num, alignment) click to toggle source

Round num up to the specified power of two, alignment

        # File lib/dbus/marshall.rb
    179 def num_align(num, alignment)
    180   case alignment
    181   when 1, 2, 4, 8
    182     bits = alignment - 1
    183     (num + bits) & ~bits
    184   else
    185     raise ArgumentError, "Unsupported alignment #{alignment}"
    186   end
    187 end

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

Align and allow for appending struct fields.

        # File lib/dbus/marshall.rb
    213 def struct
    214   align(8)
    215   yield
    216 end