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, line 36 def initialize(buffer, endianness) # TODO: this dup can be avoided if we can prove # that an IncompleteBufferException leaves the original *buffer* intact buffer = buffer.dup @raw_msg = RawMessage.new(buffer, endianness) end
Public Instance Methods
after the headers, the body starts 8-aligned
# File lib/dbus/marshall.rb, line 66 def align_body @raw_msg.align(8) end
@return [Integer]
# File lib/dbus/marshall.rb, line 71 def consumed_size @raw_msg.pos 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, line 54 def unmarshall(signature, len = nil, mode: :plain) @raw_msg.want!(len) if len sigtree = Type::Parser.new(signature).parse ret = [] sigtree.each do |elem| ret << do_parse(elem, mode: mode) end ret end
Private Instance Methods
@param data_class [Class] a subclass of Data::Base
(specific?) @return [::Integer,::Float]
# File lib/dbus/marshall.rb, line 79 def aligned_read_value(data_class) @raw_msg.align(data_class.alignment) bytes = @raw_msg.read(data_class.alignment) bytes.unpack1(data_class.format[@raw_msg.endianness]) 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, line 90 def do_parse(signature, mode: :plain) # FIXME: better naming for packet vs value packet = nil data_class = Data::BY_TYPE_CODE[signature.sigtype] if data_class.nil? raise NotImplementedError, "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})" end if data_class.fixed? value = aligned_read_value(data_class) packet = data_class.from_raw(value, mode: mode) elsif data_class.basic? size = aligned_read_value(data_class.size_class) # @raw_msg.align(data_class.alignment) # ^ is not necessary because we've just read a suitably-aligned *size* value = @raw_msg.read(size) nul = @raw_msg.read(1) if nul != "\u0000" raise InvalidPacketException, "#{data_class} is not NUL-terminated" end packet = data_class.from_raw(value, mode: mode) else @raw_msg.align(data_class.alignment) case signature.sigtype when Type::STRUCT, Type::DICT_ENTRY values = signature.members.map do |child_sig| do_parse(child_sig, mode: mode) end packet = data_class.from_items(values, mode: mode, type: signature) when Type::VARIANT data_sig = do_parse(Data::Signature.type, mode: :exact) # -> Data::Signature types = Type::Parser.new(data_sig.value).parse # -> Array<Type> unless types.size == 1 raise InvalidPacketException, "VARIANT must contain 1 value, #{types.size} found" end type = types.first value = do_parse(type, mode: mode) packet = data_class.from_items(value, mode: mode, member_type: type) when Type::ARRAY array_bytes = aligned_read_value(Data::UInt32) if array_bytes > 67_108_864 raise InvalidPacketException, "ARRAY body longer than 64MiB" end # needed here because of empty arrays @raw_msg.align(signature.child.alignment) items = [] end_pos = @raw_msg.pos + array_bytes while @raw_msg.pos < end_pos item = do_parse(signature.child, mode: mode) items << item end is_hash = signature.child.sigtype == Type::DICT_ENTRY packet = data_class.from_items(items, mode: mode, type: signature, hash: is_hash) end end packet end