ruby-dbus 0.16.0 → 0.18.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +160 -0
- data/README.md +3 -5
- data/Rakefile +18 -8
- data/VERSION +1 -1
- data/doc/Reference.md +106 -7
- 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 +123 -75
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/data.rb +821 -0
- data/lib/dbus/emits_changed_signal.rb +83 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +132 -31
- data/lib/dbus/logger.rb +3 -1
- data/lib/dbus/marshall.rb +247 -296
- 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 +358 -24
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/proxy_object.rb +22 -1
- data/lib/dbus/proxy_object_factory.rb +13 -7
- data/lib/dbus/proxy_object_interface.rb +63 -30
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +318 -86
- data/lib/dbus/xml.rb +32 -17
- data/lib/dbus.rb +14 -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 +1667 -0
- data/spec/data_spec.rb +673 -0
- data/spec/emits_changed_signal_spec.rb +58 -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/object_spec.rb +138 -0
- data/spec/packet_marshaller_spec.rb +41 -0
- data/spec/packet_unmarshaller_spec.rb +248 -0
- data/spec/property_spec.rb +192 -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 +70 -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 +37 -9
- data/spec/thread_safety_spec.rb +2 -0
- data/spec/tools/dbus-limited-session.conf +4 -0
- data/spec/type_spec.rb +214 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +34 -8
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,134 @@ 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
|
+
# @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
|
226
153
|
packet
|
227
|
-
end
|
228
|
-
end
|
154
|
+
end
|
155
|
+
end
|
229
156
|
|
230
157
|
# D-Bus packet marshaller class
|
231
158
|
#
|
@@ -234,40 +161,35 @@ module DBus
|
|
234
161
|
class PacketMarshaller
|
235
162
|
# The current or result packet.
|
236
163
|
# FIXME: allow access only when marshalling is finished
|
164
|
+
# @return [String]
|
237
165
|
attr_reader :packet
|
238
166
|
|
167
|
+
# @return [:little,:big]
|
168
|
+
attr_reader :endianness
|
169
|
+
|
239
170
|
# Create a new marshaller, setting the current packet to the
|
240
171
|
# empty packet.
|
241
|
-
def initialize(offset = 0)
|
172
|
+
def initialize(offset = 0, endianness: HOST_ENDIANNESS)
|
173
|
+
@endianness = endianness
|
242
174
|
@packet = ""
|
243
175
|
@offset = offset # for correct alignment of nested marshallers
|
244
176
|
end
|
245
177
|
|
246
|
-
# Round
|
247
|
-
def num_align(
|
248
|
-
case
|
178
|
+
# Round _num_ up to the specified power of two, _alignment_
|
179
|
+
def num_align(num, alignment)
|
180
|
+
case alignment
|
249
181
|
when 1, 2, 4, 8
|
250
|
-
bits =
|
251
|
-
|
182
|
+
bits = alignment - 1
|
183
|
+
num + bits & ~bits
|
252
184
|
else
|
253
|
-
raise "Unsupported alignment"
|
185
|
+
raise ArgumentError, "Unsupported alignment #{alignment}"
|
254
186
|
end
|
255
187
|
end
|
256
188
|
|
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"
|
189
|
+
# Align the buffer with NULL (\0) bytes on a byte length of _alignment_.
|
190
|
+
def align(alignment)
|
191
|
+
pad_count = num_align(@offset + @packet.bytesize, alignment) - @offset
|
192
|
+
@packet = @packet.ljust(pad_count, 0.chr)
|
271
193
|
end
|
272
194
|
|
273
195
|
# Append the array type _type_ to the packet and allow for appending
|
@@ -282,7 +204,9 @@ module DBus
|
|
282
204
|
yield
|
283
205
|
sz = @packet.bytesize - contentidx
|
284
206
|
raise InvalidPacketException if sz > 67_108_864
|
285
|
-
|
207
|
+
|
208
|
+
sz_data = Data::UInt32.new(sz)
|
209
|
+
@packet[sizeidx...sizeidx + 4] = sz_data.marshall(endianness)
|
286
210
|
end
|
287
211
|
|
288
212
|
# Align and allow for appending struct fields.
|
@@ -294,110 +218,129 @@ module DBus
|
|
294
218
|
# Append a value _val_ to the packet based on its _type_.
|
295
219
|
#
|
296
220
|
# Host native endianness is used, declared in Message#marshall
|
221
|
+
#
|
222
|
+
# @param type [SingleCompleteType] (or Integer or {Type})
|
223
|
+
# @param val [::Object]
|
297
224
|
def append(type, val)
|
298
225
|
raise TypeException, "Cannot send nil" if val.nil?
|
299
226
|
|
300
227
|
type = type.chr if type.is_a?(Integer)
|
301
228
|
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
|
-
|
347
|
-
vartype = parsed[0] if parsed.size == 1
|
348
|
-
vardata = val[1]
|
349
|
-
rescue Type::SignatureException
|
350
|
-
# no assignment
|
229
|
+
# type is [Type] now
|
230
|
+
data_class = Data::BY_TYPE_CODE[type.sigtype]
|
231
|
+
if data_class.nil?
|
232
|
+
raise NotImplementedError,
|
233
|
+
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
234
|
+
end
|
235
|
+
|
236
|
+
if data_class.fixed?
|
237
|
+
align(data_class.alignment)
|
238
|
+
data = data_class.new(val)
|
239
|
+
@packet += data.marshall(endianness)
|
240
|
+
elsif data_class.basic?
|
241
|
+
val = val.value if val.is_a?(Data::Basic)
|
242
|
+
align(data_class.size_class.alignment)
|
243
|
+
size_data = data_class.size_class.new(val.bytesize)
|
244
|
+
@packet += size_data.marshall(endianness)
|
245
|
+
# Z* makes a binary string, as opposed to interpolation
|
246
|
+
@packet += [val].pack("Z*")
|
247
|
+
else
|
248
|
+
case type.sigtype
|
249
|
+
|
250
|
+
when Type::VARIANT
|
251
|
+
append_variant(val)
|
252
|
+
when Type::ARRAY
|
253
|
+
val = val.exact_value if val.is_a?(Data::Array)
|
254
|
+
append_array(type.child, val)
|
255
|
+
when Type::STRUCT, Type::DICT_ENTRY
|
256
|
+
val = val.exact_value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry)
|
257
|
+
unless val.is_a?(Array) || val.is_a?(Struct)
|
258
|
+
type_name = Type::TYPE_MAPPING[type.sigtype].first
|
259
|
+
raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
|
260
|
+
end
|
261
|
+
|
262
|
+
if type.sigtype == Type::DICT_ENTRY && val.size != 2
|
263
|
+
raise TypeException, "DICT_ENTRY expects a pair"
|
264
|
+
end
|
265
|
+
|
266
|
+
if type.members.size != val.size
|
267
|
+
type_name = Type::TYPE_MAPPING[type.sigtype].first
|
268
|
+
raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}"
|
269
|
+
end
|
270
|
+
|
271
|
+
struct do
|
272
|
+
type.members.zip(val).each do |t, v|
|
273
|
+
append(t, v)
|
351
274
|
end
|
352
275
|
end
|
276
|
+
else
|
277
|
+
raise NotImplementedError,
|
278
|
+
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
353
279
|
end
|
354
|
-
|
355
|
-
|
356
|
-
vartype = Type::Parser.new(vartype).parse[0]
|
357
|
-
end
|
280
|
+
end
|
281
|
+
end
|
358
282
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
283
|
+
def append_variant(val)
|
284
|
+
vartype = nil
|
285
|
+
if val.is_a?(DBus::Data::Variant)
|
286
|
+
vartype = val.member_type
|
287
|
+
vardata = val.exact_value
|
288
|
+
elsif val.is_a?(DBus::Data::Container)
|
289
|
+
vartype = val.type
|
290
|
+
vardata = val.exact_value
|
291
|
+
elsif val.is_a?(DBus::Data::Base)
|
292
|
+
vartype = val.type
|
293
|
+
vardata = val.value
|
294
|
+
elsif val.is_a?(Array) && val.size == 2
|
295
|
+
case val[0]
|
296
|
+
when Type
|
297
|
+
vartype, vardata = val
|
298
|
+
# Ambiguous but easy to use, because Type
|
299
|
+
# cannot construct "as" "a{sv}" easily
|
300
|
+
when String
|
301
|
+
begin
|
302
|
+
parsed = Type::Parser.new(val[0]).parse
|
303
|
+
vartype = parsed[0] if parsed.size == 1
|
304
|
+
vardata = val[1]
|
305
|
+
rescue Type::SignatureException
|
306
|
+
# no assignment
|
380
307
|
end
|
381
308
|
end
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
309
|
+
end
|
310
|
+
if vartype.nil?
|
311
|
+
vartype, vardata = PacketMarshaller.make_variant(val)
|
312
|
+
vartype = Type::Parser.new(vartype).parse[0]
|
313
|
+
end
|
314
|
+
|
315
|
+
append(Data::Signature.type, vartype.to_s)
|
316
|
+
align(vartype.alignment)
|
317
|
+
sub = PacketMarshaller.new(@offset + @packet.bytesize, endianness: endianness)
|
318
|
+
sub.append(vartype, vardata)
|
319
|
+
@packet += sub.packet
|
320
|
+
end
|
321
|
+
|
322
|
+
# @param child_type [Type]
|
323
|
+
def append_array(child_type, val)
|
324
|
+
if val.is_a?(Hash)
|
325
|
+
raise TypeException, "Expected an Array but got a Hash" if child_type.sigtype != Type::DICT_ENTRY
|
326
|
+
|
327
|
+
# Damn ruby rocks here
|
328
|
+
val = val.to_a
|
329
|
+
end
|
330
|
+
# If string is recieved and ay is expected, explode the string
|
331
|
+
if val.is_a?(String) && child_type.sigtype == Type::BYTE
|
332
|
+
val = val.bytes
|
333
|
+
end
|
334
|
+
if !val.is_a?(Enumerable)
|
335
|
+
raise TypeException, "Expected an Enumerable of #{child_type.inspect} but got a #{val.class}"
|
336
|
+
end
|
337
|
+
|
338
|
+
array(child_type) do
|
339
|
+
val.each do |elem|
|
340
|
+
append(child_type, elem)
|
395
341
|
end
|
396
|
-
else
|
397
|
-
raise NotImplementedError,
|
398
|
-
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
|
399
342
|
end
|
400
|
-
end
|
343
|
+
end
|
401
344
|
|
402
345
|
# Make a [signature, value] pair for a variant
|
403
346
|
def self.make_variant(value)
|
@@ -417,17 +360,25 @@ module DBus
|
|
417
360
|
elsif value.is_a? Hash
|
418
361
|
h = {}
|
419
362
|
value.each_key { |k| h[k] = make_variant(value[k]) }
|
420
|
-
|
363
|
+
key_type = if value.empty?
|
364
|
+
"s"
|
365
|
+
else
|
366
|
+
t, = make_variant(value.first.first)
|
367
|
+
t
|
368
|
+
end
|
369
|
+
["a{#{key_type}v}", h]
|
421
370
|
elsif value.respond_to? :to_str
|
422
371
|
["s", value.to_str]
|
423
372
|
elsif value.respond_to? :to_int
|
424
373
|
i = value.to_int
|
425
|
-
if
|
374
|
+
if Data::Int32.range.cover?(i)
|
426
375
|
["i", i]
|
427
|
-
|
376
|
+
elsif Data::Int64.range.cover?(i)
|
428
377
|
["x", i]
|
378
|
+
else
|
379
|
+
["t", i]
|
429
380
|
end
|
430
381
|
end
|
431
382
|
end
|
432
|
-
end
|
433
|
-
end
|
383
|
+
end
|
384
|
+
end
|