ruby-dbus 0.16.0 → 0.18.1
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 +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
|