ruby-dbus 0.16.0 → 0.18.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/NEWS.md +46 -0
- data/README.md +3 -5
- data/Rakefile +18 -8
- data/VERSION +1 -1
- data/doc/Reference.md +94 -4
- data/examples/doc/_extract_examples +7 -0
- data/examples/gdbus/gdbus +31 -24
- data/examples/no-introspect/nm-test.rb +2 -0
- data/examples/no-introspect/tracker-test.rb +3 -1
- data/examples/rhythmbox/playpause.rb +2 -1
- data/examples/service/call_service.rb +2 -1
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +1 -1
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +2 -1
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +1 -0
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +20 -15
- data/lib/dbus/bus.rb +126 -74
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/data.rb +725 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +91 -30
- data/lib/dbus/logger.rb +3 -1
- data/lib/dbus/marshall.rb +228 -294
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +44 -37
- data/lib/dbus/message_queue.rb +16 -10
- data/lib/dbus/object.rb +296 -24
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/proxy_object.rb +22 -1
- data/lib/dbus/proxy_object_factory.rb +11 -7
- data/lib/dbus/proxy_object_interface.rb +26 -21
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +182 -80
- data/lib/dbus/xml.rb +28 -17
- data/lib/dbus.rb +13 -7
- data/ruby-dbus.gemspec +7 -3
- data/spec/async_spec.rb +2 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +2 -0
- data/spec/bus_driver_spec.rb +2 -0
- data/spec/bus_name_spec.rb +3 -1
- data/spec/bus_spec.rb +2 -0
- data/spec/byte_array_spec.rb +2 -0
- data/spec/client_robustness_spec.rb +4 -2
- data/spec/data/marshall.yaml +1639 -0
- data/spec/data_spec.rb +298 -0
- data/spec/err_msg_spec.rb +2 -0
- data/spec/introspect_xml_parser_spec.rb +2 -0
- data/spec/introspection_spec.rb +2 -0
- data/spec/main_loop_spec.rb +3 -1
- data/spec/node_spec.rb +23 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/packet_marshaller_spec.rb +34 -0
- data/spec/packet_unmarshaller_spec.rb +262 -0
- data/spec/property_spec.rb +88 -5
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/server_robustness_spec.rb +2 -0
- data/spec/server_spec.rb +2 -0
- data/spec/service_newapi.rb +39 -70
- data/spec/session_bus_spec.rb +3 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +5 -3
- data/spec/spec_helper.rb +35 -9
- data/spec/thread_safety_spec.rb +2 -0
- data/spec/tools/dbus-limited-session.conf +4 -0
- data/spec/type_spec.rb +69 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- metadata +32 -10
data/lib/dbus/marshall.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# dbus.rb - Module containing the low-level D-Bus implementation
|
2
4
|
#
|
3
5
|
# This file is part of the ruby-dbus project
|
@@ -10,6 +12,8 @@
|
|
10
12
|
|
11
13
|
require "socket"
|
12
14
|
|
15
|
+
require_relative "../dbus/type"
|
16
|
+
|
13
17
|
# = D-Bus main module
|
14
18
|
#
|
15
19
|
# Module containing all the D-Bus modules and classes.
|
@@ -21,211 +25,132 @@ module DBus
|
|
21
25
|
# = D-Bus packet unmarshaller class
|
22
26
|
#
|
23
27
|
# Class that handles the conversion (unmarshalling) of payload data
|
24
|
-
# to
|
28
|
+
# to #{::Object}s (in **plain** mode) or to {Data::Base} (in **exact** mode)
|
29
|
+
#
|
30
|
+
# Spelling note: this codebase always uses a double L
|
31
|
+
# in the "marshall" word and its inflections.
|
25
32
|
class PacketUnmarshaller
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# Used to kown what part of the buffer has been consumed by unmarshalling.
|
30
|
-
# FIXME: Maybe should be accessed with a "consumed_size" method.
|
31
|
-
attr_reader :idx
|
32
|
-
|
33
|
-
# Create a new unmarshaller for the given data _buffer_ and _endianness_.
|
33
|
+
# Create a new unmarshaller for the given data *buffer*.
|
34
|
+
# @param buffer [String]
|
35
|
+
# @param endianness [:little,:big]
|
34
36
|
def initialize(buffer, endianness)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@uint16 = "n"
|
40
|
-
@double = "G"
|
41
|
-
elsif @endianness == LIL_END
|
42
|
-
@uint32 = "V"
|
43
|
-
@uint16 = "v"
|
44
|
-
@double = "E"
|
45
|
-
else
|
46
|
-
raise InvalidPacketException, "Incorrect endianness #{@endianness}"
|
47
|
-
end
|
48
|
-
@idx = 0
|
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)
|
49
41
|
end
|
50
42
|
|
51
43
|
# Unmarshall the buffer for a given _signature_ and length _len_.
|
52
|
-
# Return an array of unmarshalled objects
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
44
|
+
# Return an array of unmarshalled objects.
|
45
|
+
# @param signature [Signature]
|
46
|
+
# @param len [Integer,nil] if given, and there is not enough data
|
47
|
+
# in the buffer, raise {IncompleteBufferException}
|
48
|
+
# @param mode [:plain,:exact]
|
49
|
+
# @return [Array<::Object,DBus::Data::Base>]
|
50
|
+
# Objects in `:plain` mode, {DBus::Data::Base} in `:exact` mode
|
51
|
+
# The array size corresponds to the number of types in *signature*.
|
52
|
+
# @raise IncompleteBufferException
|
53
|
+
# @raise InvalidPacketException
|
54
|
+
def unmarshall(signature, len = nil, mode: :plain)
|
55
|
+
@raw_msg.want!(len) if len
|
56
|
+
|
59
57
|
sigtree = Type::Parser.new(signature).parse
|
60
58
|
ret = []
|
61
59
|
sigtree.each do |elem|
|
62
|
-
ret << do_parse(elem)
|
60
|
+
ret << do_parse(elem, mode: mode)
|
63
61
|
end
|
64
62
|
ret
|
65
63
|
end
|
66
64
|
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
case a
|
71
|
-
when 1
|
72
|
-
nil
|
73
|
-
when 2, 4, 8
|
74
|
-
bits = a - 1
|
75
|
-
@idx = @idx + bits & ~bits
|
76
|
-
raise IncompleteBufferException if @idx > @buffy.bytesize
|
77
|
-
else
|
78
|
-
raise "Unsupported alignment #{a}"
|
79
|
-
end
|
65
|
+
# after the headers, the body starts 8-aligned
|
66
|
+
def align_body
|
67
|
+
@raw_msg.align(8)
|
80
68
|
end
|
81
69
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
# Retrieve the next _nbytes_ number of bytes from the buffer.
|
88
|
-
def read(nbytes)
|
89
|
-
raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
|
90
|
-
ret = @buffy.slice(@idx, nbytes)
|
91
|
-
@idx += nbytes
|
92
|
-
ret
|
70
|
+
# @return [Integer]
|
71
|
+
def consumed_size
|
72
|
+
@raw_msg.pos
|
93
73
|
end
|
94
74
|
|
95
|
-
|
96
|
-
# Return the string.
|
97
|
-
def read_string
|
98
|
-
align(4)
|
99
|
-
str_sz = read(4).unpack(@uint32)[0]
|
100
|
-
ret = @buffy.slice(@idx, str_sz)
|
101
|
-
raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
|
102
|
-
@idx += str_sz
|
103
|
-
if @buffy[@idx].ord != 0
|
104
|
-
raise InvalidPacketException, "String is not nul-terminated"
|
105
|
-
end
|
106
|
-
@idx += 1
|
107
|
-
# no exception, see check above
|
108
|
-
ret
|
109
|
-
end
|
75
|
+
private
|
110
76
|
|
111
|
-
#
|
112
|
-
#
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
@idx += str_sz
|
118
|
-
if @buffy[@idx].ord != 0
|
119
|
-
raise InvalidPacketException, "Type is not nul-terminated"
|
120
|
-
end
|
121
|
-
@idx += 1
|
122
|
-
# no exception, see check above
|
123
|
-
ret
|
77
|
+
# @param data_class [Class] a subclass of Data::Base (specific?)
|
78
|
+
# @return [::Integer,::Float]
|
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])
|
124
83
|
end
|
125
84
|
|
126
85
|
# Based on the _signature_ type, retrieve a packet from the buffer
|
127
86
|
# and return it.
|
128
|
-
|
87
|
+
# @param signature [Type]
|
88
|
+
# @param mode [:plain,:exact]
|
89
|
+
# @return [Data::Base]
|
90
|
+
def do_parse(signature, mode: :plain)
|
91
|
+
# FIXME: better naming for packet vs value
|
129
92
|
packet = nil
|
130
|
-
|
131
|
-
when Type::BYTE
|
132
|
-
packet = read(1).unpack("C")[0]
|
133
|
-
when Type::UINT16
|
134
|
-
align(2)
|
135
|
-
packet = read(2).unpack(@uint16)[0]
|
136
|
-
when Type::INT16
|
137
|
-
align(2)
|
138
|
-
packet = read(2).unpack(@uint16)[0]
|
139
|
-
if (packet & 0x8000) != 0
|
140
|
-
packet -= 0x10000
|
141
|
-
end
|
142
|
-
when Type::UINT32, Type::UNIX_FD
|
143
|
-
align(4)
|
144
|
-
packet = read(4).unpack(@uint32)[0]
|
145
|
-
when Type::INT32
|
146
|
-
align(4)
|
147
|
-
packet = read(4).unpack(@uint32)[0]
|
148
|
-
if (packet & 0x80000000) != 0
|
149
|
-
packet -= 0x100000000
|
150
|
-
end
|
151
|
-
when Type::UINT64
|
152
|
-
align(8)
|
153
|
-
packet_l = read(4).unpack(@uint32)[0]
|
154
|
-
packet_h = read(4).unpack(@uint32)[0]
|
155
|
-
packet = if @endianness == LIL_END
|
156
|
-
packet_l + packet_h * 2**32
|
157
|
-
else
|
158
|
-
packet_l * 2**32 + packet_h
|
159
|
-
end
|
160
|
-
when Type::INT64
|
161
|
-
align(8)
|
162
|
-
packet_l = read(4).unpack(@uint32)[0]
|
163
|
-
packet_h = read(4).unpack(@uint32)[0]
|
164
|
-
packet = if @endianness == LIL_END
|
165
|
-
packet_l + packet_h * 2**32
|
166
|
-
else
|
167
|
-
packet_l * 2**32 + packet_h
|
168
|
-
end
|
169
|
-
if (packet & 0x8000000000000000) != 0
|
170
|
-
packet -= 0x10000000000000000
|
171
|
-
end
|
172
|
-
when Type::DOUBLE
|
173
|
-
align(8)
|
174
|
-
packet = read(8).unpack(@double)[0]
|
175
|
-
when Type::BOOLEAN
|
176
|
-
align(4)
|
177
|
-
v = read(4).unpack(@uint32)[0]
|
178
|
-
raise InvalidPacketException if ![0, 1].member?(v)
|
179
|
-
packet = (v == 1)
|
180
|
-
when Type::ARRAY
|
181
|
-
align(4)
|
182
|
-
# checks please
|
183
|
-
array_sz = read(4).unpack(@uint32)[0]
|
184
|
-
raise InvalidPacketException if array_sz > 67_108_864
|
185
|
-
|
186
|
-
align(signature.child.alignment)
|
187
|
-
raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize
|
188
|
-
|
189
|
-
packet = []
|
190
|
-
start_idx = @idx
|
191
|
-
while @idx - start_idx < array_sz
|
192
|
-
packet << do_parse(signature.child)
|
193
|
-
end
|
93
|
+
data_class = Data::BY_TYPE_CODE[signature.sigtype]
|
194
94
|
|
195
|
-
|
196
|
-
packet = Hash[packet]
|
197
|
-
end
|
198
|
-
when Type::STRUCT
|
199
|
-
align(8)
|
200
|
-
packet = []
|
201
|
-
signature.members.each do |elem|
|
202
|
-
packet << do_parse(elem)
|
203
|
-
end
|
204
|
-
when Type::VARIANT
|
205
|
-
string = read_signature
|
206
|
-
# error checking please
|
207
|
-
sig = Type::Parser.new(string).parse[0]
|
208
|
-
align(sig.alignment)
|
209
|
-
packet = do_parse(sig)
|
210
|
-
when Type::OBJECT_PATH
|
211
|
-
packet = read_string
|
212
|
-
when Type::STRING
|
213
|
-
packet = read_string
|
214
|
-
packet.force_encoding("UTF-8")
|
215
|
-
when Type::SIGNATURE
|
216
|
-
packet = read_signature
|
217
|
-
when Type::DICT_ENTRY
|
218
|
-
align(8)
|
219
|
-
key = do_parse(signature.members[0])
|
220
|
-
value = do_parse(signature.members[1])
|
221
|
-
packet = [key, value]
|
222
|
-
else
|
95
|
+
if data_class.nil?
|
223
96
|
raise NotImplementedError,
|
224
97
|
"sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
|
225
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
|
+
value = @raw_msg.read(size)
|
106
|
+
nul = @raw_msg.read(1)
|
107
|
+
if nul != "\u0000"
|
108
|
+
raise InvalidPacketException, "#{data_class} is not NUL-terminated"
|
109
|
+
end
|
110
|
+
|
111
|
+
packet = data_class.from_raw(value, mode: mode)
|
112
|
+
else
|
113
|
+
@raw_msg.align(data_class.alignment)
|
114
|
+
case signature.sigtype
|
115
|
+
when Type::STRUCT, Type::DICT_ENTRY
|
116
|
+
values = signature.members.map do |child_sig|
|
117
|
+
do_parse(child_sig, mode: mode)
|
118
|
+
end
|
119
|
+
packet = data_class.from_items(values, mode: mode, member_types: signature.members)
|
120
|
+
|
121
|
+
when Type::VARIANT
|
122
|
+
data_sig = do_parse(Data::Signature.type, mode: :exact) # -> Data::Signature
|
123
|
+
types = Type::Parser.new(data_sig.value).parse # -> Array<Type>
|
124
|
+
unless types.size == 1
|
125
|
+
raise InvalidPacketException, "VARIANT must contain 1 value, #{types.size} found"
|
126
|
+
end
|
127
|
+
|
128
|
+
type = types.first
|
129
|
+
value = do_parse(type, mode: mode)
|
130
|
+
packet = data_class.from_items(value, mode: mode, member_type: type)
|
131
|
+
|
132
|
+
when Type::ARRAY
|
133
|
+
array_bytes = aligned_read_value(Data::UInt32)
|
134
|
+
if array_bytes > 67_108_864
|
135
|
+
raise InvalidPacketException, "ARRAY body longer than 64MiB"
|
136
|
+
end
|
137
|
+
|
138
|
+
# needed here because of empty arrays
|
139
|
+
@raw_msg.align(signature.child.alignment)
|
140
|
+
|
141
|
+
items = []
|
142
|
+
end_pos = @raw_msg.pos + array_bytes
|
143
|
+
while @raw_msg.pos < end_pos
|
144
|
+
item = do_parse(signature.child, mode: mode)
|
145
|
+
items << item
|
146
|
+
end
|
147
|
+
is_hash = signature.child.sigtype == Type::DICT_ENTRY
|
148
|
+
packet = data_class.from_items(items, mode: mode, member_type: signature.child, hash: is_hash)
|
149
|
+
end
|
150
|
+
end
|
226
151
|
packet
|
227
|
-
end
|
228
|
-
end
|
152
|
+
end
|
153
|
+
end
|
229
154
|
|
230
155
|
# D-Bus packet marshaller class
|
231
156
|
#
|
@@ -234,40 +159,35 @@ module DBus
|
|
234
159
|
class PacketMarshaller
|
235
160
|
# The current or result packet.
|
236
161
|
# FIXME: allow access only when marshalling is finished
|
162
|
+
# @return [String]
|
237
163
|
attr_reader :packet
|
238
164
|
|
165
|
+
# @return [:little,:big]
|
166
|
+
attr_reader :endianness
|
167
|
+
|
239
168
|
# Create a new marshaller, setting the current packet to the
|
240
169
|
# empty packet.
|
241
|
-
def initialize(offset = 0)
|
170
|
+
def initialize(offset = 0, endianness: HOST_ENDIANNESS)
|
171
|
+
@endianness = endianness
|
242
172
|
@packet = ""
|
243
173
|
@offset = offset # for correct alignment of nested marshallers
|
244
174
|
end
|
245
175
|
|
246
|
-
# Round
|
247
|
-
def num_align(
|
248
|
-
case
|
176
|
+
# Round _num_ up to the specified power of two, _alignment_
|
177
|
+
def num_align(num, alignment)
|
178
|
+
case alignment
|
249
179
|
when 1, 2, 4, 8
|
250
|
-
bits =
|
251
|
-
|
180
|
+
bits = alignment - 1
|
181
|
+
num + bits & ~bits
|
252
182
|
else
|
253
|
-
raise "Unsupported alignment"
|
183
|
+
raise ArgumentError, "Unsupported alignment #{alignment}"
|
254
184
|
end
|
255
185
|
end
|
256
186
|
|
257
|
-
# Align the buffer with NULL (\0) bytes on a byte length of
|
258
|
-
def align(
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
# Append the the string _str_ itself to the packet.
|
263
|
-
def append_string(str)
|
264
|
-
align(4)
|
265
|
-
@packet += [str.bytesize].pack("L") + [str].pack("Z*")
|
266
|
-
end
|
267
|
-
|
268
|
-
# Append the the signature _signature_ itself to the packet.
|
269
|
-
def append_signature(str)
|
270
|
-
@packet += str.bytesize.chr + str + "\0"
|
187
|
+
# Align the buffer with NULL (\0) bytes on a byte length of _alignment_.
|
188
|
+
def align(alignment)
|
189
|
+
pad_count = num_align(@offset + @packet.bytesize, alignment) - @offset
|
190
|
+
@packet = @packet.ljust(pad_count, 0.chr)
|
271
191
|
end
|
272
192
|
|
273
193
|
# Append the array type _type_ to the packet and allow for appending
|
@@ -282,7 +202,9 @@ module DBus
|
|
282
202
|
yield
|
283
203
|
sz = @packet.bytesize - contentidx
|
284
204
|
raise InvalidPacketException if sz > 67_108_864
|
285
|
-
|
205
|
+
|
206
|
+
sz_data = Data::UInt32.new(sz)
|
207
|
+
@packet[sizeidx...sizeidx + 4] = sz_data.marshall(endianness)
|
286
208
|
end
|
287
209
|
|
288
210
|
# Align and allow for appending struct fields.
|
@@ -294,110 +216,122 @@ module DBus
|
|
294
216
|
# Append a value _val_ to the packet based on its _type_.
|
295
217
|
#
|
296
218
|
# Host native endianness is used, declared in Message#marshall
|
219
|
+
#
|
220
|
+
# @param type [SingleCompleteType] (or Integer or {Type})
|
221
|
+
# @param val [::Object]
|
297
222
|
def append(type, val)
|
298
223
|
raise TypeException, "Cannot send nil" if val.nil?
|
299
224
|
|
300
225
|
type = type.chr if type.is_a?(Integer)
|
301
226
|
type = Type::Parser.new(type).parse[0] if type.is_a?(String)
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
align(
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
@packet += [val].pack("
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
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
|
227
|
+
# type is [Type] now
|
228
|
+
data_class = Data::BY_TYPE_CODE[type.sigtype]
|
229
|
+
if data_class.nil?
|
230
|
+
raise NotImplementedError,
|
231
|
+
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
232
|
+
end
|
233
|
+
|
234
|
+
if data_class.fixed?
|
235
|
+
align(data_class.alignment)
|
236
|
+
data = data_class.new(val)
|
237
|
+
@packet += data.marshall(endianness)
|
238
|
+
elsif data_class.basic?
|
239
|
+
val = val.value if val.is_a?(Data::Basic)
|
240
|
+
align(data_class.size_class.alignment)
|
241
|
+
size_data = data_class.size_class.new(val.bytesize)
|
242
|
+
@packet += size_data.marshall(endianness)
|
243
|
+
# Z* makes a binary string, as opposed to interpolation
|
244
|
+
@packet += [val].pack("Z*")
|
245
|
+
else
|
246
|
+
case type.sigtype
|
247
|
+
|
248
|
+
when Type::VARIANT
|
249
|
+
append_variant(val)
|
250
|
+
when Type::ARRAY
|
251
|
+
append_array(type.child, val)
|
252
|
+
when Type::STRUCT, Type::DICT_ENTRY
|
253
|
+
val = val.value if val.is_a?(Data::Struct)
|
254
|
+
unless val.is_a?(Array) || val.is_a?(Struct)
|
255
|
+
type_name = Type::TYPE_MAPPING[type.sigtype].first
|
256
|
+
raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
|
257
|
+
end
|
258
|
+
|
259
|
+
if type.sigtype == Type::DICT_ENTRY && val.size != 2
|
260
|
+
raise TypeException, "DICT_ENTRY expects a pair"
|
261
|
+
end
|
262
|
+
|
263
|
+
if type.members.size != val.size
|
264
|
+
type_name = Type::TYPE_MAPPING[type.sigtype].first
|
265
|
+
raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}"
|
266
|
+
end
|
267
|
+
|
268
|
+
struct do
|
269
|
+
type.members.zip(val).each do |t, v|
|
270
|
+
append(t, v)
|
351
271
|
end
|
352
272
|
end
|
273
|
+
else
|
274
|
+
raise NotImplementedError,
|
275
|
+
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
353
276
|
end
|
354
|
-
|
355
|
-
|
356
|
-
vartype = Type::Parser.new(vartype).parse[0]
|
357
|
-
end
|
277
|
+
end
|
278
|
+
end
|
358
279
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
array(type.child) do
|
378
|
-
val.each do |elem|
|
379
|
-
append(type.child, elem)
|
280
|
+
def append_variant(val)
|
281
|
+
vartype = nil
|
282
|
+
if val.is_a?(DBus::Data::Base)
|
283
|
+
vartype = val.type # FIXME: box or unbox another variant?
|
284
|
+
vardata = val.value
|
285
|
+
elsif val.is_a?(Array) && val.size == 2
|
286
|
+
case val[0]
|
287
|
+
when Type
|
288
|
+
vartype, vardata = val
|
289
|
+
# Ambiguous but easy to use, because Type
|
290
|
+
# cannot construct "as" "a{sv}" easily
|
291
|
+
when String
|
292
|
+
begin
|
293
|
+
parsed = Type::Parser.new(val[0]).parse
|
294
|
+
vartype = parsed[0] if parsed.size == 1
|
295
|
+
vardata = val[1]
|
296
|
+
rescue Type::SignatureException
|
297
|
+
# no assignment
|
380
298
|
end
|
381
299
|
end
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
300
|
+
end
|
301
|
+
if vartype.nil?
|
302
|
+
vartype, vardata = PacketMarshaller.make_variant(val)
|
303
|
+
vartype = Type::Parser.new(vartype).parse[0]
|
304
|
+
end
|
305
|
+
|
306
|
+
append(Data::Signature.type, vartype.to_s)
|
307
|
+
align(vartype.alignment)
|
308
|
+
sub = PacketMarshaller.new(@offset + @packet.bytesize, endianness: endianness)
|
309
|
+
sub.append(vartype, vardata)
|
310
|
+
@packet += sub.packet
|
311
|
+
end
|
312
|
+
|
313
|
+
# @param child_type [Type]
|
314
|
+
def append_array(child_type, val)
|
315
|
+
if val.is_a?(Hash)
|
316
|
+
raise TypeException, "Expected an Array but got a Hash" if child_type.sigtype != Type::DICT_ENTRY
|
317
|
+
|
318
|
+
# Damn ruby rocks here
|
319
|
+
val = val.to_a
|
320
|
+
end
|
321
|
+
# If string is recieved and ay is expected, explode the string
|
322
|
+
if val.is_a?(String) && child_type.sigtype == Type::BYTE
|
323
|
+
val = val.bytes
|
324
|
+
end
|
325
|
+
if !val.is_a?(Enumerable)
|
326
|
+
raise TypeException, "Expected an Enumerable of #{child_type.inspect} but got a #{val.class}"
|
327
|
+
end
|
328
|
+
|
329
|
+
array(child_type) do
|
330
|
+
val.each do |elem|
|
331
|
+
append(child_type, elem)
|
395
332
|
end
|
396
|
-
else
|
397
|
-
raise NotImplementedError,
|
398
|
-
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
399
333
|
end
|
400
|
-
end
|
334
|
+
end
|
401
335
|
|
402
336
|
# Make a [signature, value] pair for a variant
|
403
337
|
def self.make_variant(value)
|
@@ -422,12 +356,12 @@ module DBus
|
|
422
356
|
["s", value.to_str]
|
423
357
|
elsif value.respond_to? :to_int
|
424
358
|
i = value.to_int
|
425
|
-
if -2_147_483_648
|
359
|
+
if (-2_147_483_648...2_147_483_648).cover?(i)
|
426
360
|
["i", i]
|
427
361
|
else
|
428
362
|
["x", i]
|
429
363
|
end
|
430
364
|
end
|
431
365
|
end
|
432
|
-
end
|
433
|
-
end
|
366
|
+
end
|
367
|
+
end
|