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 */
65struct _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 */
75typedef 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 */
88typedef 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 */
100typedef 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 */
110static 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 */
150static 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 */
156static void
157g_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 */
198const gchar *
199g_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 */
243void
244g_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 */
268gsize
269g_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'
284static ArrayInfo *
285GV_ARRAY_INFO (GVariantTypeInfo *info)
286{
287 g_variant_type_info_check (info, GV_ARRAY_INFO_CLASS);
288
289 return (ArrayInfo *) info;
290}
291
292static void
293array_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
304static ContainerInfo *
305array_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 */
326GVariantTypeInfo *
327g_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 */
342void
343g_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'
353static TupleInfo *
354GV_TUPLE_INFO (GVariantTypeInfo *info)
355{
356 g_variant_type_info_check (info, GV_TUPLE_INFO_CLASS);
357
358 return (TupleInfo *) info;
359}
360
361static void
362tuple_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
378static void
379tuple_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 */
412static gboolean
413tuple_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 */
443static void
444tuple_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
537static gsize
538tuple_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 */
580static void
581tuple_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
618static void
619tuple_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
685static ContainerInfo *
686tuple_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 */
707gsize
708g_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 */
725const GVariantMemberInfo *
726g_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 == */
738static GRecMutex g_variant_type_info_lock;
739static 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 */
754GVariantTypeInfo *
755g_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 */
831GVariantTypeInfo *
832g_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 */
853void
854g_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
890void
891g_variant_type_info_assert_no_infos (void)
892{
893 g_assert (g_variant_type_info_table == NULL);
894}
895

source code of gtk/subprojects/glib/glib/gvarianttypeinfo.c