1 | /* |
2 | * Copyright © 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 | #include "config.h" |
22 | |
23 | #include "gvarianttypeinfo.h" |
24 | |
25 | #include <glib/gtestutils.h> |
26 | #include <glib/gthread.h> |
27 | #include <glib/gslice.h> |
28 | #include <glib/ghash.h> |
29 | #include <glib/grefcount.h> |
30 | |
31 | /* < private > |
32 | * GVariantTypeInfo: |
33 | * |
34 | * This structure contains the necessary information to facilitate the |
35 | * serialisation and fast deserialisation of a given type of GVariant |
36 | * value. A GVariant instance holds a pointer to one of these |
37 | * structures to provide for efficient operation. |
38 | * |
39 | * The GVariantTypeInfo structures for all of the base types, plus the |
40 | * "variant" type are stored in a read-only static array. |
41 | * |
42 | * For container types, a hash table and reference counting is used to |
43 | * ensure that only one of these structures exists for any given type. |
44 | * In general, a container GVariantTypeInfo will exist for a given type |
45 | * only if one or more GVariant instances of that type exist or if |
46 | * another GVariantTypeInfo has that type as a subtype. For example, if |
47 | * a process contains a single GVariant instance with type "(asv)", then |
48 | * container GVariantTypeInfo structures will exist for "(asv)" and |
49 | * for "as" (note that "s" and "v" always exist in the static array). |
50 | * |
51 | * The trickiest part of GVariantTypeInfo (and in fact, the major reason |
52 | * for its existence) is the storage of somewhat magical constants that |
53 | * allow for O(1) lookups of items in tuples. This is described below. |
54 | * |
55 | * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is |
56 | * contained inside of an ArrayInfo or TupleInfo, respectively. This |
57 | * allows the storage of the necessary additional information. |
58 | * |
59 | * 'fixed_size' is set to the fixed size of the type, if applicable, or |
60 | * 0 otherwise (since no type has a fixed size of 0). |
61 | * |
62 | * 'alignment' is set to one less than the alignment requirement for |
63 | * this type. This makes many operations much more convenient. |
64 | */ |
65 | struct _GVariantTypeInfo |
66 | { |
67 | gsize fixed_size; |
68 | guchar alignment; |
69 | guchar container_class; |
70 | }; |
71 | |
72 | /* Container types are reference counted. They also need to have their |
73 | * type string stored explicitly since it is not merely a single letter. |
74 | */ |
75 | typedef struct |
76 | { |
77 | GVariantTypeInfo info; |
78 | |
79 | gchar *type_string; |
80 | gatomicrefcount ref_count; |
81 | } ContainerInfo; |
82 | |
83 | /* For 'array' and 'maybe' types, we store some extra information on the |
84 | * end of the GVariantTypeInfo struct -- the element type (ie: "s" for |
85 | * "as"). The container GVariantTypeInfo structure holds a reference to |
86 | * the element typeinfo. |
87 | */ |
88 | typedef struct |
89 | { |
90 | ContainerInfo container; |
91 | |
92 | GVariantTypeInfo *element; |
93 | } ArrayInfo; |
94 | |
95 | /* For 'tuple' and 'dict entry' types, we store extra information for |
96 | * each member -- its type and how to find it inside the serialised data |
97 | * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'. See the |
98 | * comment on GVariantMemberInfo in gvarianttypeinfo.h. |
99 | */ |
100 | typedef struct |
101 | { |
102 | ContainerInfo container; |
103 | |
104 | GVariantMemberInfo *members; |
105 | gsize n_members; |
106 | } TupleInfo; |
107 | |
108 | |
109 | /* Hard-code the base types in a constant array */ |
110 | static const GVariantTypeInfo g_variant_type_info_basic_table[24] = { |
111 | #define fixed_aligned(x) x, x - 1, 0 |
112 | #define not_a_type 0, 0, 0 |
113 | #define unaligned 0, 0, 0 |
114 | #define aligned(x) 0, x - 1, 0 |
115 | /* 'b' */ { fixed_aligned(1) }, /* boolean */ |
116 | /* 'c' */ { not_a_type }, |
117 | /* 'd' */ { fixed_aligned(8) }, /* double */ |
118 | /* 'e' */ { not_a_type }, |
119 | /* 'f' */ { not_a_type }, |
120 | /* 'g' */ { unaligned }, /* signature string */ |
121 | /* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */ |
122 | /* 'i' */ { fixed_aligned(4) }, /* int32 */ |
123 | /* 'j' */ { not_a_type }, |
124 | /* 'k' */ { not_a_type }, |
125 | /* 'l' */ { not_a_type }, |
126 | /* 'm' */ { not_a_type }, |
127 | /* 'n' */ { fixed_aligned(2) }, /* int16 */ |
128 | /* 'o' */ { unaligned }, /* object path string */ |
129 | /* 'p' */ { not_a_type }, |
130 | /* 'q' */ { fixed_aligned(2) }, /* uint16 */ |
131 | /* 'r' */ { not_a_type }, |
132 | /* 's' */ { unaligned }, /* string */ |
133 | /* 't' */ { fixed_aligned(8) }, /* uint64 */ |
134 | /* 'u' */ { fixed_aligned(4) }, /* uint32 */ |
135 | /* 'v' */ { aligned(8) }, /* variant */ |
136 | /* 'w' */ { not_a_type }, |
137 | /* 'x' */ { fixed_aligned(8) }, /* int64 */ |
138 | /* 'y' */ { fixed_aligned(1) }, /* byte */ |
139 | #undef fixed_aligned |
140 | #undef not_a_type |
141 | #undef unaligned |
142 | #undef aligned |
143 | }; |
144 | |
145 | /* We need to have type strings to return for the base types. We store |
146 | * those in another array. Since all base type strings are single |
147 | * characters this is easy. By not storing pointers to strings into the |
148 | * GVariantTypeInfo itself, we save a bunch of relocations. |
149 | */ |
150 | static const char g_variant_type_info_basic_chars[24][2] = { |
151 | "b" , " " , "d" , " " , " " , "g" , "h" , "i" , " " , " " , " " , " " , |
152 | "n" , "o" , " " , "q" , " " , "s" , "t" , "u" , "v" , " " , "x" , "y" |
153 | }; |
154 | |
155 | /* sanity checks to make debugging easier */ |
156 | static void |
157 | g_variant_type_info_check (const GVariantTypeInfo *info, |
158 | char container_class) |
159 | { |
160 | #ifndef G_DISABLE_ASSERT |
161 | g_assert (!container_class || info->container_class == container_class); |
162 | |
163 | /* alignment can only be one of these */ |
164 | g_assert (info->alignment == 0 || info->alignment == 1 || |
165 | info->alignment == 3 || info->alignment == 7); |
166 | |
167 | if (info->container_class) |
168 | { |
169 | ContainerInfo *container = (ContainerInfo *) info; |
170 | |
171 | /* extra checks for containers */ |
172 | g_assert (!g_atomic_ref_count_compare (&container->ref_count, 0)); |
173 | g_assert (container->type_string != NULL); |
174 | } |
175 | else |
176 | { |
177 | gint index; |
178 | |
179 | /* if not a container, then ensure that it is a valid member of |
180 | * the basic types table |
181 | */ |
182 | index = info - g_variant_type_info_basic_table; |
183 | |
184 | g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24); |
185 | g_assert (G_N_ELEMENTS (g_variant_type_info_basic_chars) == 24); |
186 | g_assert (0 <= index && index < 24); |
187 | g_assert (g_variant_type_info_basic_chars[index][0] != ' '); |
188 | } |
189 | #endif /* !G_DISABLE_ASSERT */ |
190 | } |
191 | |
192 | /* < private > |
193 | * g_variant_type_info_get_type_string: |
194 | * @info: a #GVariantTypeInfo |
195 | * |
196 | * Gets the type string for @info. The string is nul-terminated. |
197 | */ |
198 | const gchar * |
199 | g_variant_type_info_get_type_string (GVariantTypeInfo *info) |
200 | { |
201 | g_variant_type_info_check (info, container_class: 0); |
202 | |
203 | if (info->container_class) |
204 | { |
205 | ContainerInfo *container = (ContainerInfo *) info; |
206 | |
207 | /* containers have their type string stored inside them */ |
208 | return container->type_string; |
209 | } |
210 | else |
211 | { |
212 | gint index; |
213 | |
214 | /* look up the type string in the base type array. the call to |
215 | * g_variant_type_info_check() above already ensured validity. |
216 | */ |
217 | index = info - g_variant_type_info_basic_table; |
218 | |
219 | return g_variant_type_info_basic_chars[index]; |
220 | } |
221 | } |
222 | |
223 | /* < private > |
224 | * g_variant_type_info_query: |
225 | * @info: a #GVariantTypeInfo |
226 | * @alignment: (out) (optional): the location to store the alignment, or %NULL |
227 | * @fixed_size: (out) (optional): the location to store the fixed size, or %NULL |
228 | * |
229 | * Queries @info to determine the alignment requirements and fixed size |
230 | * (if any) of the type. |
231 | * |
232 | * @fixed_size, if non-%NULL is set to the fixed size of the type, or 0 |
233 | * to indicate that the type is a variable-sized type. No type has a |
234 | * fixed size of 0. |
235 | * |
236 | * @alignment, if non-%NULL, is set to one less than the required |
237 | * alignment of the type. For example, for a 32bit integer, @alignment |
238 | * would be set to 3. This allows you to round an integer up to the |
239 | * proper alignment by performing the following efficient calculation: |
240 | * |
241 | * offset += ((-offset) & alignment); |
242 | */ |
243 | void |
244 | g_variant_type_info_query (GVariantTypeInfo *info, |
245 | guint *alignment, |
246 | gsize *fixed_size) |
247 | { |
248 | g_variant_type_info_check (info, container_class: 0); |
249 | |
250 | if (alignment) |
251 | *alignment = info->alignment; |
252 | |
253 | if (fixed_size) |
254 | *fixed_size = info->fixed_size; |
255 | } |
256 | |
257 | /* < private > |
258 | * g_variant_type_info_query_depth: |
259 | * @info: a #GVariantTypeInfo |
260 | * |
261 | * Queries @info to determine the depth of the type. |
262 | * |
263 | * See g_variant_type_string_get_depth_() for more details. |
264 | * |
265 | * Returns: depth of @info |
266 | * Since: 2.60 |
267 | */ |
268 | gsize |
269 | g_variant_type_info_query_depth (GVariantTypeInfo *info) |
270 | { |
271 | g_variant_type_info_check (info, container_class: 0); |
272 | |
273 | if (info->container_class) |
274 | { |
275 | ContainerInfo *container = (ContainerInfo *) info; |
276 | return g_variant_type_string_get_depth_ (type_string: container->type_string); |
277 | } |
278 | |
279 | return 1; |
280 | } |
281 | |
282 | /* == array == */ |
283 | #define GV_ARRAY_INFO_CLASS 'a' |
284 | static ArrayInfo * |
285 | GV_ARRAY_INFO (GVariantTypeInfo *info) |
286 | { |
287 | g_variant_type_info_check (info, GV_ARRAY_INFO_CLASS); |
288 | |
289 | return (ArrayInfo *) info; |
290 | } |
291 | |
292 | static void |
293 | array_info_free (GVariantTypeInfo *info) |
294 | { |
295 | ArrayInfo *array_info; |
296 | |
297 | g_assert (info->container_class == GV_ARRAY_INFO_CLASS); |
298 | array_info = (ArrayInfo *) info; |
299 | |
300 | g_variant_type_info_unref (typeinfo: array_info->element); |
301 | g_slice_free (ArrayInfo, array_info); |
302 | } |
303 | |
304 | static ContainerInfo * |
305 | array_info_new (const GVariantType *type) |
306 | { |
307 | ArrayInfo *info; |
308 | |
309 | info = g_slice_new (ArrayInfo); |
310 | info->container.info.container_class = GV_ARRAY_INFO_CLASS; |
311 | |
312 | info->element = g_variant_type_info_get (type: g_variant_type_element (type)); |
313 | info->container.info.alignment = info->element->alignment; |
314 | info->container.info.fixed_size = 0; |
315 | |
316 | return (ContainerInfo *) info; |
317 | } |
318 | |
319 | /* < private > |
320 | * g_variant_type_info_element: |
321 | * @info: a #GVariantTypeInfo for an array or maybe type |
322 | * |
323 | * Returns the element type for the array or maybe type. A reference is |
324 | * not added, so the caller must add their own. |
325 | */ |
326 | GVariantTypeInfo * |
327 | g_variant_type_info_element (GVariantTypeInfo *info) |
328 | { |
329 | return GV_ARRAY_INFO (info)->element; |
330 | } |
331 | |
332 | /* < private > |
333 | * g_variant_type_query_element: |
334 | * @info: a #GVariantTypeInfo for an array or maybe type |
335 | * @alignment: (out) (optional): the location to store the alignment, or %NULL |
336 | * @fixed_size: (out) (optional): the location to store the fixed size, or %NULL |
337 | * |
338 | * Returns the alignment requires and fixed size (if any) for the |
339 | * element type of the array. This call is a convenience wrapper around |
340 | * g_variant_type_info_element() and g_variant_type_info_query(). |
341 | */ |
342 | void |
343 | g_variant_type_info_query_element (GVariantTypeInfo *info, |
344 | guint *alignment, |
345 | gsize *fixed_size) |
346 | { |
347 | g_variant_type_info_query (info: GV_ARRAY_INFO (info)->element, |
348 | alignment, fixed_size); |
349 | } |
350 | |
351 | /* == tuple == */ |
352 | #define GV_TUPLE_INFO_CLASS 'r' |
353 | static TupleInfo * |
354 | GV_TUPLE_INFO (GVariantTypeInfo *info) |
355 | { |
356 | g_variant_type_info_check (info, GV_TUPLE_INFO_CLASS); |
357 | |
358 | return (TupleInfo *) info; |
359 | } |
360 | |
361 | static void |
362 | tuple_info_free (GVariantTypeInfo *info) |
363 | { |
364 | TupleInfo *tuple_info; |
365 | gsize i; |
366 | |
367 | g_assert (info->container_class == GV_TUPLE_INFO_CLASS); |
368 | tuple_info = (TupleInfo *) info; |
369 | |
370 | for (i = 0; i < tuple_info->n_members; i++) |
371 | g_variant_type_info_unref (typeinfo: tuple_info->members[i].type_info); |
372 | |
373 | g_slice_free1 (block_size: sizeof (GVariantMemberInfo) * tuple_info->n_members, |
374 | mem_block: tuple_info->members); |
375 | g_slice_free (TupleInfo, tuple_info); |
376 | } |
377 | |
378 | static void |
379 | tuple_allocate_members (const GVariantType *type, |
380 | GVariantMemberInfo **members, |
381 | gsize *n_members) |
382 | { |
383 | const GVariantType *item_type; |
384 | gsize i = 0; |
385 | |
386 | *n_members = g_variant_type_n_items (type); |
387 | *members = g_slice_alloc (block_size: sizeof (GVariantMemberInfo) * *n_members); |
388 | |
389 | item_type = g_variant_type_first (type); |
390 | while (item_type) |
391 | { |
392 | GVariantMemberInfo *member = &(*members)[i++]; |
393 | |
394 | member->type_info = g_variant_type_info_get (type: item_type); |
395 | item_type = g_variant_type_next (type: item_type); |
396 | |
397 | if (member->type_info->fixed_size) |
398 | member->ending_type = G_VARIANT_MEMBER_ENDING_FIXED; |
399 | else if (item_type == NULL) |
400 | member->ending_type = G_VARIANT_MEMBER_ENDING_LAST; |
401 | else |
402 | member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET; |
403 | } |
404 | |
405 | g_assert (i == *n_members); |
406 | } |
407 | |
408 | /* this is g_variant_type_info_query for a given member of the tuple. |
409 | * before the access is done, it is ensured that the item is within |
410 | * range and %FALSE is returned if not. |
411 | */ |
412 | static gboolean |
413 | tuple_get_item (TupleInfo *info, |
414 | GVariantMemberInfo *item, |
415 | gsize *d, |
416 | gsize *e) |
417 | { |
418 | if (&info->members[info->n_members] == item) |
419 | return FALSE; |
420 | |
421 | *d = item->type_info->alignment; |
422 | *e = item->type_info->fixed_size; |
423 | return TRUE; |
424 | } |
425 | |
426 | /* Read the documentation for #GVariantMemberInfo in gvarianttype.h |
427 | * before attempting to understand this. |
428 | * |
429 | * This function adds one set of "magic constant" values (for one item |
430 | * in the tuple) to the table. |
431 | * |
432 | * The algorithm in tuple_generate_table() calculates values of 'a', 'b' |
433 | * and 'c' for each item, such that the procedure for finding the item |
434 | * is to start at the end of the previous variable-sized item, add 'a', |
435 | * then round up to the nearest multiple of 'b', then then add 'c'. |
436 | * Note that 'b' is stored in the usual "one less than" form. ie: |
437 | * |
438 | * start = ROUND_UP(prev_end + a, (b + 1)) + c; |
439 | * |
440 | * We tweak these values a little to allow for a slightly easier |
441 | * computation and more compact storage. |
442 | */ |
443 | static void |
444 | tuple_table_append (GVariantMemberInfo **items, |
445 | gsize i, |
446 | gsize a, |
447 | gsize b, |
448 | gsize c) |
449 | { |
450 | GVariantMemberInfo *item = (*items)++; |
451 | |
452 | /* We can shift multiples of the alignment size from 'c' into 'a'. |
453 | * As long as we're shifting whole multiples, it won't affect the |
454 | * result. This means that we can take the "aligned" portion off of |
455 | * 'c' and add it into 'a'. |
456 | * |
457 | * Imagine (for sake of clarity) that ROUND_10 rounds up to the |
458 | * nearest 10. It is clear that: |
459 | * |
460 | * ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10) |
461 | * |
462 | * ie: remove the 10s portion of 'c' and add it onto 'a'. |
463 | * |
464 | * To put some numbers on it, imagine we start with a = 34 and c = 27: |
465 | * |
466 | * ROUND_10(34) + 27 = 40 + 27 = 67 |
467 | * |
468 | * but also, we can split 27 up into 20 and 7 and do this: |
469 | * |
470 | * ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67 |
471 | * ^^ ^ |
472 | * without affecting the result. We do that here. |
473 | * |
474 | * This reduction in the size of 'c' means that we can store it in a |
475 | * gchar instead of a gsize. Due to how the structure is packed, this |
476 | * ends up saving us 'two pointer sizes' per item in each tuple when |
477 | * allocating using GSlice. |
478 | */ |
479 | a += ~b & c; /* take the "aligned" part of 'c' and add to 'a' */ |
480 | c &= b; /* chop 'c' to contain only the unaligned part */ |
481 | |
482 | |
483 | /* Finally, we made one last adjustment. Recall: |
484 | * |
485 | * start = ROUND_UP(prev_end + a, (b + 1)) + c; |
486 | * |
487 | * Forgetting the '+ c' for the moment: |
488 | * |
489 | * ROUND_UP(prev_end + a, (b + 1)); |
490 | * |
491 | * we can do a "round up" operation by adding 1 less than the amount |
492 | * to round up to, then rounding down. ie: |
493 | * |
494 | * #define ROUND_UP(x, y) ROUND_DOWN(x + (y-1), y) |
495 | * |
496 | * Of course, for rounding down to a power of two, we can just mask |
497 | * out the appropriate number of low order bits: |
498 | * |
499 | * #define ROUND_DOWN(x, y) (x & ~(y - 1)) |
500 | * |
501 | * Which gives us |
502 | * |
503 | * #define ROUND_UP(x, y) (x + (y - 1) & ~(y - 1)) |
504 | * |
505 | * but recall that our alignment value 'b' is already "one less". |
506 | * This means that to round 'prev_end + a' up to 'b' we can just do: |
507 | * |
508 | * ((prev_end + a) + b) & ~b |
509 | * |
510 | * Associativity, and putting the 'c' back on: |
511 | * |
512 | * (prev_end + (a + b)) & ~b + c |
513 | * |
514 | * Now, since (a + b) is constant, we can just add 'b' to 'a' now and |
515 | * store that as the number to add to prev_end. Then we use ~b as the |
516 | * number to take a bitwise 'and' with. Finally, 'c' is added on. |
517 | * |
518 | * Note, however, that all the low order bits of the 'aligned' value |
519 | * are masked out and that all of the high order bits of 'c' have been |
520 | * "moved" to 'a' (in the previous step). This means that there are |
521 | * no overlapping bits in the addition -- so we can do a bitwise 'or' |
522 | * equivalently. |
523 | * |
524 | * This means that we can now compute the start address of a given |
525 | * item in the tuple using the algorithm given in the documentation |
526 | * for #GVariantMemberInfo: |
527 | * |
528 | * item_start = ((prev_end + a) & b) | c; |
529 | */ |
530 | |
531 | item->i = i; |
532 | item->a = a + b; |
533 | item->b = ~b; |
534 | item->c = c; |
535 | } |
536 | |
537 | static gsize |
538 | tuple_align (gsize offset, |
539 | guint alignment) |
540 | { |
541 | return offset + ((-offset) & alignment); |
542 | } |
543 | |
544 | /* This function is the heart of the algorithm for calculating 'i', 'a', |
545 | * 'b' and 'c' for each item in the tuple. |
546 | * |
547 | * Imagine we want to find the start of the "i" in the type "(su(qx)ni)". |
548 | * That's a string followed by a uint32, then a tuple containing a |
549 | * uint16 and an int64, then an int16, then our "i". In order to get to |
550 | * our "i" we: |
551 | * |
552 | * Start at the end of the string, align to 4 (for the uint32), add 4. |
553 | * Align to 8, add 16 (for the tuple). Align to 2, add 2 (for the |
554 | * int16). Then we're there. It turns out that, given 3 simple rules, |
555 | * we can flatten this iteration into one addition, one alignment, then |
556 | * one more addition. |
557 | * |
558 | * The loop below plays through each item in the tuple, querying its |
559 | * alignment and fixed_size into 'd' and 'e', respectively. At all |
560 | * times the variables 'a', 'b', and 'c' are maintained such that in |
561 | * order to get to the current point, you add 'a', align to 'b' then add |
562 | * 'c'. 'b' is kept in "one less than" form. For each item, the proper |
563 | * alignment is applied to find the values of 'a', 'b' and 'c' to get to |
564 | * the start of that item. Those values are recorded into the table. |
565 | * The fixed size of the item (if applicable) is then added on. |
566 | * |
567 | * These 3 rules are how 'a', 'b' and 'c' are modified for alignment and |
568 | * addition of fixed size. They have been proven correct but are |
569 | * presented here, without proof: |
570 | * |
571 | * 1) in order to "align to 'd'" where 'd' is less than or equal to the |
572 | * largest level of alignment seen so far ('b'), you align 'c' to |
573 | * 'd'. |
574 | * 2) in order to "align to 'd'" where 'd' is greater than the largest |
575 | * level of alignment seen so far, you add 'c' aligned to 'b' to the |
576 | * value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment |
577 | * seen') and reset 'c' to 0. |
578 | * 3) in order to "add 'e'", just add 'e' to 'c'. |
579 | */ |
580 | static void |
581 | tuple_generate_table (TupleInfo *info) |
582 | { |
583 | GVariantMemberInfo *items = info->members; |
584 | gsize i = -1, a = 0, b = 0, c = 0, d, e; |
585 | |
586 | /* iterate over each item in the tuple. |
587 | * 'd' will be the alignment of the item (in one-less form) |
588 | * 'e' will be the fixed size (or 0 for variable-size items) |
589 | */ |
590 | while (tuple_get_item (info, item: items, d: &d, e: &e)) |
591 | { |
592 | /* align to 'd' */ |
593 | if (d <= b) |
594 | c = tuple_align (offset: c, alignment: d); /* rule 1 */ |
595 | else |
596 | a += tuple_align (offset: c, alignment: b), b = d, c = 0; /* rule 2 */ |
597 | |
598 | /* the start of the item is at this point (ie: right after we |
599 | * have aligned for it). store this information in the table. |
600 | */ |
601 | tuple_table_append (items: &items, i, a, b, c); |
602 | |
603 | /* "move past" the item by adding in its size. */ |
604 | if (e == 0) |
605 | /* variable size: |
606 | * |
607 | * we'll have an offset stored to mark the end of this item, so |
608 | * just bump the offset index to give us a new starting point |
609 | * and reset all the counters. |
610 | */ |
611 | i++, a = b = c = 0; |
612 | else |
613 | /* fixed size */ |
614 | c += e; /* rule 3 */ |
615 | } |
616 | } |
617 | |
618 | static void |
619 | tuple_set_base_info (TupleInfo *info) |
620 | { |
621 | GVariantTypeInfo *base = &info->container.info; |
622 | |
623 | if (info->n_members > 0) |
624 | { |
625 | GVariantMemberInfo *m; |
626 | |
627 | /* the alignment requirement of the tuple is the alignment |
628 | * requirement of its largest item. |
629 | */ |
630 | base->alignment = 0; |
631 | for (m = info->members; m < &info->members[info->n_members]; m++) |
632 | /* can find the max of a list of "one less than" powers of two |
633 | * by 'or'ing them |
634 | */ |
635 | base->alignment |= m->type_info->alignment; |
636 | |
637 | m--; /* take 'm' back to the last item */ |
638 | |
639 | /* the structure only has a fixed size if no variable-size |
640 | * offsets are stored and the last item is fixed-sized too (since |
641 | * an offset is never stored for the last item). |
642 | */ |
643 | if (m->i == (gsize) -1 && m->type_info->fixed_size) |
644 | /* in that case, the fixed size can be found by finding the |
645 | * start of the last item (in the usual way) and adding its |
646 | * fixed size. |
647 | * |
648 | * if a tuple has a fixed size then it is always a multiple of |
649 | * the alignment requirement (to make packing into arrays |
650 | * easier) so we round up to that here. |
651 | */ |
652 | base->fixed_size = |
653 | tuple_align (offset: ((m->a & m->b) | m->c) + m->type_info->fixed_size, |
654 | alignment: base->alignment); |
655 | else |
656 | /* else, the tuple is not fixed size */ |
657 | base->fixed_size = 0; |
658 | } |
659 | else |
660 | { |
661 | /* the empty tuple: '()'. |
662 | * |
663 | * has a size of 1 and a no alignment requirement. |
664 | * |
665 | * It has a size of 1 (not 0) for two practical reasons: |
666 | * |
667 | * 1) So we can determine how many of them are in an array |
668 | * without dividing by zero or without other tricks. |
669 | * |
670 | * 2) Even if we had some trick to know the number of items in |
671 | * the array (as GVariant did at one time) this would open a |
672 | * potential denial of service attack: an attacker could send |
673 | * you an extremely small array (in terms of number of bytes) |
674 | * containing trillions of zero-sized items. If you iterated |
675 | * over this array you would effectively infinite-loop your |
676 | * program. By forcing a size of at least one, we bound the |
677 | * amount of computation done in response to a message to a |
678 | * reasonable function of the size of that message. |
679 | */ |
680 | base->alignment = 0; |
681 | base->fixed_size = 1; |
682 | } |
683 | } |
684 | |
685 | static ContainerInfo * |
686 | tuple_info_new (const GVariantType *type) |
687 | { |
688 | TupleInfo *info; |
689 | |
690 | info = g_slice_new (TupleInfo); |
691 | info->container.info.container_class = GV_TUPLE_INFO_CLASS; |
692 | |
693 | tuple_allocate_members (type, members: &info->members, n_members: &info->n_members); |
694 | tuple_generate_table (info); |
695 | tuple_set_base_info (info); |
696 | |
697 | return (ContainerInfo *) info; |
698 | } |
699 | |
700 | /* < private > |
701 | * g_variant_type_info_n_members: |
702 | * @info: a #GVariantTypeInfo for a tuple or dictionary entry type |
703 | * |
704 | * Returns the number of members in a tuple or dictionary entry type. |
705 | * For a dictionary entry this will always be 2. |
706 | */ |
707 | gsize |
708 | g_variant_type_info_n_members (GVariantTypeInfo *info) |
709 | { |
710 | return GV_TUPLE_INFO (info)->n_members; |
711 | } |
712 | |
713 | /* < private > |
714 | * g_variant_type_info_member_info: |
715 | * @info: a #GVariantTypeInfo for a tuple or dictionary entry type |
716 | * @index: the member to fetch information for |
717 | * |
718 | * Returns the #GVariantMemberInfo for a given member. See |
719 | * documentation for that structure for why you would want this |
720 | * information. |
721 | * |
722 | * @index must refer to a valid child (ie: strictly less than |
723 | * g_variant_type_info_n_members() returns). |
724 | */ |
725 | const GVariantMemberInfo * |
726 | g_variant_type_info_member_info (GVariantTypeInfo *info, |
727 | gsize index) |
728 | { |
729 | TupleInfo *tuple_info = GV_TUPLE_INFO (info); |
730 | |
731 | if (index < tuple_info->n_members) |
732 | return &tuple_info->members[index]; |
733 | |
734 | return NULL; |
735 | } |
736 | |
737 | /* == new/ref/unref == */ |
738 | static GRecMutex g_variant_type_info_lock; |
739 | static GHashTable *g_variant_type_info_table; |
740 | |
741 | /* < private > |
742 | * g_variant_type_info_get: |
743 | * @type: a #GVariantType |
744 | * |
745 | * Returns a reference to a #GVariantTypeInfo for @type. |
746 | * |
747 | * If an info structure already exists for this type, a new reference is |
748 | * returned. If not, the required calculations are performed and a new |
749 | * info structure is returned. |
750 | * |
751 | * It is appropriate to call g_variant_type_info_unref() on the return |
752 | * value. |
753 | */ |
754 | GVariantTypeInfo * |
755 | g_variant_type_info_get (const GVariantType *type) |
756 | { |
757 | char type_char; |
758 | |
759 | type_char = g_variant_type_peek_string (type)[0]; |
760 | |
761 | if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE || |
762 | type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY || |
763 | type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE || |
764 | type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY) |
765 | { |
766 | GVariantTypeInfo *info; |
767 | gchar *type_string; |
768 | |
769 | type_string = g_variant_type_dup_string (type); |
770 | |
771 | g_rec_mutex_lock (rec_mutex: &g_variant_type_info_lock); |
772 | |
773 | if (g_variant_type_info_table == NULL) |
774 | g_variant_type_info_table = g_hash_table_new (hash_func: g_str_hash, |
775 | key_equal_func: g_str_equal); |
776 | info = g_hash_table_lookup (hash_table: g_variant_type_info_table, key: type_string); |
777 | |
778 | if (info == NULL) |
779 | { |
780 | ContainerInfo *container; |
781 | |
782 | if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE || |
783 | type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY) |
784 | { |
785 | container = array_info_new (type); |
786 | } |
787 | else /* tuple or dict entry */ |
788 | { |
789 | container = tuple_info_new (type); |
790 | } |
791 | |
792 | info = (GVariantTypeInfo *) container; |
793 | container->type_string = type_string; |
794 | g_atomic_ref_count_init (arc: &container->ref_count); |
795 | |
796 | g_hash_table_insert (hash_table: g_variant_type_info_table, key: type_string, value: info); |
797 | type_string = NULL; |
798 | } |
799 | else |
800 | g_variant_type_info_ref (typeinfo: info); |
801 | |
802 | g_rec_mutex_unlock (rec_mutex: &g_variant_type_info_lock); |
803 | g_variant_type_info_check (info, container_class: 0); |
804 | g_free (mem: type_string); |
805 | |
806 | return info; |
807 | } |
808 | else |
809 | { |
810 | const GVariantTypeInfo *info; |
811 | int index; |
812 | |
813 | index = type_char - 'b'; |
814 | g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24); |
815 | g_assert_cmpint (0, <=, index); |
816 | g_assert_cmpint (index, <, 24); |
817 | |
818 | info = g_variant_type_info_basic_table + index; |
819 | g_variant_type_info_check (info, container_class: 0); |
820 | |
821 | return (GVariantTypeInfo *) info; |
822 | } |
823 | } |
824 | |
825 | /* < private > |
826 | * g_variant_type_info_ref: |
827 | * @info: a #GVariantTypeInfo |
828 | * |
829 | * Adds a reference to @info. |
830 | */ |
831 | GVariantTypeInfo * |
832 | g_variant_type_info_ref (GVariantTypeInfo *info) |
833 | { |
834 | g_variant_type_info_check (info, container_class: 0); |
835 | |
836 | if (info->container_class) |
837 | { |
838 | ContainerInfo *container = (ContainerInfo *) info; |
839 | |
840 | g_atomic_ref_count_inc (arc: &container->ref_count); |
841 | } |
842 | |
843 | return info; |
844 | } |
845 | |
846 | /* < private > |
847 | * g_variant_type_info_unref: |
848 | * @info: a #GVariantTypeInfo |
849 | * |
850 | * Releases a reference held on @info. This may result in @info being |
851 | * freed. |
852 | */ |
853 | void |
854 | g_variant_type_info_unref (GVariantTypeInfo *info) |
855 | { |
856 | g_variant_type_info_check (info, container_class: 0); |
857 | |
858 | if (info->container_class) |
859 | { |
860 | ContainerInfo *container = (ContainerInfo *) info; |
861 | |
862 | g_rec_mutex_lock (rec_mutex: &g_variant_type_info_lock); |
863 | if (g_atomic_ref_count_dec (arc: &container->ref_count)) |
864 | { |
865 | g_hash_table_remove (hash_table: g_variant_type_info_table, |
866 | key: container->type_string); |
867 | if (g_hash_table_size (hash_table: g_variant_type_info_table) == 0) |
868 | { |
869 | g_hash_table_unref (hash_table: g_variant_type_info_table); |
870 | g_variant_type_info_table = NULL; |
871 | } |
872 | g_rec_mutex_unlock (rec_mutex: &g_variant_type_info_lock); |
873 | |
874 | g_free (mem: container->type_string); |
875 | |
876 | if (info->container_class == GV_ARRAY_INFO_CLASS) |
877 | array_info_free (info); |
878 | |
879 | else if (info->container_class == GV_TUPLE_INFO_CLASS) |
880 | tuple_info_free (info); |
881 | |
882 | else |
883 | g_assert_not_reached (); |
884 | } |
885 | else |
886 | g_rec_mutex_unlock (rec_mutex: &g_variant_type_info_lock); |
887 | } |
888 | } |
889 | |
890 | void |
891 | g_variant_type_info_assert_no_infos (void) |
892 | { |
893 | g_assert (g_variant_type_info_table == NULL); |
894 | } |
895 | |