class DBus::PacketMarshaller
D-Bus packet marshaller class
Class
that handles the conversion (marshalling) of Ruby objects to (binary) payload data.
Attributes
The current or result packet. FIXME: allow access only when marshalling is finished
Public Class Methods
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
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 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 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 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 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
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
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
Align and allow for appending struct fields.
# File lib/dbus/marshall.rb 289 def struct 290 align(8) 291 yield 292 end