/src/irssi/subprojects/glib-2.74.3/glib/gvariant-serialiser.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright © 2007, 2008 Ryan Lortie |
3 | | * Copyright © 2010 Codethink Limited |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
19 | | * |
20 | | * Author: Ryan Lortie <desrt@desrt.ca> |
21 | | */ |
22 | | |
23 | | /* Prologue {{{1 */ |
24 | | #include "config.h" |
25 | | |
26 | | #include "gvariant-serialiser.h" |
27 | | |
28 | | #include <glib/gvariant-internal.h> |
29 | | #include <glib/gtestutils.h> |
30 | | #include <glib/gstrfuncs.h> |
31 | | #include <glib/gtypes.h> |
32 | | |
33 | | #include <string.h> |
34 | | |
35 | | |
36 | | /* GVariantSerialiser |
37 | | * |
38 | | * After this prologue section, this file has roughly 2 parts. |
39 | | * |
40 | | * The first part is split up into sections according to various |
41 | | * container types. Maybe, Array, Tuple, Variant. The Maybe and Array |
42 | | * sections are subdivided for element types being fixed or |
43 | | * variable-sized types. |
44 | | * |
45 | | * Each section documents the format of that particular type of |
46 | | * container and implements 5 functions for dealing with it: |
47 | | * |
48 | | * n_children: |
49 | | * - determines (according to serialized data) how many child values |
50 | | * are inside a particular container value. |
51 | | * |
52 | | * get_child: |
53 | | * - gets the type of and the serialized data corresponding to a |
54 | | * given child value within the container value. |
55 | | * |
56 | | * needed_size: |
57 | | * - determines how much space would be required to serialize a |
58 | | * container of this type, containing the given children so that |
59 | | * buffers can be preallocated before serializing. |
60 | | * |
61 | | * serialise: |
62 | | * - write the serialized data for a container of this type, |
63 | | * containing the given children, to a buffer. |
64 | | * |
65 | | * is_normal: |
66 | | * - check the given data to ensure that it is in normal form. For a |
67 | | * given set of child values, there is exactly one normal form for |
68 | | * the serialized data of a container. Other forms are possible |
69 | | * while maintaining the same children (for example, by inserting |
70 | | * something other than zero bytes as padding) but only one form is |
71 | | * the normal form. |
72 | | * |
73 | | * The second part contains the main entry point for each of the above 5 |
74 | | * functions and logic to dispatch it to the handler for the appropriate |
75 | | * container type code. |
76 | | * |
77 | | * The second part also contains a routine to byteswap serialized |
78 | | * values. This code makes use of the n_children() and get_child() |
79 | | * functions above to do its work so no extra support is needed on a |
80 | | * per-container-type basis. |
81 | | * |
82 | | * There is also additional code for checking for normal form. All |
83 | | * numeric types are always in normal form since the full range of |
84 | | * values is permitted (eg: 0 to 255 is a valid byte). Special checks |
85 | | * need to be performed for booleans (only 0 or 1 allowed), strings |
86 | | * (properly nul-terminated) and object paths and signature strings |
87 | | * (meeting the D-Bus specification requirements). Depth checks need to be |
88 | | * performed for nested types (arrays, tuples, and variants), to avoid massive |
89 | | * recursion which could exhaust our stack when handling untrusted input. |
90 | | */ |
91 | | |
92 | | /* < private > |
93 | | * GVariantSerialised: |
94 | | * @type_info: the #GVariantTypeInfo of this value |
95 | | * @data: (nullable): the serialized data of this value, or %NULL |
96 | | * @size: the size of this value |
97 | | * |
98 | | * A structure representing a GVariant in serialized form. This |
99 | | * structure is used with #GVariantSerialisedFiller functions and as the |
100 | | * primary interface to the serializer. See #GVariantSerialisedFiller |
101 | | * for a description of its use there. |
102 | | * |
103 | | * When used with the serializer API functions, the following invariants |
104 | | * apply to all #GVariantTypeSerialised structures passed to and |
105 | | * returned from the serializer. |
106 | | * |
107 | | * @type_info must be non-%NULL. |
108 | | * |
109 | | * @data must be properly aligned for the type described by @type_info. |
110 | | * |
111 | | * If @type_info describes a fixed-sized type then @size must always be |
112 | | * equal to the fixed size of that type. |
113 | | * |
114 | | * For fixed-sized types (and only fixed-sized types), @data may be |
115 | | * %NULL even if @size is non-zero. This happens when a framing error |
116 | | * occurs while attempting to extract a fixed-sized value out of a |
117 | | * variable-sized container. There is no data to return for the |
118 | | * fixed-sized type, yet @size must be non-zero. The effect of this |
119 | | * combination should be as if @data were a pointer to an |
120 | | * appropriately-sized zero-filled region. |
121 | | * |
122 | | * @depth has no restrictions; the depth of a top-level serialized #GVariant is |
123 | | * zero, and it increases for each level of nested child. |
124 | | */ |
125 | | |
126 | | /* < private > |
127 | | * g_variant_serialised_check: |
128 | | * @serialised: a #GVariantSerialised struct |
129 | | * |
130 | | * Checks @serialised for validity according to the invariants described |
131 | | * above. |
132 | | * |
133 | | * Returns: %TRUE if @serialised is valid; %FALSE otherwise |
134 | | */ |
135 | | gboolean |
136 | | g_variant_serialised_check (GVariantSerialised serialised) |
137 | 0 | { |
138 | 0 | gsize fixed_size; |
139 | 0 | guint alignment; |
140 | |
|
141 | 0 | if (serialised.type_info == NULL) |
142 | 0 | return FALSE; |
143 | 0 | g_variant_type_info_query (serialised.type_info, &alignment, &fixed_size); |
144 | |
|
145 | 0 | if (fixed_size != 0 && serialised.size != fixed_size) |
146 | 0 | return FALSE; |
147 | 0 | else if (fixed_size == 0 && |
148 | 0 | !(serialised.size == 0 || serialised.data != NULL)) |
149 | 0 | return FALSE; |
150 | | |
151 | | /* Depending on the native alignment requirements of the machine, the |
152 | | * compiler will insert either 3 or 7 padding bytes after the char. |
153 | | * This will result in the sizeof() the struct being 12 or 16. |
154 | | * Subtract 9 to get 3 or 7 which is a nice bitmask to apply to get |
155 | | * the alignment bits that we "care about" being zero: in the |
156 | | * 4-aligned case, we care about 2 bits, and in the 8-aligned case, we |
157 | | * care about 3 bits. |
158 | | */ |
159 | 0 | alignment &= sizeof (struct { |
160 | 0 | char a; |
161 | 0 | union { |
162 | 0 | guint64 x; |
163 | 0 | void *y; |
164 | 0 | gdouble z; |
165 | 0 | } b; |
166 | 0 | } |
167 | 0 | ) - 9; |
168 | | |
169 | | /* Some OSes (FreeBSD is a known example) have a malloc() that returns |
170 | | * unaligned memory if you request small sizes. 'malloc (1);', for |
171 | | * example, has been seen to return pointers aligned to 6 mod 16. |
172 | | * |
173 | | * Check if this is a small allocation and return without enforcing |
174 | | * the alignment assertion if this is the case. |
175 | | */ |
176 | 0 | return (serialised.size <= alignment || |
177 | 0 | (alignment & (gsize) serialised.data) == 0); |
178 | 0 | } |
179 | | |
180 | | /* < private > |
181 | | * GVariantSerialisedFiller: |
182 | | * @serialised: a #GVariantSerialised instance to fill |
183 | | * @data: data from the children array |
184 | | * |
185 | | * This function is called back from g_variant_serialiser_needed_size() |
186 | | * and g_variant_serialiser_serialise(). It fills in missing details |
187 | | * from a partially-complete #GVariantSerialised. |
188 | | * |
189 | | * The @data parameter passed back to the function is one of the items |
190 | | * that was passed to the serializer in the @children array. It |
191 | | * represents a single child item of the container that is being |
192 | | * serialized. The information filled in to @serialised is the |
193 | | * information for this child. |
194 | | * |
195 | | * If the @type_info field of @serialised is %NULL then the callback |
196 | | * function must set it to the type information corresponding to the |
197 | | * type of the child. No reference should be added. If it is non-%NULL |
198 | | * then the callback should assert that it is equal to the actual type |
199 | | * of the child. |
200 | | * |
201 | | * If the @size field is zero then the callback must fill it in with the |
202 | | * required amount of space to store the serialized form of the child. |
203 | | * If it is non-zero then the callback should assert that it is equal to |
204 | | * the needed size of the child. |
205 | | * |
206 | | * If @data is non-%NULL then it points to a space that is properly |
207 | | * aligned for and large enough to store the serialized data of the |
208 | | * child. The callback must store the serialized form of the child at |
209 | | * @data. |
210 | | * |
211 | | * If the child value is another container then the callback will likely |
212 | | * recurse back into the serializer by calling |
213 | | * g_variant_serialiser_needed_size() to determine @size and |
214 | | * g_variant_serialiser_serialise() to write to @data. |
215 | | */ |
216 | | |
217 | | /* PART 1: Container types {{{1 |
218 | | * |
219 | | * This section contains the serializer implementation functions for |
220 | | * each container type. |
221 | | */ |
222 | | |
223 | | /* Maybe {{{2 |
224 | | * |
225 | | * Maybe types are handled depending on if the element type of the maybe |
226 | | * type is a fixed-sized or variable-sized type. Although all maybe |
227 | | * types themselves are variable-sized types, herein, a maybe value with |
228 | | * a fixed-sized element type is called a "fixed-sized maybe" for |
229 | | * convenience and a maybe value with a variable-sized element type is |
230 | | * called a "variable-sized maybe". |
231 | | */ |
232 | | |
233 | | /* Fixed-sized Maybe {{{3 |
234 | | * |
235 | | * The size of a maybe value with a fixed-sized element type is either 0 |
236 | | * or equal to the fixed size of its element type. The case where the |
237 | | * size of the maybe value is zero corresponds to the "Nothing" case and |
238 | | * the case where the size of the maybe value is equal to the fixed size |
239 | | * of the element type corresponds to the "Just" case; in that case, the |
240 | | * serialized data of the child value forms the entire serialized data |
241 | | * of the maybe value. |
242 | | * |
243 | | * In the event that a fixed-sized maybe value is presented with a size |
244 | | * that is not equal to the fixed size of the element type then the |
245 | | * value must be taken to be "Nothing". |
246 | | */ |
247 | | |
248 | | static gsize |
249 | | gvs_fixed_sized_maybe_n_children (GVariantSerialised value) |
250 | 0 | { |
251 | 0 | gsize element_fixed_size; |
252 | |
|
253 | 0 | g_variant_type_info_query_element (value.type_info, NULL, |
254 | 0 | &element_fixed_size); |
255 | |
|
256 | 0 | return (element_fixed_size == value.size) ? 1 : 0; |
257 | 0 | } |
258 | | |
259 | | static GVariantSerialised |
260 | | gvs_fixed_sized_maybe_get_child (GVariantSerialised value, |
261 | | gsize index_) |
262 | 0 | { |
263 | | /* the child has the same bounds as the |
264 | | * container, so just update the type. |
265 | | */ |
266 | 0 | value.type_info = g_variant_type_info_element (value.type_info); |
267 | 0 | g_variant_type_info_ref (value.type_info); |
268 | 0 | value.depth++; |
269 | |
|
270 | 0 | return value; |
271 | 0 | } |
272 | | |
273 | | static gsize |
274 | | gvs_fixed_sized_maybe_needed_size (GVariantTypeInfo *type_info, |
275 | | GVariantSerialisedFiller gvs_filler, |
276 | | const gpointer *children, |
277 | | gsize n_children) |
278 | 0 | { |
279 | 0 | if (n_children) |
280 | 0 | { |
281 | 0 | gsize element_fixed_size; |
282 | |
|
283 | 0 | g_variant_type_info_query_element (type_info, NULL, |
284 | 0 | &element_fixed_size); |
285 | |
|
286 | 0 | return element_fixed_size; |
287 | 0 | } |
288 | 0 | else |
289 | 0 | return 0; |
290 | 0 | } |
291 | | |
292 | | static void |
293 | | gvs_fixed_sized_maybe_serialise (GVariantSerialised value, |
294 | | GVariantSerialisedFiller gvs_filler, |
295 | | const gpointer *children, |
296 | | gsize n_children) |
297 | 0 | { |
298 | 0 | if (n_children) |
299 | 0 | { |
300 | 0 | GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1 }; |
301 | |
|
302 | 0 | gvs_filler (&child, children[0]); |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | static gboolean |
307 | | gvs_fixed_sized_maybe_is_normal (GVariantSerialised value) |
308 | 0 | { |
309 | 0 | if (value.size > 0) |
310 | 0 | { |
311 | 0 | gsize element_fixed_size; |
312 | |
|
313 | 0 | g_variant_type_info_query_element (value.type_info, |
314 | 0 | NULL, &element_fixed_size); |
315 | |
|
316 | 0 | if (value.size != element_fixed_size) |
317 | 0 | return FALSE; |
318 | | |
319 | | /* proper element size: "Just". recurse to the child. */ |
320 | 0 | value.type_info = g_variant_type_info_element (value.type_info); |
321 | 0 | value.depth++; |
322 | |
|
323 | 0 | return g_variant_serialised_is_normal (value); |
324 | 0 | } |
325 | | |
326 | | /* size of 0: "Nothing" */ |
327 | 0 | return TRUE; |
328 | 0 | } |
329 | | |
330 | | /* Variable-sized Maybe |
331 | | * |
332 | | * The size of a maybe value with a variable-sized element type is |
333 | | * either 0 or strictly greater than 0. The case where the size of the |
334 | | * maybe value is zero corresponds to the "Nothing" case and the case |
335 | | * where the size of the maybe value is greater than zero corresponds to |
336 | | * the "Just" case; in that case, the serialized data of the child value |
337 | | * forms the first part of the serialized data of the maybe value and is |
338 | | * followed by a single zero byte. This zero byte is always appended, |
339 | | * regardless of any zero bytes that may already be at the end of the |
340 | | * serialized ata of the child value. |
341 | | */ |
342 | | |
343 | | static gsize |
344 | | gvs_variable_sized_maybe_n_children (GVariantSerialised value) |
345 | 0 | { |
346 | 0 | return (value.size > 0) ? 1 : 0; |
347 | 0 | } |
348 | | |
349 | | static GVariantSerialised |
350 | | gvs_variable_sized_maybe_get_child (GVariantSerialised value, |
351 | | gsize index_) |
352 | 0 | { |
353 | | /* remove the padding byte and update the type. */ |
354 | 0 | value.type_info = g_variant_type_info_element (value.type_info); |
355 | 0 | g_variant_type_info_ref (value.type_info); |
356 | 0 | value.size--; |
357 | | |
358 | | /* if it's zero-sized then it may as well be NULL */ |
359 | 0 | if (value.size == 0) |
360 | 0 | value.data = NULL; |
361 | |
|
362 | 0 | value.depth++; |
363 | |
|
364 | 0 | return value; |
365 | 0 | } |
366 | | |
367 | | static gsize |
368 | | gvs_variable_sized_maybe_needed_size (GVariantTypeInfo *type_info, |
369 | | GVariantSerialisedFiller gvs_filler, |
370 | | const gpointer *children, |
371 | | gsize n_children) |
372 | 0 | { |
373 | 0 | if (n_children) |
374 | 0 | { |
375 | 0 | GVariantSerialised child = { 0, }; |
376 | |
|
377 | 0 | gvs_filler (&child, children[0]); |
378 | |
|
379 | 0 | return child.size + 1; |
380 | 0 | } |
381 | 0 | else |
382 | 0 | return 0; |
383 | 0 | } |
384 | | |
385 | | static void |
386 | | gvs_variable_sized_maybe_serialise (GVariantSerialised value, |
387 | | GVariantSerialisedFiller gvs_filler, |
388 | | const gpointer *children, |
389 | | gsize n_children) |
390 | 0 | { |
391 | 0 | if (n_children) |
392 | 0 | { |
393 | 0 | GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1 }; |
394 | | |
395 | | /* write the data for the child. */ |
396 | 0 | gvs_filler (&child, children[0]); |
397 | 0 | value.data[child.size] = '\0'; |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | static gboolean |
402 | | gvs_variable_sized_maybe_is_normal (GVariantSerialised value) |
403 | 0 | { |
404 | 0 | if (value.size == 0) |
405 | 0 | return TRUE; |
406 | | |
407 | 0 | if (value.data[value.size - 1] != '\0') |
408 | 0 | return FALSE; |
409 | | |
410 | 0 | value.type_info = g_variant_type_info_element (value.type_info); |
411 | 0 | value.size--; |
412 | 0 | value.depth++; |
413 | |
|
414 | 0 | return g_variant_serialised_is_normal (value); |
415 | 0 | } |
416 | | |
417 | | /* Arrays {{{2 |
418 | | * |
419 | | * Just as with maybe types, array types are handled depending on if the |
420 | | * element type of the array type is a fixed-sized or variable-sized |
421 | | * type. Similar to maybe types, for convenience, an array value with a |
422 | | * fixed-sized element type is called a "fixed-sized array" and an array |
423 | | * value with a variable-sized element type is called a "variable sized |
424 | | * array". |
425 | | */ |
426 | | |
427 | | /* Fixed-sized Array {{{3 |
428 | | * |
429 | | * For fixed sized arrays, the serialized data is simply a concatenation |
430 | | * of the serialized data of each element, in order. Since fixed-sized |
431 | | * values always have a fixed size that is a multiple of their alignment |
432 | | * requirement no extra padding is required. |
433 | | * |
434 | | * In the event that a fixed-sized array is presented with a size that |
435 | | * is not an integer multiple of the element size then the value of the |
436 | | * array must be taken as being empty. |
437 | | */ |
438 | | |
439 | | static gsize |
440 | | gvs_fixed_sized_array_n_children (GVariantSerialised value) |
441 | 0 | { |
442 | 0 | gsize element_fixed_size; |
443 | |
|
444 | 0 | g_variant_type_info_query_element (value.type_info, NULL, |
445 | 0 | &element_fixed_size); |
446 | |
|
447 | 0 | if (value.size % element_fixed_size == 0) |
448 | 0 | return value.size / element_fixed_size; |
449 | | |
450 | 0 | return 0; |
451 | 0 | } |
452 | | |
453 | | static GVariantSerialised |
454 | | gvs_fixed_sized_array_get_child (GVariantSerialised value, |
455 | | gsize index_) |
456 | 0 | { |
457 | 0 | GVariantSerialised child = { 0, }; |
458 | |
|
459 | 0 | child.type_info = g_variant_type_info_element (value.type_info); |
460 | 0 | g_variant_type_info_query (child.type_info, NULL, &child.size); |
461 | 0 | child.data = value.data + (child.size * index_); |
462 | 0 | g_variant_type_info_ref (child.type_info); |
463 | 0 | child.depth = value.depth + 1; |
464 | |
|
465 | 0 | return child; |
466 | 0 | } |
467 | | |
468 | | static gsize |
469 | | gvs_fixed_sized_array_needed_size (GVariantTypeInfo *type_info, |
470 | | GVariantSerialisedFiller gvs_filler, |
471 | | const gpointer *children, |
472 | | gsize n_children) |
473 | 0 | { |
474 | 0 | gsize element_fixed_size; |
475 | |
|
476 | 0 | g_variant_type_info_query_element (type_info, NULL, &element_fixed_size); |
477 | |
|
478 | 0 | return element_fixed_size * n_children; |
479 | 0 | } |
480 | | |
481 | | static void |
482 | | gvs_fixed_sized_array_serialise (GVariantSerialised value, |
483 | | GVariantSerialisedFiller gvs_filler, |
484 | | const gpointer *children, |
485 | | gsize n_children) |
486 | 0 | { |
487 | 0 | GVariantSerialised child = { 0, }; |
488 | 0 | gsize i; |
489 | |
|
490 | 0 | child.type_info = g_variant_type_info_element (value.type_info); |
491 | 0 | g_variant_type_info_query (child.type_info, NULL, &child.size); |
492 | 0 | child.data = value.data; |
493 | 0 | child.depth = value.depth + 1; |
494 | |
|
495 | 0 | for (i = 0; i < n_children; i++) |
496 | 0 | { |
497 | 0 | gvs_filler (&child, children[i]); |
498 | 0 | child.data += child.size; |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | | static gboolean |
503 | | gvs_fixed_sized_array_is_normal (GVariantSerialised value) |
504 | 0 | { |
505 | 0 | GVariantSerialised child = { 0, }; |
506 | |
|
507 | 0 | child.type_info = g_variant_type_info_element (value.type_info); |
508 | 0 | g_variant_type_info_query (child.type_info, NULL, &child.size); |
509 | 0 | child.depth = value.depth + 1; |
510 | |
|
511 | 0 | if (value.size % child.size != 0) |
512 | 0 | return FALSE; |
513 | | |
514 | 0 | for (child.data = value.data; |
515 | 0 | child.data < value.data + value.size; |
516 | 0 | child.data += child.size) |
517 | 0 | { |
518 | 0 | if (!g_variant_serialised_is_normal (child)) |
519 | 0 | return FALSE; |
520 | 0 | } |
521 | | |
522 | 0 | return TRUE; |
523 | 0 | } |
524 | | |
525 | | /* Variable-sized Array {{{3 |
526 | | * |
527 | | * Variable sized arrays, containing variable-sized elements, must be |
528 | | * able to determine the boundaries between the elements. The items |
529 | | * cannot simply be concatenated. Additionally, we are faced with the |
530 | | * fact that non-fixed-sized values do not necessarily have a size that |
531 | | * is a multiple of their alignment requirement, so we may need to |
532 | | * insert zero-filled padding. |
533 | | * |
534 | | * While it is possible to find the start of an item by starting from |
535 | | * the end of the item before it and padding for alignment, it is not |
536 | | * generally possible to do the reverse operation. For this reason, we |
537 | | * record the end point of each element in the array. |
538 | | * |
539 | | * GVariant works in terms of "offsets". An offset is a pointer to a |
540 | | * boundary between two bytes. In 4 bytes of serialized data, there |
541 | | * would be 5 possible offsets: one at the start ('0'), one between each |
542 | | * pair of adjacent bytes ('1', '2', '3') and one at the end ('4'). |
543 | | * |
544 | | * The numeric value of an offset is an unsigned integer given relative |
545 | | * to the start of the serialized data of the array. Offsets are always |
546 | | * stored in little endian byte order and are always only as big as they |
547 | | * need to be. For example, in 255 bytes of serialized data, there are |
548 | | * 256 offsets. All possibilities can be stored in an 8 bit unsigned |
549 | | * integer. In 256 bytes of serialized data, however, there are 257 |
550 | | * possible offsets so 16 bit integers must be used. The size of an |
551 | | * offset is always a power of 2. |
552 | | * |
553 | | * The offsets are stored at the end of the serialized data of the |
554 | | * array. They are simply concatenated on without any particular |
555 | | * alignment. The size of the offsets is included in the size of the |
556 | | * serialized data for purposes of determining the size of the offsets. |
557 | | * This presents a possibly ambiguity; in certain cases, a particular |
558 | | * value of array could have two different serialized forms. |
559 | | * |
560 | | * Imagine an array containing a single string of 253 bytes in length |
561 | | * (so, 254 bytes including the nul terminator). Now the offset must be |
562 | | * written. If an 8 bit offset is written, it will bring the size of |
563 | | * the array's serialized data to 255 -- which means that the use of an |
564 | | * 8 bit offset was valid. If a 16 bit offset is used then the total |
565 | | * size of the array will be 256 -- which means that the use of a 16 bit |
566 | | * offset was valid. Although both of these will be accepted by the |
567 | | * deserializer, only the smaller of the two is considered to be in |
568 | | * normal form and that is the one that the serializer must produce. |
569 | | */ |
570 | | |
571 | | /* bytes may be NULL if (size == 0). */ |
572 | | static inline gsize |
573 | | gvs_read_unaligned_le (guchar *bytes, |
574 | | guint size) |
575 | 0 | { |
576 | 0 | union |
577 | 0 | { |
578 | 0 | guchar bytes[GLIB_SIZEOF_SIZE_T]; |
579 | 0 | gsize integer; |
580 | 0 | } tmpvalue; |
581 | |
|
582 | 0 | tmpvalue.integer = 0; |
583 | 0 | if (bytes != NULL) |
584 | 0 | memcpy (&tmpvalue.bytes, bytes, size); |
585 | |
|
586 | 0 | return GSIZE_FROM_LE (tmpvalue.integer); |
587 | 0 | } |
588 | | |
589 | | static inline void |
590 | | gvs_write_unaligned_le (guchar *bytes, |
591 | | gsize value, |
592 | | guint size) |
593 | 0 | { |
594 | 0 | union |
595 | 0 | { |
596 | 0 | guchar bytes[GLIB_SIZEOF_SIZE_T]; |
597 | 0 | gsize integer; |
598 | 0 | } tmpvalue; |
599 | |
|
600 | 0 | tmpvalue.integer = GSIZE_TO_LE (value); |
601 | 0 | memcpy (bytes, &tmpvalue.bytes, size); |
602 | 0 | } |
603 | | |
604 | | static guint |
605 | | gvs_get_offset_size (gsize size) |
606 | 0 | { |
607 | 0 | if (size > G_MAXUINT32) |
608 | 0 | return 8; |
609 | | |
610 | 0 | else if (size > G_MAXUINT16) |
611 | 0 | return 4; |
612 | | |
613 | 0 | else if (size > G_MAXUINT8) |
614 | 0 | return 2; |
615 | | |
616 | 0 | else if (size > 0) |
617 | 0 | return 1; |
618 | | |
619 | 0 | return 0; |
620 | 0 | } |
621 | | |
622 | | static gsize |
623 | | gvs_calculate_total_size (gsize body_size, |
624 | | gsize offsets) |
625 | 0 | { |
626 | 0 | if (body_size + 1 * offsets <= G_MAXUINT8) |
627 | 0 | return body_size + 1 * offsets; |
628 | | |
629 | 0 | if (body_size + 2 * offsets <= G_MAXUINT16) |
630 | 0 | return body_size + 2 * offsets; |
631 | | |
632 | 0 | if (body_size + 4 * offsets <= G_MAXUINT32) |
633 | 0 | return body_size + 4 * offsets; |
634 | | |
635 | 0 | return body_size + 8 * offsets; |
636 | 0 | } |
637 | | |
638 | | static gsize |
639 | | gvs_variable_sized_array_n_children (GVariantSerialised value) |
640 | 0 | { |
641 | 0 | gsize offsets_array_size; |
642 | 0 | gsize offset_size; |
643 | 0 | gsize last_end; |
644 | |
|
645 | 0 | if (value.size == 0) |
646 | 0 | return 0; |
647 | | |
648 | 0 | offset_size = gvs_get_offset_size (value.size); |
649 | |
|
650 | 0 | last_end = gvs_read_unaligned_le (value.data + value.size - |
651 | 0 | offset_size, offset_size); |
652 | |
|
653 | 0 | if (last_end > value.size) |
654 | 0 | return 0; |
655 | | |
656 | 0 | offsets_array_size = value.size - last_end; |
657 | |
|
658 | 0 | if (offsets_array_size % offset_size) |
659 | 0 | return 0; |
660 | | |
661 | 0 | return offsets_array_size / offset_size; |
662 | 0 | } |
663 | | |
664 | | static GVariantSerialised |
665 | | gvs_variable_sized_array_get_child (GVariantSerialised value, |
666 | | gsize index_) |
667 | 0 | { |
668 | 0 | GVariantSerialised child = { 0, }; |
669 | 0 | gsize offset_size; |
670 | 0 | gsize last_end; |
671 | 0 | gsize start; |
672 | 0 | gsize end; |
673 | |
|
674 | 0 | child.type_info = g_variant_type_info_element (value.type_info); |
675 | 0 | g_variant_type_info_ref (child.type_info); |
676 | 0 | child.depth = value.depth + 1; |
677 | |
|
678 | 0 | offset_size = gvs_get_offset_size (value.size); |
679 | |
|
680 | 0 | last_end = gvs_read_unaligned_le (value.data + value.size - |
681 | 0 | offset_size, offset_size); |
682 | |
|
683 | 0 | if (index_ > 0) |
684 | 0 | { |
685 | 0 | guint alignment; |
686 | |
|
687 | 0 | start = gvs_read_unaligned_le (value.data + last_end + |
688 | 0 | (offset_size * (index_ - 1)), |
689 | 0 | offset_size); |
690 | |
|
691 | 0 | g_variant_type_info_query (child.type_info, &alignment, NULL); |
692 | 0 | start += (-start) & alignment; |
693 | 0 | } |
694 | 0 | else |
695 | 0 | start = 0; |
696 | |
|
697 | 0 | end = gvs_read_unaligned_le (value.data + last_end + |
698 | 0 | (offset_size * index_), |
699 | 0 | offset_size); |
700 | |
|
701 | 0 | if (start < end && end <= value.size && end <= last_end) |
702 | 0 | { |
703 | 0 | child.data = value.data + start; |
704 | 0 | child.size = end - start; |
705 | 0 | } |
706 | |
|
707 | 0 | return child; |
708 | 0 | } |
709 | | |
710 | | static gsize |
711 | | gvs_variable_sized_array_needed_size (GVariantTypeInfo *type_info, |
712 | | GVariantSerialisedFiller gvs_filler, |
713 | | const gpointer *children, |
714 | | gsize n_children) |
715 | 0 | { |
716 | 0 | guint alignment; |
717 | 0 | gsize offset; |
718 | 0 | gsize i; |
719 | |
|
720 | 0 | g_variant_type_info_query (type_info, &alignment, NULL); |
721 | 0 | offset = 0; |
722 | |
|
723 | 0 | for (i = 0; i < n_children; i++) |
724 | 0 | { |
725 | 0 | GVariantSerialised child = { 0, }; |
726 | |
|
727 | 0 | offset += (-offset) & alignment; |
728 | 0 | gvs_filler (&child, children[i]); |
729 | 0 | offset += child.size; |
730 | 0 | } |
731 | |
|
732 | 0 | return gvs_calculate_total_size (offset, n_children); |
733 | 0 | } |
734 | | |
735 | | static void |
736 | | gvs_variable_sized_array_serialise (GVariantSerialised value, |
737 | | GVariantSerialisedFiller gvs_filler, |
738 | | const gpointer *children, |
739 | | gsize n_children) |
740 | 0 | { |
741 | 0 | guchar *offset_ptr; |
742 | 0 | gsize offset_size; |
743 | 0 | guint alignment; |
744 | 0 | gsize offset; |
745 | 0 | gsize i; |
746 | |
|
747 | 0 | g_variant_type_info_query (value.type_info, &alignment, NULL); |
748 | 0 | offset_size = gvs_get_offset_size (value.size); |
749 | 0 | offset = 0; |
750 | |
|
751 | 0 | offset_ptr = value.data + value.size - offset_size * n_children; |
752 | |
|
753 | 0 | for (i = 0; i < n_children; i++) |
754 | 0 | { |
755 | 0 | GVariantSerialised child = { 0, }; |
756 | |
|
757 | 0 | while (offset & alignment) |
758 | 0 | value.data[offset++] = '\0'; |
759 | |
|
760 | 0 | child.data = value.data + offset; |
761 | 0 | gvs_filler (&child, children[i]); |
762 | 0 | offset += child.size; |
763 | |
|
764 | 0 | gvs_write_unaligned_le (offset_ptr, offset, offset_size); |
765 | 0 | offset_ptr += offset_size; |
766 | 0 | } |
767 | 0 | } |
768 | | |
769 | | static gboolean |
770 | | gvs_variable_sized_array_is_normal (GVariantSerialised value) |
771 | 0 | { |
772 | 0 | GVariantSerialised child = { 0, }; |
773 | 0 | gsize offsets_array_size; |
774 | 0 | guchar *offsets_array; |
775 | 0 | guint offset_size; |
776 | 0 | guint alignment; |
777 | 0 | gsize last_end; |
778 | 0 | gsize length; |
779 | 0 | gsize offset; |
780 | 0 | gsize i; |
781 | |
|
782 | 0 | if (value.size == 0) |
783 | 0 | return TRUE; |
784 | | |
785 | 0 | offset_size = gvs_get_offset_size (value.size); |
786 | 0 | last_end = gvs_read_unaligned_le (value.data + value.size - |
787 | 0 | offset_size, offset_size); |
788 | |
|
789 | 0 | if (last_end > value.size) |
790 | 0 | return FALSE; |
791 | | |
792 | 0 | offsets_array_size = value.size - last_end; |
793 | |
|
794 | 0 | if (offsets_array_size % offset_size) |
795 | 0 | return FALSE; |
796 | | |
797 | 0 | offsets_array = value.data + value.size - offsets_array_size; |
798 | 0 | length = offsets_array_size / offset_size; |
799 | |
|
800 | 0 | if (length == 0) |
801 | 0 | return FALSE; |
802 | | |
803 | 0 | child.type_info = g_variant_type_info_element (value.type_info); |
804 | 0 | g_variant_type_info_query (child.type_info, &alignment, NULL); |
805 | 0 | child.depth = value.depth + 1; |
806 | 0 | offset = 0; |
807 | |
|
808 | 0 | for (i = 0; i < length; i++) |
809 | 0 | { |
810 | 0 | gsize this_end; |
811 | |
|
812 | 0 | this_end = gvs_read_unaligned_le (offsets_array + offset_size * i, |
813 | 0 | offset_size); |
814 | |
|
815 | 0 | if (this_end < offset || this_end > last_end) |
816 | 0 | return FALSE; |
817 | | |
818 | 0 | while (offset & alignment) |
819 | 0 | { |
820 | 0 | if (!(offset < this_end && value.data[offset] == '\0')) |
821 | 0 | return FALSE; |
822 | 0 | offset++; |
823 | 0 | } |
824 | | |
825 | 0 | child.data = value.data + offset; |
826 | 0 | child.size = this_end - offset; |
827 | |
|
828 | 0 | if (child.size == 0) |
829 | 0 | child.data = NULL; |
830 | |
|
831 | 0 | if (!g_variant_serialised_is_normal (child)) |
832 | 0 | return FALSE; |
833 | | |
834 | 0 | offset = this_end; |
835 | 0 | } |
836 | | |
837 | 0 | g_assert (offset == last_end); |
838 | | |
839 | 0 | return TRUE; |
840 | 0 | } |
841 | | |
842 | | /* Tuples {{{2 |
843 | | * |
844 | | * Since tuples can contain a mix of variable- and fixed-sized items, |
845 | | * they are, in terms of serialization, a hybrid of variable-sized and |
846 | | * fixed-sized arrays. |
847 | | * |
848 | | * Offsets are only stored for variable-sized items. Also, since the |
849 | | * number of items in a tuple is known from its type, we are able to |
850 | | * know exactly how many offsets to expect in the serialized data (and |
851 | | * therefore how much space is taken up by the offset array). This |
852 | | * means that we know where the end of the serialized data for the last |
853 | | * item is -- we can just subtract the size of the offset array from the |
854 | | * total size of the tuple. For this reason, the last item in the tuple |
855 | | * doesn't need an offset stored. |
856 | | * |
857 | | * Tuple offsets are stored in reverse. This design choice allows |
858 | | * iterator-based deserializers to be more efficient. |
859 | | * |
860 | | * Most of the "heavy lifting" here is handled by the GVariantTypeInfo |
861 | | * for the tuple. See the notes in gvarianttypeinfo.h. |
862 | | */ |
863 | | |
864 | | static gsize |
865 | | gvs_tuple_n_children (GVariantSerialised value) |
866 | 0 | { |
867 | 0 | return g_variant_type_info_n_members (value.type_info); |
868 | 0 | } |
869 | | |
870 | | static GVariantSerialised |
871 | | gvs_tuple_get_child (GVariantSerialised value, |
872 | | gsize index_) |
873 | 0 | { |
874 | 0 | const GVariantMemberInfo *member_info; |
875 | 0 | GVariantSerialised child = { 0, }; |
876 | 0 | gsize offset_size; |
877 | 0 | gsize start, end, last_end; |
878 | |
|
879 | 0 | member_info = g_variant_type_info_member_info (value.type_info, index_); |
880 | 0 | child.type_info = g_variant_type_info_ref (member_info->type_info); |
881 | 0 | child.depth = value.depth + 1; |
882 | 0 | offset_size = gvs_get_offset_size (value.size); |
883 | | |
884 | | /* tuples are the only (potentially) fixed-sized containers, so the |
885 | | * only ones that have to deal with the possibility of having %NULL |
886 | | * data with a non-zero %size if errors occurred elsewhere. |
887 | | */ |
888 | 0 | if G_UNLIKELY (value.data == NULL && value.size != 0) |
889 | 0 | { |
890 | 0 | g_variant_type_info_query (child.type_info, NULL, &child.size); |
891 | | |
892 | | /* this can only happen in fixed-sized tuples, |
893 | | * so the child must also be fixed sized. |
894 | | */ |
895 | 0 | g_assert (child.size != 0); |
896 | 0 | child.data = NULL; |
897 | |
|
898 | 0 | return child; |
899 | 0 | } |
900 | | |
901 | 0 | if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET) |
902 | 0 | { |
903 | 0 | if (offset_size * (member_info->i + 2) > value.size) |
904 | 0 | return child; |
905 | 0 | } |
906 | 0 | else |
907 | 0 | { |
908 | 0 | if (offset_size * (member_info->i + 1) > value.size) |
909 | 0 | { |
910 | | /* if the child is fixed size, return its size. |
911 | | * if child is not fixed-sized, return size = 0. |
912 | | */ |
913 | 0 | g_variant_type_info_query (child.type_info, NULL, &child.size); |
914 | |
|
915 | 0 | return child; |
916 | 0 | } |
917 | 0 | } |
918 | | |
919 | 0 | if (member_info->i + 1) |
920 | 0 | start = gvs_read_unaligned_le (value.data + value.size - |
921 | 0 | offset_size * (member_info->i + 1), |
922 | 0 | offset_size); |
923 | 0 | else |
924 | 0 | start = 0; |
925 | |
|
926 | 0 | start += member_info->a; |
927 | 0 | start &= member_info->b; |
928 | 0 | start |= member_info->c; |
929 | |
|
930 | 0 | if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST) |
931 | 0 | end = value.size - offset_size * (member_info->i + 1); |
932 | | |
933 | 0 | else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED) |
934 | 0 | { |
935 | 0 | gsize fixed_size; |
936 | |
|
937 | 0 | g_variant_type_info_query (child.type_info, NULL, &fixed_size); |
938 | 0 | end = start + fixed_size; |
939 | 0 | child.size = fixed_size; |
940 | 0 | } |
941 | | |
942 | 0 | else /* G_VARIANT_MEMBER_ENDING_OFFSET */ |
943 | 0 | end = gvs_read_unaligned_le (value.data + value.size - |
944 | 0 | offset_size * (member_info->i + 2), |
945 | 0 | offset_size); |
946 | | |
947 | | /* The child should not extend into the offset table. */ |
948 | 0 | if (index_ != g_variant_type_info_n_members (value.type_info) - 1) |
949 | 0 | { |
950 | 0 | GVariantSerialised last_child; |
951 | 0 | last_child = gvs_tuple_get_child (value, |
952 | 0 | g_variant_type_info_n_members (value.type_info) - 1); |
953 | 0 | last_end = last_child.data + last_child.size - value.data; |
954 | 0 | g_variant_type_info_unref (last_child.type_info); |
955 | 0 | } |
956 | 0 | else |
957 | 0 | last_end = end; |
958 | |
|
959 | 0 | if (start < end && end <= value.size && end <= last_end) |
960 | 0 | { |
961 | 0 | child.data = value.data + start; |
962 | 0 | child.size = end - start; |
963 | 0 | } |
964 | |
|
965 | 0 | return child; |
966 | 0 | } |
967 | | |
968 | | static gsize |
969 | | gvs_tuple_needed_size (GVariantTypeInfo *type_info, |
970 | | GVariantSerialisedFiller gvs_filler, |
971 | | const gpointer *children, |
972 | | gsize n_children) |
973 | 0 | { |
974 | 0 | const GVariantMemberInfo *member_info = NULL; |
975 | 0 | gsize fixed_size; |
976 | 0 | gsize offset; |
977 | 0 | gsize i; |
978 | |
|
979 | 0 | g_variant_type_info_query (type_info, NULL, &fixed_size); |
980 | |
|
981 | 0 | if (fixed_size) |
982 | 0 | return fixed_size; |
983 | | |
984 | 0 | offset = 0; |
985 | |
|
986 | 0 | for (i = 0; i < n_children; i++) |
987 | 0 | { |
988 | 0 | guint alignment; |
989 | |
|
990 | 0 | member_info = g_variant_type_info_member_info (type_info, i); |
991 | 0 | g_variant_type_info_query (member_info->type_info, |
992 | 0 | &alignment, &fixed_size); |
993 | 0 | offset += (-offset) & alignment; |
994 | |
|
995 | 0 | if (fixed_size) |
996 | 0 | offset += fixed_size; |
997 | 0 | else |
998 | 0 | { |
999 | 0 | GVariantSerialised child = { 0, }; |
1000 | |
|
1001 | 0 | gvs_filler (&child, children[i]); |
1002 | 0 | offset += child.size; |
1003 | 0 | } |
1004 | 0 | } |
1005 | |
|
1006 | 0 | return gvs_calculate_total_size (offset, member_info->i + 1); |
1007 | 0 | } |
1008 | | |
1009 | | static void |
1010 | | gvs_tuple_serialise (GVariantSerialised value, |
1011 | | GVariantSerialisedFiller gvs_filler, |
1012 | | const gpointer *children, |
1013 | | gsize n_children) |
1014 | 0 | { |
1015 | 0 | gsize offset_size; |
1016 | 0 | gsize offset; |
1017 | 0 | gsize i; |
1018 | |
|
1019 | 0 | offset_size = gvs_get_offset_size (value.size); |
1020 | 0 | offset = 0; |
1021 | |
|
1022 | 0 | for (i = 0; i < n_children; i++) |
1023 | 0 | { |
1024 | 0 | const GVariantMemberInfo *member_info; |
1025 | 0 | GVariantSerialised child = { 0, }; |
1026 | 0 | guint alignment; |
1027 | |
|
1028 | 0 | member_info = g_variant_type_info_member_info (value.type_info, i); |
1029 | 0 | g_variant_type_info_query (member_info->type_info, &alignment, NULL); |
1030 | |
|
1031 | 0 | while (offset & alignment) |
1032 | 0 | value.data[offset++] = '\0'; |
1033 | |
|
1034 | 0 | child.data = value.data + offset; |
1035 | 0 | gvs_filler (&child, children[i]); |
1036 | 0 | offset += child.size; |
1037 | |
|
1038 | 0 | if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET) |
1039 | 0 | { |
1040 | 0 | value.size -= offset_size; |
1041 | 0 | gvs_write_unaligned_le (value.data + value.size, |
1042 | 0 | offset, offset_size); |
1043 | 0 | } |
1044 | 0 | } |
1045 | |
|
1046 | 0 | while (offset < value.size) |
1047 | 0 | value.data[offset++] = '\0'; |
1048 | 0 | } |
1049 | | |
1050 | | static gboolean |
1051 | | gvs_tuple_is_normal (GVariantSerialised value) |
1052 | 0 | { |
1053 | 0 | guint offset_size; |
1054 | 0 | gsize offset_ptr; |
1055 | 0 | gsize length; |
1056 | 0 | gsize offset; |
1057 | 0 | gsize i; |
1058 | | |
1059 | | /* as per the comment in gvs_tuple_get_child() */ |
1060 | 0 | if G_UNLIKELY (value.data == NULL && value.size != 0) |
1061 | 0 | return FALSE; |
1062 | | |
1063 | 0 | offset_size = gvs_get_offset_size (value.size); |
1064 | 0 | length = g_variant_type_info_n_members (value.type_info); |
1065 | 0 | offset_ptr = value.size; |
1066 | 0 | offset = 0; |
1067 | |
|
1068 | 0 | for (i = 0; i < length; i++) |
1069 | 0 | { |
1070 | 0 | const GVariantMemberInfo *member_info; |
1071 | 0 | GVariantSerialised child; |
1072 | 0 | gsize fixed_size; |
1073 | 0 | guint alignment; |
1074 | 0 | gsize end; |
1075 | |
|
1076 | 0 | member_info = g_variant_type_info_member_info (value.type_info, i); |
1077 | 0 | child.type_info = member_info->type_info; |
1078 | 0 | child.depth = value.depth + 1; |
1079 | |
|
1080 | 0 | g_variant_type_info_query (child.type_info, &alignment, &fixed_size); |
1081 | |
|
1082 | 0 | while (offset & alignment) |
1083 | 0 | { |
1084 | 0 | if (offset > value.size || value.data[offset] != '\0') |
1085 | 0 | return FALSE; |
1086 | 0 | offset++; |
1087 | 0 | } |
1088 | | |
1089 | 0 | child.data = value.data + offset; |
1090 | |
|
1091 | 0 | switch (member_info->ending_type) |
1092 | 0 | { |
1093 | 0 | case G_VARIANT_MEMBER_ENDING_FIXED: |
1094 | 0 | end = offset + fixed_size; |
1095 | 0 | break; |
1096 | | |
1097 | 0 | case G_VARIANT_MEMBER_ENDING_LAST: |
1098 | 0 | end = offset_ptr; |
1099 | 0 | break; |
1100 | | |
1101 | 0 | case G_VARIANT_MEMBER_ENDING_OFFSET: |
1102 | 0 | if (offset_ptr < offset_size) |
1103 | 0 | return FALSE; |
1104 | | |
1105 | 0 | offset_ptr -= offset_size; |
1106 | |
|
1107 | 0 | if (offset_ptr < offset) |
1108 | 0 | return FALSE; |
1109 | | |
1110 | 0 | end = gvs_read_unaligned_le (value.data + offset_ptr, offset_size); |
1111 | 0 | break; |
1112 | | |
1113 | 0 | default: |
1114 | 0 | g_assert_not_reached (); |
1115 | 0 | } |
1116 | | |
1117 | 0 | if (end < offset || end > offset_ptr) |
1118 | 0 | return FALSE; |
1119 | | |
1120 | 0 | child.size = end - offset; |
1121 | |
|
1122 | 0 | if (child.size == 0) |
1123 | 0 | child.data = NULL; |
1124 | |
|
1125 | 0 | if (!g_variant_serialised_is_normal (child)) |
1126 | 0 | return FALSE; |
1127 | | |
1128 | 0 | offset = end; |
1129 | 0 | } |
1130 | | |
1131 | 0 | { |
1132 | 0 | gsize fixed_size; |
1133 | 0 | guint alignment; |
1134 | |
|
1135 | 0 | g_variant_type_info_query (value.type_info, &alignment, &fixed_size); |
1136 | |
|
1137 | 0 | if (fixed_size) |
1138 | 0 | { |
1139 | 0 | g_assert (fixed_size == value.size); |
1140 | 0 | g_assert (offset_ptr == value.size); |
1141 | | |
1142 | 0 | if (i == 0) |
1143 | 0 | { |
1144 | 0 | if (value.data[offset++] != '\0') |
1145 | 0 | return FALSE; |
1146 | 0 | } |
1147 | 0 | else |
1148 | 0 | { |
1149 | 0 | while (offset & alignment) |
1150 | 0 | if (value.data[offset++] != '\0') |
1151 | 0 | return FALSE; |
1152 | 0 | } |
1153 | | |
1154 | 0 | g_assert (offset == value.size); |
1155 | 0 | } |
1156 | 0 | } |
1157 | | |
1158 | 0 | return offset_ptr == offset; |
1159 | 0 | } |
1160 | | |
1161 | | /* Variants {{{2 |
1162 | | * |
1163 | | * Variants are stored by storing the serialized data of the child, |
1164 | | * followed by a '\0' character, followed by the type string of the |
1165 | | * child. |
1166 | | * |
1167 | | * In the case that a value is presented that contains no '\0' |
1168 | | * character, or doesn't have a single well-formed definite type string |
1169 | | * following that character, the variant must be taken as containing the |
1170 | | * unit tuple: (). |
1171 | | */ |
1172 | | |
1173 | | static inline gsize |
1174 | | gvs_variant_n_children (GVariantSerialised value) |
1175 | 0 | { |
1176 | 0 | return 1; |
1177 | 0 | } |
1178 | | |
1179 | | static inline GVariantSerialised |
1180 | | gvs_variant_get_child (GVariantSerialised value, |
1181 | | gsize index_) |
1182 | 0 | { |
1183 | 0 | GVariantSerialised child = { 0, }; |
1184 | | |
1185 | | /* NOTE: not O(1) and impossible for it to be... */ |
1186 | 0 | if (value.size) |
1187 | 0 | { |
1188 | | /* find '\0' character */ |
1189 | 0 | for (child.size = value.size - 1; child.size; child.size--) |
1190 | 0 | if (value.data[child.size] == '\0') |
1191 | 0 | break; |
1192 | | |
1193 | | /* ensure we didn't just hit the start of the string */ |
1194 | 0 | if (value.data[child.size] == '\0') |
1195 | 0 | { |
1196 | 0 | const gchar *type_string = (gchar *) &value.data[child.size + 1]; |
1197 | 0 | const gchar *limit = (gchar *) &value.data[value.size]; |
1198 | 0 | const gchar *end; |
1199 | |
|
1200 | 0 | if (g_variant_type_string_scan (type_string, limit, &end) && |
1201 | 0 | end == limit) |
1202 | 0 | { |
1203 | 0 | const GVariantType *type = (GVariantType *) type_string; |
1204 | |
|
1205 | 0 | if (g_variant_type_is_definite (type)) |
1206 | 0 | { |
1207 | 0 | gsize fixed_size; |
1208 | 0 | gsize child_type_depth; |
1209 | |
|
1210 | 0 | child.type_info = g_variant_type_info_get (type); |
1211 | 0 | child.depth = value.depth + 1; |
1212 | |
|
1213 | 0 | if (child.size != 0) |
1214 | | /* only set to non-%NULL if size > 0 */ |
1215 | 0 | child.data = value.data; |
1216 | |
|
1217 | 0 | g_variant_type_info_query (child.type_info, |
1218 | 0 | NULL, &fixed_size); |
1219 | 0 | child_type_depth = g_variant_type_info_query_depth (child.type_info); |
1220 | |
|
1221 | 0 | if ((!fixed_size || fixed_size == child.size) && |
1222 | 0 | value.depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth) |
1223 | 0 | return child; |
1224 | | |
1225 | 0 | g_variant_type_info_unref (child.type_info); |
1226 | 0 | } |
1227 | 0 | } |
1228 | 0 | } |
1229 | 0 | } |
1230 | | |
1231 | 0 | child.type_info = g_variant_type_info_get (G_VARIANT_TYPE_UNIT); |
1232 | 0 | child.data = NULL; |
1233 | 0 | child.size = 1; |
1234 | 0 | child.depth = value.depth + 1; |
1235 | |
|
1236 | 0 | return child; |
1237 | 0 | } |
1238 | | |
1239 | | static inline gsize |
1240 | | gvs_variant_needed_size (GVariantTypeInfo *type_info, |
1241 | | GVariantSerialisedFiller gvs_filler, |
1242 | | const gpointer *children, |
1243 | | gsize n_children) |
1244 | 0 | { |
1245 | 0 | GVariantSerialised child = { 0, }; |
1246 | 0 | const gchar *type_string; |
1247 | |
|
1248 | 0 | gvs_filler (&child, children[0]); |
1249 | 0 | type_string = g_variant_type_info_get_type_string (child.type_info); |
1250 | |
|
1251 | 0 | return child.size + 1 + strlen (type_string); |
1252 | 0 | } |
1253 | | |
1254 | | static inline void |
1255 | | gvs_variant_serialise (GVariantSerialised value, |
1256 | | GVariantSerialisedFiller gvs_filler, |
1257 | | const gpointer *children, |
1258 | | gsize n_children) |
1259 | 0 | { |
1260 | 0 | GVariantSerialised child = { 0, }; |
1261 | 0 | const gchar *type_string; |
1262 | |
|
1263 | 0 | child.data = value.data; |
1264 | |
|
1265 | 0 | gvs_filler (&child, children[0]); |
1266 | 0 | type_string = g_variant_type_info_get_type_string (child.type_info); |
1267 | 0 | value.data[child.size] = '\0'; |
1268 | 0 | memcpy (value.data + child.size + 1, type_string, strlen (type_string)); |
1269 | 0 | } |
1270 | | |
1271 | | static inline gboolean |
1272 | | gvs_variant_is_normal (GVariantSerialised value) |
1273 | 0 | { |
1274 | 0 | GVariantSerialised child; |
1275 | 0 | gboolean normal; |
1276 | 0 | gsize child_type_depth; |
1277 | |
|
1278 | 0 | child = gvs_variant_get_child (value, 0); |
1279 | 0 | child_type_depth = g_variant_type_info_query_depth (child.type_info); |
1280 | |
|
1281 | 0 | normal = (value.depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth) && |
1282 | 0 | (child.data != NULL || child.size == 0) && |
1283 | 0 | g_variant_serialised_is_normal (child); |
1284 | |
|
1285 | 0 | g_variant_type_info_unref (child.type_info); |
1286 | |
|
1287 | 0 | return normal; |
1288 | 0 | } |
1289 | | |
1290 | | |
1291 | | |
1292 | | /* PART 2: Serializer API {{{1 |
1293 | | * |
1294 | | * This is the implementation of the API of the serializer as advertised |
1295 | | * in gvariant-serialiser.h. |
1296 | | */ |
1297 | | |
1298 | | /* Dispatch Utilities {{{2 |
1299 | | * |
1300 | | * These macros allow a given function (for example, |
1301 | | * g_variant_serialiser_serialise) to be dispatched to the appropriate |
1302 | | * type-specific function above (fixed/variable-sized maybe, |
1303 | | * fixed/variable-sized array, tuple or variant). |
1304 | | */ |
1305 | | #define DISPATCH_FIXED(type_info, before, after) \ |
1306 | 0 | { \ |
1307 | 0 | gsize fixed_size; \ |
1308 | 0 | \ |
1309 | 0 | g_variant_type_info_query_element (type_info, NULL, \ |
1310 | 0 | &fixed_size); \ |
1311 | 0 | \ |
1312 | 0 | if (fixed_size) \ |
1313 | 0 | { \ |
1314 | 0 | before ## fixed_sized ## after \ |
1315 | 0 | } \ |
1316 | 0 | else \ |
1317 | 0 | { \ |
1318 | 0 | before ## variable_sized ## after \ |
1319 | 0 | } \ |
1320 | 0 | } |
1321 | | |
1322 | | #define DISPATCH_CASES(type_info, before, after) \ |
1323 | 0 | switch (g_variant_type_info_get_type_char (type_info)) \ |
1324 | 0 | { \ |
1325 | 0 | case G_VARIANT_TYPE_INFO_CHAR_MAYBE: \ |
1326 | 0 | DISPATCH_FIXED (type_info, before, _maybe ## after) \ |
1327 | 0 | \ |
1328 | 0 | case G_VARIANT_TYPE_INFO_CHAR_ARRAY: \ |
1329 | 0 | DISPATCH_FIXED (type_info, before, _array ## after) \ |
1330 | 0 | \ |
1331 | 0 | case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY: \ |
1332 | 0 | case G_VARIANT_TYPE_INFO_CHAR_TUPLE: \ |
1333 | 0 | { \ |
1334 | 0 | before ## tuple ## after \ |
1335 | 0 | } \ |
1336 | 0 | \ |
1337 | 0 | case G_VARIANT_TYPE_INFO_CHAR_VARIANT: \ |
1338 | 0 | { \ |
1339 | 0 | before ## variant ## after \ |
1340 | 0 | } \ |
1341 | 0 | } |
1342 | | |
1343 | | /* Serializer entry points {{{2 |
1344 | | * |
1345 | | * These are the functions that are called in order for the serializer |
1346 | | * to do its thing. |
1347 | | */ |
1348 | | |
1349 | | /* < private > |
1350 | | * g_variant_serialised_n_children: |
1351 | | * @serialised: a #GVariantSerialised |
1352 | | * |
1353 | | * For serialized data that represents a container value (maybes, |
1354 | | * tuples, arrays, variants), determine how many child items are inside |
1355 | | * that container. |
1356 | | * |
1357 | | * Returns: the number of children |
1358 | | */ |
1359 | | gsize |
1360 | | g_variant_serialised_n_children (GVariantSerialised serialised) |
1361 | 0 | { |
1362 | 0 | g_assert (g_variant_serialised_check (serialised)); |
1363 | | |
1364 | 0 | DISPATCH_CASES (serialised.type_info, |
1365 | |
|
1366 | 0 | return gvs_/**/,/**/_n_children (serialised); |
1367 | |
|
1368 | 0 | ) |
1369 | 0 | g_assert_not_reached (); |
1370 | 0 | } |
1371 | | |
1372 | | /* < private > |
1373 | | * g_variant_serialised_get_child: |
1374 | | * @serialised: a #GVariantSerialised |
1375 | | * @index_: the index of the child to fetch |
1376 | | * |
1377 | | * Extracts a child from a serialized data representing a container |
1378 | | * value. |
1379 | | * |
1380 | | * It is an error to call this function with an index out of bounds. |
1381 | | * |
1382 | | * If the result .data == %NULL and .size > 0 then there has been an |
1383 | | * error extracting the requested fixed-sized value. This number of |
1384 | | * zero bytes needs to be allocated instead. |
1385 | | * |
1386 | | * In the case that .data == %NULL and .size == 0 then a zero-sized |
1387 | | * item of a variable-sized type is being returned. |
1388 | | * |
1389 | | * .data is never non-%NULL if size is 0. |
1390 | | * |
1391 | | * Returns: a #GVariantSerialised for the child |
1392 | | */ |
1393 | | GVariantSerialised |
1394 | | g_variant_serialised_get_child (GVariantSerialised serialised, |
1395 | | gsize index_) |
1396 | 0 | { |
1397 | 0 | GVariantSerialised child; |
1398 | |
|
1399 | 0 | g_assert (g_variant_serialised_check (serialised)); |
1400 | | |
1401 | 0 | if G_LIKELY (index_ < g_variant_serialised_n_children (serialised)) |
1402 | 0 | { |
1403 | 0 | DISPATCH_CASES (serialised.type_info, |
1404 | |
|
1405 | 0 | child = gvs_/**/,/**/_get_child (serialised, index_); |
1406 | 0 | g_assert (child.size || child.data == NULL); |
1407 | 0 | g_assert (g_variant_serialised_check (child)); |
1408 | 0 | return child; |
1409 | |
|
1410 | 0 | ) |
1411 | 0 | g_assert_not_reached (); |
1412 | 0 | } |
1413 | | |
1414 | 0 | g_error ("Attempt to access item %"G_GSIZE_FORMAT |
1415 | 0 | " in a container with only %"G_GSIZE_FORMAT" items", |
1416 | 0 | index_, g_variant_serialised_n_children (serialised)); |
1417 | 0 | } |
1418 | | |
1419 | | /* < private > |
1420 | | * g_variant_serialiser_serialise: |
1421 | | * @serialised: a #GVariantSerialised, properly set up |
1422 | | * @gvs_filler: the filler function |
1423 | | * @children: an array of child items |
1424 | | * @n_children: the size of @children |
1425 | | * |
1426 | | * Writes data in serialized form. |
1427 | | * |
1428 | | * The type_info field of @serialised must be filled in to type info for |
1429 | | * the type that we are serializing. |
1430 | | * |
1431 | | * The size field of @serialised must be filled in with the value |
1432 | | * returned by a previous call to g_variant_serialiser_needed_size(). |
1433 | | * |
1434 | | * The data field of @serialised must be a pointer to a properly-aligned |
1435 | | * memory region large enough to serialize into (ie: at least as big as |
1436 | | * the size field). |
1437 | | * |
1438 | | * This function is only resonsible for serializing the top-level |
1439 | | * container. @gvs_filler is called on each child of the container in |
1440 | | * order for all of the data of that child to be filled in. |
1441 | | */ |
1442 | | void |
1443 | | g_variant_serialiser_serialise (GVariantSerialised serialised, |
1444 | | GVariantSerialisedFiller gvs_filler, |
1445 | | const gpointer *children, |
1446 | | gsize n_children) |
1447 | 0 | { |
1448 | 0 | g_assert (g_variant_serialised_check (serialised)); |
1449 | | |
1450 | 0 | DISPATCH_CASES (serialised.type_info, |
1451 | |
|
1452 | 0 | gvs_/**/,/**/_serialise (serialised, gvs_filler, |
1453 | 0 | children, n_children); |
1454 | 0 | return; |
1455 | |
|
1456 | 0 | ) |
1457 | 0 | g_assert_not_reached (); |
1458 | 0 | } |
1459 | | |
1460 | | /* < private > |
1461 | | * g_variant_serialiser_needed_size: |
1462 | | * @type_info: the type to serialize for |
1463 | | * @gvs_filler: the filler function |
1464 | | * @children: an array of child items |
1465 | | * @n_children: the size of @children |
1466 | | * |
1467 | | * Determines how much memory would be needed to serialize this value. |
1468 | | * |
1469 | | * This function is only resonsible for performing calculations for the |
1470 | | * top-level container. @gvs_filler is called on each child of the |
1471 | | * container in order to determine its size. |
1472 | | */ |
1473 | | gsize |
1474 | | g_variant_serialiser_needed_size (GVariantTypeInfo *type_info, |
1475 | | GVariantSerialisedFiller gvs_filler, |
1476 | | const gpointer *children, |
1477 | | gsize n_children) |
1478 | 0 | { |
1479 | 0 | DISPATCH_CASES (type_info, |
1480 | |
|
1481 | 0 | return gvs_/**/,/**/_needed_size (type_info, gvs_filler, |
1482 | 0 | children, n_children); |
1483 | |
|
1484 | 0 | ) |
1485 | 0 | g_assert_not_reached (); |
1486 | 0 | } |
1487 | | |
1488 | | /* Byteswapping {{{2 */ |
1489 | | |
1490 | | /* < private > |
1491 | | * g_variant_serialised_byteswap: |
1492 | | * @value: a #GVariantSerialised |
1493 | | * |
1494 | | * Byte-swap serialized data. The result of this function is only |
1495 | | * well-defined if the data is in normal form. |
1496 | | */ |
1497 | | void |
1498 | | g_variant_serialised_byteswap (GVariantSerialised serialised) |
1499 | 0 | { |
1500 | 0 | gsize fixed_size; |
1501 | 0 | guint alignment; |
1502 | |
|
1503 | 0 | g_assert (g_variant_serialised_check (serialised)); |
1504 | | |
1505 | 0 | if (!serialised.data) |
1506 | 0 | return; |
1507 | | |
1508 | | /* the types we potentially need to byteswap are |
1509 | | * exactly those with alignment requirements. |
1510 | | */ |
1511 | 0 | g_variant_type_info_query (serialised.type_info, &alignment, &fixed_size); |
1512 | 0 | if (!alignment) |
1513 | 0 | return; |
1514 | | |
1515 | | /* if fixed size and alignment are equal then we are down |
1516 | | * to the base integer type and we should swap it. the |
1517 | | * only exception to this is if we have a tuple with a |
1518 | | * single item, and then swapping it will be OK anyway. |
1519 | | */ |
1520 | 0 | if (alignment + 1 == fixed_size) |
1521 | 0 | { |
1522 | 0 | switch (fixed_size) |
1523 | 0 | { |
1524 | 0 | case 2: |
1525 | 0 | { |
1526 | 0 | guint16 *ptr = (guint16 *) serialised.data; |
1527 | |
|
1528 | 0 | g_assert_cmpint (serialised.size, ==, 2); |
1529 | 0 | *ptr = GUINT16_SWAP_LE_BE (*ptr); |
1530 | 0 | } |
1531 | 0 | return; |
1532 | | |
1533 | 0 | case 4: |
1534 | 0 | { |
1535 | 0 | guint32 *ptr = (guint32 *) serialised.data; |
1536 | |
|
1537 | 0 | g_assert_cmpint (serialised.size, ==, 4); |
1538 | 0 | *ptr = GUINT32_SWAP_LE_BE (*ptr); |
1539 | 0 | } |
1540 | 0 | return; |
1541 | | |
1542 | 0 | case 8: |
1543 | 0 | { |
1544 | 0 | guint64 *ptr = (guint64 *) serialised.data; |
1545 | |
|
1546 | 0 | g_assert_cmpint (serialised.size, ==, 8); |
1547 | 0 | *ptr = GUINT64_SWAP_LE_BE (*ptr); |
1548 | 0 | } |
1549 | 0 | return; |
1550 | | |
1551 | 0 | default: |
1552 | 0 | g_assert_not_reached (); |
1553 | 0 | } |
1554 | 0 | } |
1555 | | |
1556 | | /* else, we have a container that potentially contains |
1557 | | * some children that need to be byteswapped. |
1558 | | */ |
1559 | 0 | else |
1560 | 0 | { |
1561 | 0 | gsize children, i; |
1562 | |
|
1563 | 0 | children = g_variant_serialised_n_children (serialised); |
1564 | 0 | for (i = 0; i < children; i++) |
1565 | 0 | { |
1566 | 0 | GVariantSerialised child; |
1567 | |
|
1568 | 0 | child = g_variant_serialised_get_child (serialised, i); |
1569 | 0 | g_variant_serialised_byteswap (child); |
1570 | 0 | g_variant_type_info_unref (child.type_info); |
1571 | 0 | } |
1572 | 0 | } |
1573 | 0 | } |
1574 | | |
1575 | | /* Normal form checking {{{2 */ |
1576 | | |
1577 | | /* < private > |
1578 | | * g_variant_serialised_is_normal: |
1579 | | * @serialised: a #GVariantSerialised |
1580 | | * |
1581 | | * Determines, recursively if @serialised is in normal form. There is |
1582 | | * precisely one normal form of serialized data for each possible value. |
1583 | | * |
1584 | | * It is possible that multiple byte sequences form the serialized data |
1585 | | * for a given value if, for example, the padding bytes are filled in |
1586 | | * with something other than zeros, but only one form is the normal |
1587 | | * form. |
1588 | | */ |
1589 | | gboolean |
1590 | | g_variant_serialised_is_normal (GVariantSerialised serialised) |
1591 | 0 | { |
1592 | 0 | if (serialised.depth >= G_VARIANT_MAX_RECURSION_DEPTH) |
1593 | 0 | return FALSE; |
1594 | | |
1595 | 0 | DISPATCH_CASES (serialised.type_info, |
1596 | |
|
1597 | 0 | return gvs_/**/,/**/_is_normal (serialised); |
1598 | |
|
1599 | 0 | ) |
1600 | | |
1601 | 0 | if (serialised.data == NULL) |
1602 | 0 | return FALSE; |
1603 | | |
1604 | | /* some hard-coded terminal cases */ |
1605 | 0 | switch (g_variant_type_info_get_type_char (serialised.type_info)) |
1606 | 0 | { |
1607 | 0 | case 'b': /* boolean */ |
1608 | 0 | return serialised.data[0] < 2; |
1609 | | |
1610 | 0 | case 's': /* string */ |
1611 | 0 | return g_variant_serialiser_is_string (serialised.data, |
1612 | 0 | serialised.size); |
1613 | | |
1614 | 0 | case 'o': |
1615 | 0 | return g_variant_serialiser_is_object_path (serialised.data, |
1616 | 0 | serialised.size); |
1617 | | |
1618 | 0 | case 'g': |
1619 | 0 | return g_variant_serialiser_is_signature (serialised.data, |
1620 | 0 | serialised.size); |
1621 | | |
1622 | 0 | default: |
1623 | | /* all of the other types are fixed-sized numerical types for |
1624 | | * which all possible values are valid (including various NaN |
1625 | | * representations for floating point values). |
1626 | | */ |
1627 | 0 | return TRUE; |
1628 | 0 | } |
1629 | 0 | } |
1630 | | |
1631 | | /* Validity-checking functions {{{2 |
1632 | | * |
1633 | | * Checks if strings, object paths and signature strings are valid. |
1634 | | */ |
1635 | | |
1636 | | /* < private > |
1637 | | * g_variant_serialiser_is_string: |
1638 | | * @data: a possible string |
1639 | | * @size: the size of @data |
1640 | | * |
1641 | | * Ensures that @data is a valid string with a nul terminator at the end |
1642 | | * and no nul bytes embedded. |
1643 | | */ |
1644 | | gboolean |
1645 | | g_variant_serialiser_is_string (gconstpointer data, |
1646 | | gsize size) |
1647 | 0 | { |
1648 | 0 | const gchar *expected_end; |
1649 | 0 | const gchar *end; |
1650 | | |
1651 | | /* Strings must end with a nul terminator. */ |
1652 | 0 | if (size == 0) |
1653 | 0 | return FALSE; |
1654 | | |
1655 | 0 | expected_end = ((gchar *) data) + size - 1; |
1656 | |
|
1657 | 0 | if (*expected_end != '\0') |
1658 | 0 | return FALSE; |
1659 | | |
1660 | 0 | g_utf8_validate_len (data, size, &end); |
1661 | |
|
1662 | 0 | return end == expected_end; |
1663 | 0 | } |
1664 | | |
1665 | | /* < private > |
1666 | | * g_variant_serialiser_is_object_path: |
1667 | | * @data: a possible D-Bus object path |
1668 | | * @size: the size of @data |
1669 | | * |
1670 | | * Performs the checks for being a valid string. |
1671 | | * |
1672 | | * Also, ensures that @data is a valid D-Bus object path, as per the D-Bus |
1673 | | * specification. |
1674 | | */ |
1675 | | gboolean |
1676 | | g_variant_serialiser_is_object_path (gconstpointer data, |
1677 | | gsize size) |
1678 | 0 | { |
1679 | 0 | const gchar *string = data; |
1680 | 0 | gsize i; |
1681 | |
|
1682 | 0 | if (!g_variant_serialiser_is_string (data, size)) |
1683 | 0 | return FALSE; |
1684 | | |
1685 | | /* The path must begin with an ASCII '/' (integer 47) character */ |
1686 | 0 | if (string[0] != '/') |
1687 | 0 | return FALSE; |
1688 | | |
1689 | 0 | for (i = 1; string[i]; i++) |
1690 | | /* Each element must only contain the ASCII characters |
1691 | | * "[A-Z][a-z][0-9]_" |
1692 | | */ |
1693 | 0 | if (g_ascii_isalnum (string[i]) || string[i] == '_') |
1694 | 0 | ; |
1695 | | |
1696 | | /* must consist of elements separated by slash characters. */ |
1697 | 0 | else if (string[i] == '/') |
1698 | 0 | { |
1699 | | /* No element may be the empty string. */ |
1700 | | /* Multiple '/' characters cannot occur in sequence. */ |
1701 | 0 | if (string[i - 1] == '/') |
1702 | 0 | return FALSE; |
1703 | 0 | } |
1704 | | |
1705 | 0 | else |
1706 | 0 | return FALSE; |
1707 | | |
1708 | | /* A trailing '/' character is not allowed unless the path is the |
1709 | | * root path (a single '/' character). |
1710 | | */ |
1711 | 0 | if (i > 1 && string[i - 1] == '/') |
1712 | 0 | return FALSE; |
1713 | | |
1714 | 0 | return TRUE; |
1715 | 0 | } |
1716 | | |
1717 | | /* < private > |
1718 | | * g_variant_serialiser_is_signature: |
1719 | | * @data: a possible D-Bus signature |
1720 | | * @size: the size of @data |
1721 | | * |
1722 | | * Performs the checks for being a valid string. |
1723 | | * |
1724 | | * Also, ensures that @data is a valid D-Bus type signature, as per the |
1725 | | * D-Bus specification. Note that this means the empty string is valid, as the |
1726 | | * D-Bus specification defines a signature as “zero or more single complete |
1727 | | * types”. |
1728 | | */ |
1729 | | gboolean |
1730 | | g_variant_serialiser_is_signature (gconstpointer data, |
1731 | | gsize size) |
1732 | 0 | { |
1733 | 0 | const gchar *string = data; |
1734 | 0 | gsize first_invalid; |
1735 | |
|
1736 | 0 | if (!g_variant_serialiser_is_string (data, size)) |
1737 | 0 | return FALSE; |
1738 | | |
1739 | | /* make sure no non-definite characters appear */ |
1740 | 0 | first_invalid = strspn (string, "ybnqiuxthdvasog(){}"); |
1741 | 0 | if (string[first_invalid]) |
1742 | 0 | return FALSE; |
1743 | | |
1744 | | /* make sure each type string is well-formed */ |
1745 | 0 | while (*string) |
1746 | 0 | if (!g_variant_type_string_scan (string, NULL, &string)) |
1747 | 0 | return FALSE; |
1748 | | |
1749 | 0 | return TRUE; |
1750 | 0 | } |
1751 | | |
1752 | | /* Epilogue {{{1 */ |
1753 | | /* vim:set foldmethod=marker: */ |