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