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.



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

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



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

def packet
  @packet
end

Class Method Details

.make_variant(value) ⇒ Object

Make a [signature, value] pair for a variant



403
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
# File 'lib/dbus/marshall.rb', line 403

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.



258
259
260
# File 'lib/dbus/marshall.rb', line 258

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:



297
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
# File 'lib/dbus/marshall.rb', line 297

def append(type, val)
  raise TypeException, "Cannot send nil" if val.nil?

  type = type.chr if type.is_a?(Integer)
  type = Type::Parser.new(type).parse[0] if type.is_a?(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)
    @packet += if val
                 [1].pack("L")
               else
                 [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) && 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.is_a?(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.is_a?(String) && type.child.sigtype == Type::BYTE
      val = val.bytes
    end
    if !val.is_a?(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 !val.is_a?(Array)
    if type.sigtype == Type::DICT_ENTRY && 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.



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

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

#append_string(str) ⇒ Object

Append the the string str itself to the packet.



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

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.



275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/dbus/marshall.rb', line 275

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 > 67_108_864
  @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
end

#num_align(n, a) ⇒ Object

Round n up to the specified power of two, a



247
248
249
250
251
252
253
254
255
# File 'lib/dbus/marshall.rb', line 247

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.



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

def struct
  align(8)
  yield
end