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