# 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

new(buffer, endianness) click to toggle source

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

align_body() click to toggle source

after the headers, the body starts 8-aligned

       # File lib/dbus/marshall.rb
    66 def align_body
    67   @raw_msg.align(8)
    68 end

consumed_size() click to toggle source

@return [Integer]

       # File lib/dbus/marshall.rb
    71 def consumed_size
    72   @raw_msg.pos
    73 end

unmarshall(signature, len = nil, mode: :plain) click to toggle source

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

aligned_read_value(data_class) click to toggle source

@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

do_parse(signature, mode: :plain) click to toggle source

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