Class: DBus::PacketMarshaller

Inherits:
Object
  • Object
show all
Defined in:
lib/dbus/marshall.rb

Overview

D-Bus packet marshaller class

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(offset = 0) ⇒ PacketMarshaller

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



242
243
244
245
# File 'lib/dbus/marshall.rb', line 242

def initialize(offset = 0)
  @packet = ""
  @offset = offset          # for correct alignment of nested marshallers
end

Instance Attribute Details

#packetObject (readonly)

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



238
239
240
# File 'lib/dbus/marshall.rb', line 238

def packet
  @packet
end

Class Method Details

.make_variant(value) ⇒ Object

Make a [signature, value] pair for a variant



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# 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

Instance Method Details

#align(a) ⇒ Object

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



259
260
261
# 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) ⇒ Object

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

Host native endianness is used, declared in Message#marshall

Raises:



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# 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) ⇒ Object

Append the the signature signature itself to the packet.



270
271
272
# File 'lib/dbus/marshall.rb', line 270

def append_signature(str)
  @packet += str.bytesize.chr + str + "\0"
end

#append_string(str) ⇒ Object

Append the the string str itself to the packet.



264
265
266
267
# File 'lib/dbus/marshall.rb', line 264

def append_string(str)
  align(4)
  @packet += [str.bytesize].pack("L") + [str].pack("Z*")
end

#array(type) ⇒ Object

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



276
277
278
279
280
281
282
283
284
285
286
287
# 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) ⇒ Object

Round n up to the specified power of two, a



248
249
250
251
252
253
254
255
256
# 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

#structObject

Align and allow for appending struct fields.



290
291
292
293
# File 'lib/dbus/marshall.rb', line 290

def struct
  align(8)
  yield
end