GDelayedSettingsBackend: another mandatory fixup
[glib.git] / glib / gvarianttypeinfo.c
blob52f7b7d7636e987f14a2e54212a12057355df26b
1 /*
2 * Copyright © 2008 Ryan Lortie
3 * Copyright © 2010 Codethink Limited
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 of the License, or (at your option) any later version.
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.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Ryan Lortie <desrt@desrt.ca>
23 #include "config.h"
25 #include "gvarianttypeinfo.h"
27 #include <glib/gtestutils.h>
28 #include <glib/gthread.h>
29 #include <glib/ghash.h>
31 #include "galias.h"
33 /* < private >
34 * GVariantTypeInfo:
36 * This structure contains the necessary information to facilitate the
37 * serialisation and fast deserialisation of a given type of GVariant
38 * value. A GVariant instance holds a pointer to one of these
39 * structures to provide for efficient operation.
41 * The GVariantTypeInfo structures for all of the base types, plus the
42 * "variant" type are stored in a read-only static array.
44 * For container types, a hash table and reference counting is used to
45 * ensure that only one of these structures exists for any given type.
46 * In general, a container GVariantTypeInfo will exist for a given type
47 * only if one or more GVariant instances of that type exist or if
48 * another GVariantTypeInfo has that type as a subtype. For example, if
49 * a process contains a single GVariant instance with type "(asv)", then
50 * container GVariantTypeInfo structures will exist for "(asv)" and
51 * for "as" (note that "s" and "v" always exist in the static array).
53 * The trickiest part of GVariantTypeInfo (and in fact, the major reason
54 * for its existance) is the storage of somewhat magical constants that
55 * allow for O(1) lookups of items in tuples. This is described below.
57 * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is
58 * contained inside of an ArrayInfo or TupleInfo, respectively. This
59 * allows the storage of the necessary additional information.
61 * 'fixed_size' is set to the fixed size of the type, if applicable, or
62 * 0 otherwise (since no type has a fixed size of 0).
64 * 'alignment' is set to one less than the alignment requirement for
65 * this type. This makes many operations much more convenient.
67 struct _GVariantTypeInfo
69 gsize fixed_size;
70 guchar alignment;
71 guchar container_class;
74 /* Container types are reference counted. They also need to have their
75 * type string stored explicitly since it is not merely a single letter.
77 typedef struct
79 GVariantTypeInfo info;
81 gchar *type_string;
82 gint ref_count;
83 } ContainerInfo;
85 /* For 'array' and 'maybe' types, we store some extra information on the
86 * end of the GVariantTypeInfo struct -- the element type (ie: "s" for
87 * "as"). The container GVariantTypeInfo structure holds a reference to
88 * the element typeinfo.
90 typedef struct
92 ContainerInfo container;
94 GVariantTypeInfo *element;
95 } ArrayInfo;
97 /* For 'tuple' and 'dict entry' types, we store extra information for
98 * each member -- its type and how to find it inside the serialised data
99 * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'. See the
100 * comment on GVariantMemberInfo in gvarianttypeinfo.h.
102 typedef struct
104 ContainerInfo container;
106 GVariantMemberInfo *members;
107 gsize n_members;
108 } TupleInfo;
111 /* Hard-code the base types in a constant array */
112 static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
113 #define fixed_aligned(x) x, x - 1
114 #define not_a_type 0,
115 #define unaligned 0, 0
116 #define aligned(x) 0, x - 1
117 /* 'b' */ { fixed_aligned(1) }, /* boolean */
118 /* 'c' */ { not_a_type },
119 /* 'd' */ { fixed_aligned(8) }, /* double */
120 /* 'e' */ { not_a_type },
121 /* 'f' */ { not_a_type },
122 /* 'g' */ { unaligned }, /* signature string */
123 /* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */
124 /* 'i' */ { fixed_aligned(4) }, /* int32 */
125 /* 'j' */ { not_a_type },
126 /* 'k' */ { not_a_type },
127 /* 'l' */ { not_a_type },
128 /* 'm' */ { not_a_type },
129 /* 'n' */ { fixed_aligned(2) }, /* int16 */
130 /* 'o' */ { unaligned }, /* object path string */
131 /* 'p' */ { not_a_type },
132 /* 'q' */ { fixed_aligned(2) }, /* uint16 */
133 /* 'r' */ { not_a_type },
134 /* 's' */ { unaligned }, /* string */
135 /* 't' */ { fixed_aligned(8) }, /* uint64 */
136 /* 'u' */ { fixed_aligned(4) }, /* uint32 */
137 /* 'v' */ { aligned(8) }, /* variant */
138 /* 'w' */ { not_a_type },
139 /* 'x' */ { fixed_aligned(8) }, /* int64 */
140 /* 'y' */ { fixed_aligned(1) }, /* byte */
141 #undef fixed_aligned
142 #undef not_a_type
143 #undef unaligned
144 #undef aligned
147 /* We need to have type strings to return for the base types. We store
148 * those in another array. Since all base type strings are single
149 * characters this is easy. By not storing pointers to strings into the
150 * GVariantTypeInfo itself, we save a bunch of relocations.
152 static const char g_variant_type_info_basic_chars[24][2] = {
153 "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
154 "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
157 /* sanity checks to make debugging easier */
158 static void
159 g_variant_type_info_check (const GVariantTypeInfo *info,
160 char container_class)
162 g_assert (!container_class || info->container_class == container_class);
164 /* alignment can only be one of these */
165 g_assert (info->alignment == 0 || info->alignment == 1 ||
166 info->alignment == 3 || info->alignment == 7);
168 if (info->container_class)
170 ContainerInfo *container = (ContainerInfo *) info;
172 /* extra checks for containers */
173 g_assert_cmpint (container->ref_count, >, 0);
174 g_assert (container->type_string != NULL);
176 else
178 gint index;
180 /* if not a container, then ensure that it is a valid member of
181 * the basic types table
183 index = info - g_variant_type_info_basic_table;
185 g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
186 g_assert (G_N_ELEMENTS (g_variant_type_info_basic_chars) == 24);
187 g_assert (0 <= index && index < 24);
188 g_assert (g_variant_type_info_basic_chars[index][0] != ' ');
192 /* < private >
193 * g_variant_type_info_get_type_string:
194 * @info: a #GVariantTypeInfo
196 * Gets the type string for @info. The string is nul-terminated.
198 const gchar *
199 g_variant_type_info_get_type_string (GVariantTypeInfo *info)
201 g_variant_type_info_check (info, 0);
203 if (info->container_class)
205 ContainerInfo *container = (ContainerInfo *) info;
207 /* containers have their type string stored inside them */
208 return container->type_string;
210 else
212 gint index;
214 /* look up the type string in the base type array. the call to
215 * g_variant_type_info_check() above already ensured validity.
217 index = info - g_variant_type_info_basic_table;
219 return g_variant_type_info_basic_chars[index];
223 /* < private >
224 * g_variant_type_info_query:
225 * @info: a #GVariantTypeInfo
226 * @alignment: the location to store the alignment, or %NULL
227 * @fixed_size: the location to store the fixed size, or %NULL
229 * Queries @info to determine the alignment requirements and fixed size
230 * (if any) of the type.
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.
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:
241 * offset += ((-offset) & alignment);
243 void
244 g_variant_type_info_query (GVariantTypeInfo *info,
245 guint *alignment,
246 gsize *fixed_size)
248 g_variant_type_info_check (info, 0);
250 if (alignment)
251 *alignment = info->alignment;
253 if (fixed_size)
254 *fixed_size = info->fixed_size;
257 /* == array == */
258 #define ARRAY_INFO_CLASS 'a'
259 static ArrayInfo *
260 ARRAY_INFO (GVariantTypeInfo *info)
262 g_variant_type_info_check (info, ARRAY_INFO_CLASS);
264 return (ArrayInfo *) info;
267 static void
268 array_info_free (GVariantTypeInfo *info)
270 ArrayInfo *array_info;
272 g_assert (info->container_class == ARRAY_INFO_CLASS);
273 array_info = (ArrayInfo *) info;
275 g_variant_type_info_unref (array_info->element);
276 g_slice_free (ArrayInfo, array_info);
279 static ContainerInfo *
280 array_info_new (const GVariantType *type)
282 ArrayInfo *info;
284 info = g_slice_new (ArrayInfo);
285 info->container.info.container_class = ARRAY_INFO_CLASS;
287 info->element = g_variant_type_info_get (g_variant_type_element (type));
288 info->container.info.alignment = info->element->alignment;
289 info->container.info.fixed_size = 0;
291 return (ContainerInfo *) info;
294 /* < private >
295 * g_variant_type_info_element:
296 * @info: a #GVariantTypeInfo for an array or maybe type
298 * Returns the element type for the array or maybe type. A reference is
299 * not added, so the caller must add their own.
301 GVariantTypeInfo *
302 g_variant_type_info_element (GVariantTypeInfo *info)
304 return ARRAY_INFO (info)->element;
307 /* < private >
308 * g_variant_type_query_element:
309 * @info: a #GVariantTypeInfo for an array or maybe type
310 * @alignment: the location to store the alignment, or %NULL
311 * @fixed_size: the location to store the fixed size, or %NULL
313 * Returns the alignment requires and fixed size (if any) for the
314 * element type of the array. This call is a convenience wrapper around
315 * g_variant_type_info_element() and g_variant_type_info_query().
317 void
318 g_variant_type_info_query_element (GVariantTypeInfo *info,
319 guint *alignment,
320 gsize *fixed_size)
322 g_variant_type_info_query (ARRAY_INFO (info)->element,
323 alignment, fixed_size);
326 /* == tuple == */
327 #define TUPLE_INFO_CLASS 'r'
328 static TupleInfo *
329 TUPLE_INFO (GVariantTypeInfo *info)
331 g_variant_type_info_check (info, TUPLE_INFO_CLASS);
333 return (TupleInfo *) info;
336 static void
337 tuple_info_free (GVariantTypeInfo *info)
339 TupleInfo *tuple_info;
340 gint i;
342 g_assert (info->container_class == TUPLE_INFO_CLASS);
343 tuple_info = (TupleInfo *) info;
345 for (i = 0; i < tuple_info->n_members; i++)
346 g_variant_type_info_unref (tuple_info->members[i].type_info);
348 g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members,
349 tuple_info->members);
350 g_slice_free (TupleInfo, tuple_info);
353 static void
354 tuple_allocate_members (const GVariantType *type,
355 GVariantMemberInfo **members,
356 gsize *n_members)
358 const GVariantType *item_type;
359 gsize i = 0;
361 *n_members = g_variant_type_n_items (type);
362 *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members);
364 item_type = g_variant_type_first (type);
365 while (item_type)
367 GVariantMemberInfo *member = &(*members)[i++];
369 member->type_info = g_variant_type_info_get (item_type);
370 item_type = g_variant_type_next (item_type);
372 if (member->type_info->fixed_size)
373 member->ending_type = G_VARIANT_MEMBER_ENDING_FIXED;
374 else if (item_type == NULL)
375 member->ending_type = G_VARIANT_MEMBER_ENDING_LAST;
376 else
377 member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET;
380 g_assert (i == *n_members);
383 /* this is g_variant_type_info_query for a given member of the tuple.
384 * before the access is done, it is ensured that the item is within
385 * range and %FALSE is returned if not.
387 static gboolean
388 tuple_get_item (TupleInfo *info,
389 GVariantMemberInfo *item,
390 gsize *d,
391 gsize *e)
393 if (&info->members[info->n_members] == item)
394 return FALSE;
396 *d = item->type_info->alignment;
397 *e = item->type_info->fixed_size;
398 return TRUE;
401 /* Read the documentation for #GVariantMemberInfo in gvarianttype.h
402 * before attempting to understand this.
404 * This function adds one set of "magic constant" values (for one item
405 * in the tuple) to the table.
407 * The algorithm in tuple_generate_table() calculates values of 'a', 'b'
408 * and 'c' for each item, such that the procedure for finding the item
409 * is to start at the end of the previous variable-sized item, add 'a',
410 * then round up to the nearest multiple of 'b', then then add 'c'.
411 * Note that 'b' is stored in the usual "one less than" form. ie:
413 * start = ROUND_UP(prev_end + a, (b + 1)) + c;
415 * We tweak these values a little to allow for a slightly easier
416 * computation and more compact storage.
418 static void
419 tuple_table_append (GVariantMemberInfo **items,
420 gsize i,
421 gsize a,
422 gsize b,
423 gsize c)
425 GVariantMemberInfo *item = (*items)++;
427 /* We can shift multiples of the alignment size from 'c' into 'a'.
428 * As long as we're shifting whole multiples, it won't affect the
429 * result. This means that we can take the "aligned" portion off of
430 * 'c' and add it into 'a'.
432 * Imagine (for sake of clarity) that ROUND_10 rounds up to the
433 * nearest 10. It is clear that:
435 * ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10)
437 * ie: remove the 10s portion of 'c' and add it onto 'a'.
439 * To put some numbers on it, imagine we start with a = 34 and c = 27:
441 * ROUND_10(34) + 27 = 40 + 27 = 67
443 * but also, we can split 27 up into 20 and 7 and do this:
445 * ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67
446 * ^^ ^
447 * without affecting the result. We do that here.
449 * This reduction in the size of 'c' means that we can store it in a
450 * gchar instead of a gsize. Due to how the structure is packed, this
451 * ends up saving us 'two pointer sizes' per item in each tuple when
452 * allocating using GSlice.
454 a += ~b & c; /* take the "aligned" part of 'c' and add to 'a' */
455 c &= b; /* chop 'c' to contain only the unaligned part */
458 /* Finally, we made one last adjustment. Recall:
460 * start = ROUND_UP(prev_end + a, (b + 1)) + c;
462 * Forgetting the '+ c' for the moment:
464 * ROUND_UP(prev_end + a, (b + 1));
466 * we can do a "round up" operation by adding 1 less than the amount
467 * to round up to, then rounding down. ie:
469 * #define ROUND_UP(x, y) ROUND_DOWN(x + (y-1), y)
471 * Of course, for rounding down to a power of two, we can just mask
472 * out the appropriate number of low order bits:
474 * #define ROUND_DOWN(x, y) (x & ~(y - 1))
476 * Which gives us
478 * #define ROUND_UP(x, y) (x + (y - 1) & ~(y - 1))
480 * but recall that our alignment value 'b' is already "one less".
481 * This means that to round 'prev_end + a' up to 'b' we can just do:
483 * ((prev_end + a) + b) & ~b
485 * Associativity, and putting the 'c' back on:
487 * (prev_end + (a + b)) & ~b + c
489 * Now, since (a + b) is constant, we can just add 'b' to 'a' now and
490 * store that as the number to add to prev_end. Then we use ~b as the
491 * number to take a bitwise 'and' with. Finally, 'c' is added on.
493 * Note, however, that all the low order bits of the 'aligned' value
494 * are masked out and that all of the high order bits of 'c' have been
495 * "moved" to 'a' (in the previous step). This means that there are
496 * no overlapping bits in the addition -- so we can do a bitwise 'or'
497 * equivalently.
499 * This means that we can now compute the start address of a given
500 * item in the tuple using the algorithm given in the documentation
501 * for #GVariantMemberInfo:
503 * item_start = ((prev_end + a) & b) | c;
506 item->i = i;
507 item->a = a + b;
508 item->b = ~b;
509 item->c = c;
512 static gsize
513 tuple_align (gsize offset,
514 guint alignment)
516 return offset + ((-offset) & alignment);
519 /* This function is the heart of the algorithm for calculating 'i', 'a',
520 * 'b' and 'c' for each item in the tuple.
522 * Imagine we want to find the start of the "i" in the type "(su(qx)ni)".
523 * That's a string followed by a uint32, then a tuple containing a
524 * uint16 and a int64, then an int16, then our "i". In order to get to
525 * our "i" we:
527 * Start at the end of the string, align to 4 (for the uint32), add 4.
528 * Align to 8, add 16 (for the tuple). Align to 2, add 2 (for the
529 * int16). Then we're there. It turns out that, given 3 simple rules,
530 * we can flatten this iteration into one addition, one alignment, then
531 * one more addition.
533 * The loop below plays through each item in the tuple, querying its
534 * alignment and fixed_size into 'd' and 'e', respectively. At all
535 * times the variables 'a', 'b', and 'c' are maintained such that in
536 * order to get to the current point, you add 'a', align to 'b' then add
537 * 'c'. 'b' is kept in "one less than" form. For each item, the proper
538 * alignment is applied to find the values of 'a', 'b' and 'c' to get to
539 * the start of that item. Those values are recorded into the table.
540 * The fixed size of the item (if applicable) is then added on.
542 * These 3 rules are how 'a', 'b' and 'c' are modified for alignment and
543 * addition of fixed size. They have been proven correct but are
544 * presented here, without proof:
546 * 1) in order to "align to 'd'" where 'd' is less than or equal to the
547 * largest level of alignment seen so far ('b'), you align 'c' to
548 * 'd'.
549 * 2) in order to "align to 'd'" where 'd' is greater than the largest
550 * level of alignment seen so far, you add 'c' aligned to 'b' to the
551 * value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment
552 * seen') and reset 'c' to 0.
553 * 3) in order to "add 'e'", just add 'e' to 'c'.
555 static void
556 tuple_generate_table (TupleInfo *info)
558 GVariantMemberInfo *items = info->members;
559 gsize i = -1, a = 0, b = 0, c = 0, d, e;
561 /* iterate over each item in the tuple.
562 * 'd' will be the alignment of the item (in one-less form)
563 * 'e' will be the fixed size (or 0 for variable-size items)
565 while (tuple_get_item (info, items, &d, &e))
567 /* align to 'd' */
568 if (d <= b)
569 c = tuple_align (c, d); /* rule 1 */
570 else
571 a += tuple_align (c, b), b = d, c = 0; /* rule 2 */
573 /* the start of the item is at this point (ie: right after we
574 * have aligned for it). store this information in the table.
576 tuple_table_append (&items, i, a, b, c);
578 /* "move past" the item by adding in its size. */
579 if (e == 0)
580 /* variable size:
582 * we'll have an offset stored to mark the end of this item, so
583 * just bump the offset index to give us a new starting point
584 * and reset all the counters.
586 i++, a = b = c = 0;
587 else
588 /* fixed size */
589 c += e; /* rule 3 */
593 static void
594 tuple_set_base_info (TupleInfo *info)
596 GVariantTypeInfo *base = &info->container.info;
598 if (info->n_members > 0)
600 GVariantMemberInfo *m;
602 /* the alignment requirement of the tuple is the alignment
603 * requirement of its largest item.
605 base->alignment = 0;
606 for (m = info->members; m < &info->members[info->n_members]; m++)
607 /* can find the max of a list of "one less than" powers of two
608 * by 'or'ing them
610 base->alignment |= m->type_info->alignment;
612 m--; /* take 'm' back to the last item */
614 /* the structure only has a fixed size if no variable-size
615 * offsets are stored and the last item is fixed-sized too (since
616 * an offset is never stored for the last item).
618 if (m->i == -1 && m->type_info->fixed_size)
619 /* in that case, the fixed size can be found by finding the
620 * start of the last item (in the usual way) and adding its
621 * fixed size.
623 * if a tuple has a fixed size then it is always a multiple of
624 * the alignment requirement (to make packing into arrays
625 * easier) so we round up to that here.
627 base->fixed_size =
628 tuple_align (((m->a & m->b) | m->c) + m->type_info->fixed_size,
629 base->alignment);
630 else
631 /* else, the tuple is not fixed size */
632 base->fixed_size = 0;
634 else
636 /* the empty tuple: '()'.
638 * has a size of 1 and an no alignment requirement.
640 * It has a size of 1 (not 0) for two practical reasons:
642 * 1) So we can determine how many of them are in an array
643 * without dividing by zero or without other tricks.
645 * 2) Even if we had some trick to know the number of items in
646 * the array (as GVariant did at one time) this would open a
647 * potential denial of service attack: an attacker could send
648 * you an extremely small array (in terms of number of bytes)
649 * containing trillions of zero-sized items. If you iterated
650 * over this array you would effectively infinite-loop your
651 * program. By forcing a size of at least one, we bound the
652 * amount of computation done in response to a message to a
653 * reasonable function of the size of that message.
655 base->alignment = 0;
656 base->fixed_size = 1;
660 static ContainerInfo *
661 tuple_info_new (const GVariantType *type)
663 TupleInfo *info;
665 info = g_slice_new (TupleInfo);
666 info->container.info.container_class = TUPLE_INFO_CLASS;
668 tuple_allocate_members (type, &info->members, &info->n_members);
669 tuple_generate_table (info);
670 tuple_set_base_info (info);
672 return (ContainerInfo *) info;
675 /* < private >
676 * g_variant_type_info_n_members:
677 * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
679 * Returns the number of members in a tuple or dictionary entry type.
680 * For a dictionary entry this will always be 2.
682 gsize
683 g_variant_type_info_n_members (GVariantTypeInfo *info)
685 return TUPLE_INFO (info)->n_members;
688 /* < private >
689 * g_variant_type_info_member_info:
690 * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
691 * @index: the member to fetch information for
693 * Returns the #GVariantMemberInfo for a given member. See
694 * documentation for that structure for why you would want this
695 * information.
697 * @index must refer to a valid child (ie: strictly less than
698 * g_variant_type_info_n_members() returns).
700 const GVariantMemberInfo *
701 g_variant_type_info_member_info (GVariantTypeInfo *info,
702 gsize index)
704 TupleInfo *tuple_info = TUPLE_INFO (info);
706 if (index < tuple_info->n_members)
707 return &tuple_info->members[index];
709 return NULL;
712 /* == new/ref/unref == */
713 static GStaticRecMutex g_variant_type_info_lock = G_STATIC_REC_MUTEX_INIT;
714 static GHashTable *g_variant_type_info_table;
716 /* < private >
717 * g_variant_type_info_get:
718 * @type: a #GVariantType
720 * Returns a reference to a #GVariantTypeInfo for @type.
722 * If an info structure already exists for this type, a new reference is
723 * returned. If not, the required calculations are performed and a new
724 * info structure is returned.
726 * It is appropriate to call g_variant_type_info_unref() on the return
727 * value.
729 GVariantTypeInfo *
730 g_variant_type_info_get (const GVariantType *type)
732 char type_char;
734 type_char = g_variant_type_peek_string (type)[0];
736 if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
737 type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY ||
738 type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE ||
739 type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY)
741 GVariantTypeInfo *info;
742 gchar *type_string;
744 type_string = g_variant_type_dup_string (type);
746 g_static_rec_mutex_lock (&g_variant_type_info_lock);
748 if (g_variant_type_info_table == NULL)
749 g_variant_type_info_table = g_hash_table_new (g_str_hash,
750 g_str_equal);
751 info = g_hash_table_lookup (g_variant_type_info_table, type_string);
753 if (info == NULL)
755 ContainerInfo *container;
757 if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
758 type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY)
760 container = array_info_new (type);
762 else /* tuple or dict entry */
764 container = tuple_info_new (type);
767 info = (GVariantTypeInfo *) container;
768 container->type_string = type_string;
769 container->ref_count = 1;
771 g_hash_table_insert (g_variant_type_info_table, type_string, info);
772 type_string = NULL;
774 else
775 g_variant_type_info_ref (info);
777 g_static_rec_mutex_unlock (&g_variant_type_info_lock);
778 g_variant_type_info_check (info, 0);
779 g_free (type_string);
781 return info;
783 else
785 const GVariantTypeInfo *info;
786 int index;
788 index = type_char - 'b';
789 g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
790 g_assert_cmpint (0, <=, index);
791 g_assert_cmpint (index, <, 24);
793 info = g_variant_type_info_basic_table + index;
794 g_variant_type_info_check (info, 0);
796 return (GVariantTypeInfo *) info;
800 /* < private >
801 * g_variant_type_info_ref:
802 * @info: a #GVariantTypeInfo
804 * Adds a reference to @info.
806 GVariantTypeInfo *
807 g_variant_type_info_ref (GVariantTypeInfo *info)
809 g_variant_type_info_check (info, 0);
811 if (info->container_class)
813 ContainerInfo *container = (ContainerInfo *) info;
815 g_assert_cmpint (container->ref_count, >, 0);
816 g_atomic_int_inc (&container->ref_count);
819 return info;
822 /* < private >
823 * g_variant_type_info_unref:
824 * @info: a #GVariantTypeInfo
826 * Releases a reference held on @info. This may result in @info being
827 * freed.
829 void
830 g_variant_type_info_unref (GVariantTypeInfo *info)
832 g_variant_type_info_check (info, 0);
834 if (info->container_class)
836 ContainerInfo *container = (ContainerInfo *) info;
838 g_static_rec_mutex_lock (&g_variant_type_info_lock);
839 if (g_atomic_int_dec_and_test (&container->ref_count))
841 g_hash_table_remove (g_variant_type_info_table,
842 container->type_string);
843 if (g_hash_table_size (g_variant_type_info_table) == 0)
845 g_hash_table_unref (g_variant_type_info_table);
846 g_variant_type_info_table = NULL;
848 g_static_rec_mutex_unlock (&g_variant_type_info_lock);
850 g_free (container->type_string);
852 if (info->container_class == ARRAY_INFO_CLASS)
853 array_info_free (info);
855 else if (info->container_class == TUPLE_INFO_CLASS)
856 tuple_info_free (info);
858 else
859 g_assert_not_reached ();
861 else
862 g_static_rec_mutex_unlock (&g_variant_type_info_lock);
866 void
867 g_variant_type_info_assert_no_infos (void)
869 g_assert (g_variant_type_info_table == NULL);
872 #define __G_VARIANT_TYPE_INFO_C__
873 #include "galiasdef.c"