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