class DBus::PacketUnmarshaller
D-Bus packet unmarshaller class¶ ↑
Class that handles the conversion (unmarshalling) of payload data to #{::Object}s (in plain mode) or to {Data::Base} (in exact mode)
Spelling note: this codebase always uses a double L in the “marshall” word and its inflections.
Public Class Methods
Create a new unmarshaller for the given data buffer. @param buffer [String] @param endianness [:little,:big]
# File lib/dbus/marshall.rb 36 def initialize(buffer, endianness) 37 # TODO: this dup can be avoided if we can prove 38 # that an IncompleteBufferException leaves the original *buffer* intact 39 buffer = buffer.dup 40 @raw_msg = RawMessage.new(buffer, endianness) 41 end
Public Instance Methods
after the headers, the body starts 8-aligned
# File lib/dbus/marshall.rb 66 def align_body 67 @raw_msg.align(8) 68 end
@return [Integer]
# File lib/dbus/marshall.rb 71 def consumed_size 72 @raw_msg.pos 73 end
Unmarshall the buffer for a given signature and length len. Return an array of unmarshalled objects. @param signature [Signature] @param len [Integer,nil] if given, and there is not enough data
in the buffer, raise {IncompleteBufferException}
@param mode [:plain,:exact] @return [Array<::Object,DBus::Data::Base>]
Objects in `:plain` mode, {DBus::Data::Base} in `:exact` mode
The array size corresponds to the number of types in *signature*.
@raise IncompleteBufferException @raise InvalidPacketException
# File lib/dbus/marshall.rb 54 def unmarshall(signature, len = nil, mode: :plain) 55 @raw_msg.want!(len) if len 56 57 sigtree = Type::Parser.new(signature).parse 58 ret = [] 59 sigtree.each do |elem| 60 ret << do_parse(elem, mode: mode) 61 end 62 ret 63 end
Private Instance Methods
@param data_class [Class] a subclass of Data::Base (specific?) @return [::Integer,::Float]
# File lib/dbus/marshall.rb 79 def aligned_read_value(data_class) 80 @raw_msg.align(data_class.alignment) 81 bytes = @raw_msg.read(data_class.alignment) 82 bytes.unpack1(data_class.format[@raw_msg.endianness]) 83 end
Based on the signature type, retrieve a packet from the buffer and return it. @param signature [Type] @param mode [:plain,:exact] @return [Data::Base]
# File lib/dbus/marshall.rb 90 def do_parse(signature, mode: :plain) 91 # FIXME: better naming for packet vs value 92 packet = nil 93 data_class = Data::BY_TYPE_CODE[signature.sigtype] 94 95 if data_class.nil? 96 raise NotImplementedError, 97 "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})" 98 end 99 100 if data_class.fixed? 101 value = aligned_read_value(data_class) 102 packet = data_class.from_raw(value, mode: mode) 103 elsif data_class.basic? 104 size = aligned_read_value(data_class.size_class) 105 # @raw_msg.align(data_class.alignment) 106 # ^ is not necessary because we've just read a suitably-aligned *size* 107 value = @raw_msg.read(size) 108 nul = @raw_msg.read(1) 109 if nul != "\u0000" 110 raise InvalidPacketException, "#{data_class} is not NUL-terminated" 111 end 112 113 packet = data_class.from_raw(value, mode: mode) 114 else 115 @raw_msg.align(data_class.alignment) 116 case signature.sigtype 117 when Type::STRUCT, Type::DICT_ENTRY 118 values = signature.members.map do |child_sig| 119 do_parse(child_sig, mode: mode) 120 end 121 packet = data_class.from_items(values, mode: mode, type: signature) 122 123 when Type::VARIANT 124 data_sig = do_parse(Data::Signature.type, mode: :exact) # -> Data::Signature 125 types = Type::Parser.new(data_sig.value).parse # -> Array<Type> 126 unless types.size == 1 127 raise InvalidPacketException, "VARIANT must contain 1 value, #{types.size} found" 128 end 129 130 type = types.first 131 value = do_parse(type, mode: mode) 132 packet = data_class.from_items(value, mode: mode, member_type: type) 133 134 when Type::ARRAY 135 array_bytes = aligned_read_value(Data::UInt32) 136 if array_bytes > 67_108_864 137 raise InvalidPacketException, "ARRAY body longer than 64MiB" 138 end 139 140 # needed here because of empty arrays 141 @raw_msg.align(signature.child.alignment) 142 143 items = [] 144 end_pos = @raw_msg.pos + array_bytes 145 while @raw_msg.pos < end_pos 146 item = do_parse(signature.child, mode: mode) 147 items << item 148 end 149 is_hash = signature.child.sigtype == Type::DICT_ENTRY 150 packet = data_class.from_items(items, mode: mode, type: signature, hash: is_hash) 151 end 152 end 153 packet 154 end