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.



251
252
253
254
# File 'lib/dbus/marshall.rb', line 251

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



247
248
249
# File 'lib/dbus/marshall.rb', line 247

def packet
  @packet
end

Class Method Details

.make_variant(value) ⇒ Object

Make a [signature, value] pair for a variant



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/dbus/marshall.rb', line 414

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.



268
269
270
# File 'lib/dbus/marshall.rb', line 268

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:



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
402
403
404
405
406
407
408
409
410
411
# File 'lib/dbus/marshall.rb', line 312

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 not val.kind_of?(Array)
      raise TypeException, "Expected an Array 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.



279
280
281
# File 'lib/dbus/marshall.rb', line 279

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

#append_simple_string(s) ⇒ Object

Append a string of bytes without type.



305
306
307
# File 'lib/dbus/marshall.rb', line 305

def append_simple_string(s)
  @packet += s + "\0"
end

#append_string(str) ⇒ Object

Append the the string str itself to the packet.



273
274
275
276
# File 'lib/dbus/marshall.rb', line 273

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.



285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/dbus/marshall.rb', line 285

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



257
258
259
260
261
262
263
264
265
# File 'lib/dbus/marshall.rb', line 257

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.



299
300
301
302
# File 'lib/dbus/marshall.rb', line 299

def struct
  align(8)
  yield
end