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>
25 #include "gvarianttypeinfo.h"
27 #include <glib/gtestutils.h>
28 #include <glib/gthread.h>
29 #include <glib/gslice.h>
30 #include <glib/ghash.h>
35 * This structure contains the necessary information to facilitate the
36 * serialisation and fast deserialisation of a given type of GVariant
37 * value. A GVariant instance holds a pointer to one of these
38 * structures to provide for efficient operation.
40 * The GVariantTypeInfo structures for all of the base types, plus the
41 * "variant" type are stored in a read-only static array.
43 * For container types, a hash table and reference counting is used to
44 * ensure that only one of these structures exists for any given type.
45 * In general, a container GVariantTypeInfo will exist for a given type
46 * only if one or more GVariant instances of that type exist or if
47 * another GVariantTypeInfo has that type as a subtype. For example, if
48 * a process contains a single GVariant instance with type "(asv)", then
49 * container GVariantTypeInfo structures will exist for "(asv)" and
50 * for "as" (note that "s" and "v" always exist in the static array).
52 * The trickiest part of GVariantTypeInfo (and in fact, the major reason
53 * for its existence) is the storage of somewhat magical constants that
54 * allow for O(1) lookups of items in tuples. This is described below.
56 * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is
57 * contained inside of an ArrayInfo or TupleInfo, respectively. This
58 * allows the storage of the necessary additional information.
60 * 'fixed_size' is set to the fixed size of the type, if applicable, or
61 * 0 otherwise (since no type has a fixed size of 0).
63 * 'alignment' is set to one less than the alignment requirement for
64 * this type. This makes many operations much more convenient.
66 struct _GVariantTypeInfo
70 guchar container_class
;
73 /* Container types are reference counted. They also need to have their
74 * type string stored explicitly since it is not merely a single letter.
78 GVariantTypeInfo info
;
84 /* For 'array' and 'maybe' types, we store some extra information on the
85 * end of the GVariantTypeInfo struct -- the element type (ie: "s" for
86 * "as"). The container GVariantTypeInfo structure holds a reference to
87 * the element typeinfo.
91 ContainerInfo container
;
93 GVariantTypeInfo
*element
;
96 /* For 'tuple' and 'dict entry' types, we store extra information for
97 * each member -- its type and how to find it inside the serialised data
98 * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'. See the
99 * comment on GVariantMemberInfo in gvarianttypeinfo.h.
103 ContainerInfo container
;
105 GVariantMemberInfo
*members
;
110 /* Hard-code the base types in a constant array */
111 static const GVariantTypeInfo g_variant_type_info_basic_table
[24] = {
112 #define fixed_aligned(x) x, x - 1
113 #define not_a_type 0,
114 #define unaligned 0, 0
115 #define aligned(x) 0, x - 1
116 /* 'b' */ { fixed_aligned(1) }, /* boolean */
117 /* 'c' */ { not_a_type
},
118 /* 'd' */ { fixed_aligned(8) }, /* double */
119 /* 'e' */ { not_a_type
},
120 /* 'f' */ { not_a_type
},
121 /* 'g' */ { unaligned
}, /* signature string */
122 /* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */
123 /* 'i' */ { fixed_aligned(4) }, /* int32 */
124 /* 'j' */ { not_a_type
},
125 /* 'k' */ { not_a_type
},
126 /* 'l' */ { not_a_type
},
127 /* 'm' */ { not_a_type
},
128 /* 'n' */ { fixed_aligned(2) }, /* int16 */
129 /* 'o' */ { unaligned
}, /* object path string */
130 /* 'p' */ { not_a_type
},
131 /* 'q' */ { fixed_aligned(2) }, /* uint16 */
132 /* 'r' */ { not_a_type
},
133 /* 's' */ { unaligned
}, /* string */
134 /* 't' */ { fixed_aligned(8) }, /* uint64 */
135 /* 'u' */ { fixed_aligned(4) }, /* uint32 */
136 /* 'v' */ { aligned(8) }, /* variant */
137 /* 'w' */ { not_a_type
},
138 /* 'x' */ { fixed_aligned(8) }, /* int64 */
139 /* 'y' */ { fixed_aligned(1) }, /* byte */
146 /* We need to have type strings to return for the base types. We store
147 * those in another array. Since all base type strings are single
148 * characters this is easy. By not storing pointers to strings into the
149 * GVariantTypeInfo itself, we save a bunch of relocations.
151 static const char g_variant_type_info_basic_chars
[24][2] = {
152 "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
153 "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
156 /* sanity checks to make debugging easier */
158 g_variant_type_info_check (const GVariantTypeInfo
*info
,
159 char container_class
)
161 g_assert (!container_class
|| info
->container_class
== container_class
);
163 /* alignment can only be one of these */
164 g_assert (info
->alignment
== 0 || info
->alignment
== 1 ||
165 info
->alignment
== 3 || info
->alignment
== 7);
167 if (info
->container_class
)
169 ContainerInfo
*container
= (ContainerInfo
*) info
;
171 /* extra checks for containers */
172 g_assert_cmpint (container
->ref_count
, >, 0);
173 g_assert (container
->type_string
!= NULL
);
179 /* if not a container, then ensure that it is a valid member of
180 * the basic types table
182 index
= info
- g_variant_type_info_basic_table
;
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] != ' ');
192 * g_variant_type_info_get_type_string:
193 * @info: a #GVariantTypeInfo
195 * Gets the type string for @info. The string is nul-terminated.
198 g_variant_type_info_get_type_string (GVariantTypeInfo
*info
)
200 g_variant_type_info_check (info
, 0);
202 if (info
->container_class
)
204 ContainerInfo
*container
= (ContainerInfo
*) info
;
206 /* containers have their type string stored inside them */
207 return container
->type_string
;
213 /* look up the type string in the base type array. the call to
214 * g_variant_type_info_check() above already ensured validity.
216 index
= info
- g_variant_type_info_basic_table
;
218 return g_variant_type_info_basic_chars
[index
];
223 * g_variant_type_info_query:
224 * @info: a #GVariantTypeInfo
225 * @alignment: (allow-none): the location to store the alignment, or %NULL
226 * @fixed_size: (allow-none): the location to store the fixed size, or %NULL
228 * Queries @info to determine the alignment requirements and fixed size
229 * (if any) of the type.
231 * @fixed_size, if non-%NULL is set to the fixed size of the type, or 0
232 * to indicate that the type is a variable-sized type. No type has a
235 * @alignment, if non-%NULL, is set to one less than the required
236 * alignment of the type. For example, for a 32bit integer, @alignment
237 * would be set to 3. This allows you to round an integer up to the
238 * proper alignment by performing the following efficient calculation:
240 * offset += ((-offset) & alignment);
243 g_variant_type_info_query (GVariantTypeInfo
*info
,
247 g_variant_type_info_check (info
, 0);
250 *alignment
= info
->alignment
;
253 *fixed_size
= info
->fixed_size
;
257 #define GV_ARRAY_INFO_CLASS 'a'
259 GV_ARRAY_INFO (GVariantTypeInfo
*info
)
261 g_variant_type_info_check (info
, GV_ARRAY_INFO_CLASS
);
263 return (ArrayInfo
*) info
;
267 array_info_free (GVariantTypeInfo
*info
)
269 ArrayInfo
*array_info
;
271 g_assert (info
->container_class
== GV_ARRAY_INFO_CLASS
);
272 array_info
= (ArrayInfo
*) info
;
274 g_variant_type_info_unref (array_info
->element
);
275 g_slice_free (ArrayInfo
, array_info
);
278 static ContainerInfo
*
279 array_info_new (const GVariantType
*type
)
283 info
= g_slice_new (ArrayInfo
);
284 info
->container
.info
.container_class
= GV_ARRAY_INFO_CLASS
;
286 info
->element
= g_variant_type_info_get (g_variant_type_element (type
));
287 info
->container
.info
.alignment
= info
->element
->alignment
;
288 info
->container
.info
.fixed_size
= 0;
290 return (ContainerInfo
*) info
;
294 * g_variant_type_info_element:
295 * @info: a #GVariantTypeInfo for an array or maybe type
297 * Returns the element type for the array or maybe type. A reference is
298 * not added, so the caller must add their own.
301 g_variant_type_info_element (GVariantTypeInfo
*info
)
303 return GV_ARRAY_INFO (info
)->element
;
307 * g_variant_type_query_element:
308 * @info: a #GVariantTypeInfo for an array or maybe type
309 * @alignment: (allow-none): the location to store the alignment, or %NULL
310 * @fixed_size: (allow-none): the location to store the fixed size, or %NULL
312 * Returns the alignment requires and fixed size (if any) for the
313 * element type of the array. This call is a convenience wrapper around
314 * g_variant_type_info_element() and g_variant_type_info_query().
317 g_variant_type_info_query_element (GVariantTypeInfo
*info
,
321 g_variant_type_info_query (GV_ARRAY_INFO (info
)->element
,
322 alignment
, fixed_size
);
326 #define GV_TUPLE_INFO_CLASS 'r'
328 GV_TUPLE_INFO (GVariantTypeInfo
*info
)
330 g_variant_type_info_check (info
, GV_TUPLE_INFO_CLASS
);
332 return (TupleInfo
*) info
;
336 tuple_info_free (GVariantTypeInfo
*info
)
338 TupleInfo
*tuple_info
;
341 g_assert (info
->container_class
== GV_TUPLE_INFO_CLASS
);
342 tuple_info
= (TupleInfo
*) info
;
344 for (i
= 0; i
< tuple_info
->n_members
; i
++)
345 g_variant_type_info_unref (tuple_info
->members
[i
].type_info
);
347 g_slice_free1 (sizeof (GVariantMemberInfo
) * tuple_info
->n_members
,
348 tuple_info
->members
);
349 g_slice_free (TupleInfo
, tuple_info
);
353 tuple_allocate_members (const GVariantType
*type
,
354 GVariantMemberInfo
**members
,
357 const GVariantType
*item_type
;
360 *n_members
= g_variant_type_n_items (type
);
361 *members
= g_slice_alloc (sizeof (GVariantMemberInfo
) * *n_members
);
363 item_type
= g_variant_type_first (type
);
366 GVariantMemberInfo
*member
= &(*members
)[i
++];
368 member
->type_info
= g_variant_type_info_get (item_type
);
369 item_type
= g_variant_type_next (item_type
);
371 if (member
->type_info
->fixed_size
)
372 member
->ending_type
= G_VARIANT_MEMBER_ENDING_FIXED
;
373 else if (item_type
== NULL
)
374 member
->ending_type
= G_VARIANT_MEMBER_ENDING_LAST
;
376 member
->ending_type
= G_VARIANT_MEMBER_ENDING_OFFSET
;
379 g_assert (i
== *n_members
);
382 /* this is g_variant_type_info_query for a given member of the tuple.
383 * before the access is done, it is ensured that the item is within
384 * range and %FALSE is returned if not.
387 tuple_get_item (TupleInfo
*info
,
388 GVariantMemberInfo
*item
,
392 if (&info
->members
[info
->n_members
] == item
)
395 *d
= item
->type_info
->alignment
;
396 *e
= item
->type_info
->fixed_size
;
400 /* Read the documentation for #GVariantMemberInfo in gvarianttype.h
401 * before attempting to understand this.
403 * This function adds one set of "magic constant" values (for one item
404 * in the tuple) to the table.
406 * The algorithm in tuple_generate_table() calculates values of 'a', 'b'
407 * and 'c' for each item, such that the procedure for finding the item
408 * is to start at the end of the previous variable-sized item, add 'a',
409 * then round up to the nearest multiple of 'b', then then add 'c'.
410 * Note that 'b' is stored in the usual "one less than" form. ie:
412 * start = ROUND_UP(prev_end + a, (b + 1)) + c;
414 * We tweak these values a little to allow for a slightly easier
415 * computation and more compact storage.
418 tuple_table_append (GVariantMemberInfo
**items
,
424 GVariantMemberInfo
*item
= (*items
)++;
426 /* We can shift multiples of the alignment size from 'c' into 'a'.
427 * As long as we're shifting whole multiples, it won't affect the
428 * result. This means that we can take the "aligned" portion off of
429 * 'c' and add it into 'a'.
431 * Imagine (for sake of clarity) that ROUND_10 rounds up to the
432 * nearest 10. It is clear that:
434 * ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10)
436 * ie: remove the 10s portion of 'c' and add it onto 'a'.
438 * To put some numbers on it, imagine we start with a = 34 and c = 27:
440 * ROUND_10(34) + 27 = 40 + 27 = 67
442 * but also, we can split 27 up into 20 and 7 and do this:
444 * ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67
446 * without affecting the result. We do that here.
448 * This reduction in the size of 'c' means that we can store it in a
449 * gchar instead of a gsize. Due to how the structure is packed, this
450 * ends up saving us 'two pointer sizes' per item in each tuple when
451 * allocating using GSlice.
453 a
+= ~b
& c
; /* take the "aligned" part of 'c' and add to 'a' */
454 c
&= b
; /* chop 'c' to contain only the unaligned part */
457 /* Finally, we made one last adjustment. Recall:
459 * start = ROUND_UP(prev_end + a, (b + 1)) + c;
461 * Forgetting the '+ c' for the moment:
463 * ROUND_UP(prev_end + a, (b + 1));
465 * we can do a "round up" operation by adding 1 less than the amount
466 * to round up to, then rounding down. ie:
468 * #define ROUND_UP(x, y) ROUND_DOWN(x + (y-1), y)
470 * Of course, for rounding down to a power of two, we can just mask
471 * out the appropriate number of low order bits:
473 * #define ROUND_DOWN(x, y) (x & ~(y - 1))
477 * #define ROUND_UP(x, y) (x + (y - 1) & ~(y - 1))
479 * but recall that our alignment value 'b' is already "one less".
480 * This means that to round 'prev_end + a' up to 'b' we can just do:
482 * ((prev_end + a) + b) & ~b
484 * Associativity, and putting the 'c' back on:
486 * (prev_end + (a + b)) & ~b + c
488 * Now, since (a + b) is constant, we can just add 'b' to 'a' now and
489 * store that as the number to add to prev_end. Then we use ~b as the
490 * number to take a bitwise 'and' with. Finally, 'c' is added on.
492 * Note, however, that all the low order bits of the 'aligned' value
493 * are masked out and that all of the high order bits of 'c' have been
494 * "moved" to 'a' (in the previous step). This means that there are
495 * no overlapping bits in the addition -- so we can do a bitwise 'or'
498 * This means that we can now compute the start address of a given
499 * item in the tuple using the algorithm given in the documentation
500 * for #GVariantMemberInfo:
502 * item_start = ((prev_end + a) & b) | c;
512 tuple_align (gsize offset
,
515 return offset
+ ((-offset
) & alignment
);
518 /* This function is the heart of the algorithm for calculating 'i', 'a',
519 * 'b' and 'c' for each item in the tuple.
521 * Imagine we want to find the start of the "i" in the type "(su(qx)ni)".
522 * That's a string followed by a uint32, then a tuple containing a
523 * uint16 and a int64, then an int16, then our "i". In order to get to
526 * Start at the end of the string, align to 4 (for the uint32), add 4.
527 * Align to 8, add 16 (for the tuple). Align to 2, add 2 (for the
528 * int16). Then we're there. It turns out that, given 3 simple rules,
529 * we can flatten this iteration into one addition, one alignment, then
532 * The loop below plays through each item in the tuple, querying its
533 * alignment and fixed_size into 'd' and 'e', respectively. At all
534 * times the variables 'a', 'b', and 'c' are maintained such that in
535 * order to get to the current point, you add 'a', align to 'b' then add
536 * 'c'. 'b' is kept in "one less than" form. For each item, the proper
537 * alignment is applied to find the values of 'a', 'b' and 'c' to get to
538 * the start of that item. Those values are recorded into the table.
539 * The fixed size of the item (if applicable) is then added on.
541 * These 3 rules are how 'a', 'b' and 'c' are modified for alignment and
542 * addition of fixed size. They have been proven correct but are
543 * presented here, without proof:
545 * 1) in order to "align to 'd'" where 'd' is less than or equal to the
546 * largest level of alignment seen so far ('b'), you align 'c' to
548 * 2) in order to "align to 'd'" where 'd' is greater than the largest
549 * level of alignment seen so far, you add 'c' aligned to 'b' to the
550 * value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment
551 * seen') and reset 'c' to 0.
552 * 3) in order to "add 'e'", just add 'e' to 'c'.
555 tuple_generate_table (TupleInfo
*info
)
557 GVariantMemberInfo
*items
= info
->members
;
558 gsize i
= -1, a
= 0, b
= 0, c
= 0, d
, e
;
560 /* iterate over each item in the tuple.
561 * 'd' will be the alignment of the item (in one-less form)
562 * 'e' will be the fixed size (or 0 for variable-size items)
564 while (tuple_get_item (info
, items
, &d
, &e
))
568 c
= tuple_align (c
, d
); /* rule 1 */
570 a
+= tuple_align (c
, b
), b
= d
, c
= 0; /* rule 2 */
572 /* the start of the item is at this point (ie: right after we
573 * have aligned for it). store this information in the table.
575 tuple_table_append (&items
, i
, a
, b
, c
);
577 /* "move past" the item by adding in its size. */
581 * we'll have an offset stored to mark the end of this item, so
582 * just bump the offset index to give us a new starting point
583 * and reset all the counters.
593 tuple_set_base_info (TupleInfo
*info
)
595 GVariantTypeInfo
*base
= &info
->container
.info
;
597 if (info
->n_members
> 0)
599 GVariantMemberInfo
*m
;
601 /* the alignment requirement of the tuple is the alignment
602 * requirement of its largest item.
605 for (m
= info
->members
; m
< &info
->members
[info
->n_members
]; m
++)
606 /* can find the max of a list of "one less than" powers of two
609 base
->alignment
|= m
->type_info
->alignment
;
611 m
--; /* take 'm' back to the last item */
613 /* the structure only has a fixed size if no variable-size
614 * offsets are stored and the last item is fixed-sized too (since
615 * an offset is never stored for the last item).
617 if (m
->i
== -1 && m
->type_info
->fixed_size
)
618 /* in that case, the fixed size can be found by finding the
619 * start of the last item (in the usual way) and adding its
622 * if a tuple has a fixed size then it is always a multiple of
623 * the alignment requirement (to make packing into arrays
624 * easier) so we round up to that here.
627 tuple_align (((m
->a
& m
->b
) | m
->c
) + m
->type_info
->fixed_size
,
630 /* else, the tuple is not fixed size */
631 base
->fixed_size
= 0;
635 /* the empty tuple: '()'.
637 * has a size of 1 and an no alignment requirement.
639 * It has a size of 1 (not 0) for two practical reasons:
641 * 1) So we can determine how many of them are in an array
642 * without dividing by zero or without other tricks.
644 * 2) Even if we had some trick to know the number of items in
645 * the array (as GVariant did at one time) this would open a
646 * potential denial of service attack: an attacker could send
647 * you an extremely small array (in terms of number of bytes)
648 * containing trillions of zero-sized items. If you iterated
649 * over this array you would effectively infinite-loop your
650 * program. By forcing a size of at least one, we bound the
651 * amount of computation done in response to a message to a
652 * reasonable function of the size of that message.
655 base
->fixed_size
= 1;
659 static ContainerInfo
*
660 tuple_info_new (const GVariantType
*type
)
664 info
= g_slice_new (TupleInfo
);
665 info
->container
.info
.container_class
= GV_TUPLE_INFO_CLASS
;
667 tuple_allocate_members (type
, &info
->members
, &info
->n_members
);
668 tuple_generate_table (info
);
669 tuple_set_base_info (info
);
671 return (ContainerInfo
*) info
;
675 * g_variant_type_info_n_members:
676 * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
678 * Returns the number of members in a tuple or dictionary entry type.
679 * For a dictionary entry this will always be 2.
682 g_variant_type_info_n_members (GVariantTypeInfo
*info
)
684 return GV_TUPLE_INFO (info
)->n_members
;
688 * g_variant_type_info_member_info:
689 * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
690 * @index: the member to fetch information for
692 * Returns the #GVariantMemberInfo for a given member. See
693 * documentation for that structure for why you would want this
696 * @index must refer to a valid child (ie: strictly less than
697 * g_variant_type_info_n_members() returns).
699 const GVariantMemberInfo
*
700 g_variant_type_info_member_info (GVariantTypeInfo
*info
,
703 TupleInfo
*tuple_info
= GV_TUPLE_INFO (info
);
705 if (index
< tuple_info
->n_members
)
706 return &tuple_info
->members
[index
];
711 /* == new/ref/unref == */
712 static GRecMutex g_variant_type_info_lock
;
713 static GHashTable
*g_variant_type_info_table
;
716 * g_variant_type_info_get:
717 * @type: a #GVariantType
719 * Returns a reference to a #GVariantTypeInfo for @type.
721 * If an info structure already exists for this type, a new reference is
722 * returned. If not, the required calculations are performed and a new
723 * info structure is returned.
725 * It is appropriate to call g_variant_type_info_unref() on the return
729 g_variant_type_info_get (const GVariantType
*type
)
733 type_char
= g_variant_type_peek_string (type
)[0];
735 if (type_char
== G_VARIANT_TYPE_INFO_CHAR_MAYBE
||
736 type_char
== G_VARIANT_TYPE_INFO_CHAR_ARRAY
||
737 type_char
== G_VARIANT_TYPE_INFO_CHAR_TUPLE
||
738 type_char
== G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY
)
740 GVariantTypeInfo
*info
;
743 type_string
= g_variant_type_dup_string (type
);
745 g_rec_mutex_lock (&g_variant_type_info_lock
);
747 if (g_variant_type_info_table
== NULL
)
748 g_variant_type_info_table
= g_hash_table_new (g_str_hash
,
750 info
= g_hash_table_lookup (g_variant_type_info_table
, type_string
);
754 ContainerInfo
*container
;
756 if (type_char
== G_VARIANT_TYPE_INFO_CHAR_MAYBE
||
757 type_char
== G_VARIANT_TYPE_INFO_CHAR_ARRAY
)
759 container
= array_info_new (type
);
761 else /* tuple or dict entry */
763 container
= tuple_info_new (type
);
766 info
= (GVariantTypeInfo
*) container
;
767 container
->type_string
= type_string
;
768 container
->ref_count
= 1;
770 g_hash_table_insert (g_variant_type_info_table
, type_string
, info
);
774 g_variant_type_info_ref (info
);
776 g_rec_mutex_unlock (&g_variant_type_info_lock
);
777 g_variant_type_info_check (info
, 0);
778 g_free (type_string
);
784 const GVariantTypeInfo
*info
;
787 index
= type_char
- 'b';
788 g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table
) == 24);
789 g_assert_cmpint (0, <=, index
);
790 g_assert_cmpint (index
, <, 24);
792 info
= g_variant_type_info_basic_table
+ index
;
793 g_variant_type_info_check (info
, 0);
795 return (GVariantTypeInfo
*) info
;
800 * g_variant_type_info_ref:
801 * @info: a #GVariantTypeInfo
803 * Adds a reference to @info.
806 g_variant_type_info_ref (GVariantTypeInfo
*info
)
808 g_variant_type_info_check (info
, 0);
810 if (info
->container_class
)
812 ContainerInfo
*container
= (ContainerInfo
*) info
;
814 g_assert_cmpint (container
->ref_count
, >, 0);
815 g_atomic_int_inc (&container
->ref_count
);
822 * g_variant_type_info_unref:
823 * @info: a #GVariantTypeInfo
825 * Releases a reference held on @info. This may result in @info being
829 g_variant_type_info_unref (GVariantTypeInfo
*info
)
831 g_variant_type_info_check (info
, 0);
833 if (info
->container_class
)
835 ContainerInfo
*container
= (ContainerInfo
*) info
;
837 g_rec_mutex_lock (&g_variant_type_info_lock
);
838 if (g_atomic_int_dec_and_test (&container
->ref_count
))
840 g_hash_table_remove (g_variant_type_info_table
,
841 container
->type_string
);
842 if (g_hash_table_size (g_variant_type_info_table
) == 0)
844 g_hash_table_unref (g_variant_type_info_table
);
845 g_variant_type_info_table
= NULL
;
847 g_rec_mutex_unlock (&g_variant_type_info_lock
);
849 g_free (container
->type_string
);
851 if (info
->container_class
== GV_ARRAY_INFO_CLASS
)
852 array_info_free (info
);
854 else if (info
->container_class
== GV_TUPLE_INFO_CLASS
)
855 tuple_info_free (info
);
858 g_assert_not_reached ();
861 g_rec_mutex_unlock (&g_variant_type_info_lock
);
866 g_variant_type_info_assert_no_infos (void)
868 g_assert (g_variant_type_info_table
== NULL
);