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