Tizen 2.1 base
[platform/upstream/glib2.0.git] / glib / gvarianttypeinfo.c
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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
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
32 /* < private >
33  * GVariantTypeInfo:
34  *
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.
39  *
40  * The GVariantTypeInfo structures for all of the base types, plus the
41  * "variant" type are stored in a read-only static array.
42  *
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).
51  *
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.
55  *
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.
59  *
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).
62  *
63  * 'alignment' is set to one less than the alignment requirement for
64  * this type.  This makes many operations much more convenient.
65  */
66 struct _GVariantTypeInfo
67 {
68   gsize fixed_size;
69   guchar alignment;
70   guchar container_class;
71 };
72
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.
75  */
76 typedef struct
77 {
78   GVariantTypeInfo info;
79
80   gchar *type_string;
81   gint ref_count;
82 } ContainerInfo;
83
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.
88  */
89 typedef struct
90 {
91   ContainerInfo container;
92
93   GVariantTypeInfo *element;
94 } ArrayInfo;
95
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.
100  */
101 typedef struct
102 {
103   ContainerInfo container;
104
105   GVariantMemberInfo *members;
106   gsize n_members;
107 } TupleInfo;
108
109
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 */
140 #undef fixed_aligned
141 #undef not_a_type
142 #undef unaligned
143 #undef aligned
144 };
145
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.
150  */
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"
154 };
155
156 /* sanity checks to make debugging easier */
157 static void
158 g_variant_type_info_check (const GVariantTypeInfo *info,
159                            char                    container_class)
160 {
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_cmpint (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 }
190
191 /* < private >
192  * g_variant_type_info_get_type_string:
193  * @info: a #GVariantTypeInfo
194  *
195  * Gets the type string for @info.  The string is nul-terminated.
196  */
197 const gchar *
198 g_variant_type_info_get_type_string (GVariantTypeInfo *info)
199 {
200   g_variant_type_info_check (info, 0);
201
202   if (info->container_class)
203     {
204       ContainerInfo *container = (ContainerInfo *) info;
205
206       /* containers have their type string stored inside them */
207       return container->type_string;
208     }
209   else
210     {
211       gint index;
212
213       /* look up the type string in the base type array.  the call to
214        * g_variant_type_info_check() above already ensured validity.
215        */
216       index = info - g_variant_type_info_basic_table;
217
218       return g_variant_type_info_basic_chars[index];
219     }
220 }
221
222 /* < private >
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
227  *
228  * Queries @info to determine the alignment requirements and fixed size
229  * (if any) of the type.
230  *
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
233  * fixed size of 0.
234  *
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:
239  *
240  *   offset += ((-offset) & alignment);
241  */
242 void
243 g_variant_type_info_query (GVariantTypeInfo *info,
244                            guint            *alignment,
245                            gsize            *fixed_size)
246 {
247   g_variant_type_info_check (info, 0);
248
249   if (alignment)
250     *alignment = info->alignment;
251
252   if (fixed_size)
253     *fixed_size = info->fixed_size;
254 }
255
256 /* == array == */
257 #define GV_ARRAY_INFO_CLASS 'a'
258 static ArrayInfo *
259 GV_ARRAY_INFO (GVariantTypeInfo *info)
260 {
261   g_variant_type_info_check (info, GV_ARRAY_INFO_CLASS);
262
263   return (ArrayInfo *) info;
264 }
265
266 static void
267 array_info_free (GVariantTypeInfo *info)
268 {
269   ArrayInfo *array_info;
270
271   g_assert (info->container_class == GV_ARRAY_INFO_CLASS);
272   array_info = (ArrayInfo *) info;
273
274   g_variant_type_info_unref (array_info->element);
275   g_slice_free (ArrayInfo, array_info);
276 }
277
278 static ContainerInfo *
279 array_info_new (const GVariantType *type)
280 {
281   ArrayInfo *info;
282
283   info = g_slice_new (ArrayInfo);
284   info->container.info.container_class = GV_ARRAY_INFO_CLASS;
285
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;
289
290   return (ContainerInfo *) info;
291 }
292
293 /* < private >
294  * g_variant_type_info_element:
295  * @info: a #GVariantTypeInfo for an array or maybe type
296  *
297  * Returns the element type for the array or maybe type.  A reference is
298  * not added, so the caller must add their own.
299  */
300 GVariantTypeInfo *
301 g_variant_type_info_element (GVariantTypeInfo *info)
302 {
303   return GV_ARRAY_INFO (info)->element;
304 }
305
306 /* < private >
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
311  *
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().
315  */
316 void
317 g_variant_type_info_query_element (GVariantTypeInfo *info,
318                                    guint            *alignment,
319                                    gsize            *fixed_size)
320 {
321   g_variant_type_info_query (GV_ARRAY_INFO (info)->element,
322                              alignment, fixed_size);
323 }
324
325 /* == tuple == */
326 #define GV_TUPLE_INFO_CLASS 'r'
327 static TupleInfo *
328 GV_TUPLE_INFO (GVariantTypeInfo *info)
329 {
330   g_variant_type_info_check (info, GV_TUPLE_INFO_CLASS);
331
332   return (TupleInfo *) info;
333 }
334
335 static void
336 tuple_info_free (GVariantTypeInfo *info)
337 {
338   TupleInfo *tuple_info;
339   gint i;
340
341   g_assert (info->container_class == GV_TUPLE_INFO_CLASS);
342   tuple_info = (TupleInfo *) info;
343
344   for (i = 0; i < tuple_info->n_members; i++)
345     g_variant_type_info_unref (tuple_info->members[i].type_info);
346
347   g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members,
348                  tuple_info->members);
349   g_slice_free (TupleInfo, tuple_info);
350 }
351
352 static void
353 tuple_allocate_members (const GVariantType  *type,
354                         GVariantMemberInfo **members,
355                         gsize               *n_members)
356 {
357   const GVariantType *item_type;
358   gsize i = 0;
359
360   *n_members = g_variant_type_n_items (type);
361   *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members);
362
363   item_type = g_variant_type_first (type);
364   while (item_type)
365     {
366       GVariantMemberInfo *member = &(*members)[i++];
367
368       member->type_info = g_variant_type_info_get (item_type);
369       item_type = g_variant_type_next (item_type);
370
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;
375       else
376         member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET;
377     }
378
379   g_assert (i == *n_members);
380 }
381
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.
385  */
386 static gboolean
387 tuple_get_item (TupleInfo          *info,
388                 GVariantMemberInfo *item,
389                 gsize              *d,
390                 gsize              *e)
391 {
392   if (&info->members[info->n_members] == item)
393     return FALSE;
394
395   *d = item->type_info->alignment;
396   *e = item->type_info->fixed_size;
397   return TRUE;
398 }
399
400 /* Read the documentation for #GVariantMemberInfo in gvarianttype.h
401  * before attempting to understand this.
402  *
403  * This function adds one set of "magic constant" values (for one item
404  * in the tuple) to the table.
405  *
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:
411  *
412  *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
413  *
414  * We tweak these values a little to allow for a slightly easier
415  * computation and more compact storage.
416  */
417 static void
418 tuple_table_append (GVariantMemberInfo **items,
419                     gsize                i,
420                     gsize                a,
421                     gsize                b,
422                     gsize                c)
423 {
424   GVariantMemberInfo *item = (*items)++;
425
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'.
430    *
431    *  Imagine (for sake of clarity) that ROUND_10 rounds up to the
432    *  nearest 10.  It is clear that:
433    *
434    *   ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10)
435    *
436    * ie: remove the 10s portion of 'c' and add it onto 'a'.
437    *
438    * To put some numbers on it, imagine we start with a = 34 and c = 27:
439    *
440    *  ROUND_10(34) + 27 = 40 + 27 = 67
441    *
442    * but also, we can split 27 up into 20 and 7 and do this:
443    *
444    *  ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67
445    *                ^^    ^
446    * without affecting the result.  We do that here.
447    *
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.
452    */
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 */
455
456
457   /* Finally, we made one last adjustment.  Recall:
458    *
459    *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
460    *
461    * Forgetting the '+ c' for the moment:
462    *
463    *   ROUND_UP(prev_end + a, (b + 1));
464    *
465    * we can do a "round up" operation by adding 1 less than the amount
466    * to round up to, then rounding down.  ie:
467    *
468    *   #define ROUND_UP(x, y)    ROUND_DOWN(x + (y-1), y)
469    *
470    * Of course, for rounding down to a power of two, we can just mask
471    * out the appropriate number of low order bits:
472    *
473    *   #define ROUND_DOWN(x, y)  (x & ~(y - 1))
474    *
475    * Which gives us
476    *
477    *   #define ROUND_UP(x, y)    (x + (y - 1) & ~(y - 1))
478    *
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:
481    *
482    *   ((prev_end + a) + b) & ~b
483    *
484    * Associativity, and putting the 'c' back on:
485    *
486    *   (prev_end + (a + b)) & ~b + c
487    *
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.
491    *
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'
496    * equivalently.
497    *
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:
501    *
502    *   item_start = ((prev_end + a) & b) | c;
503    */
504
505   item->i = i;
506   item->a = a + b;
507   item->b = ~b;
508   item->c = c;
509 }
510
511 static gsize
512 tuple_align (gsize offset,
513              guint alignment)
514 {
515   return offset + ((-offset) & alignment);
516 }
517
518 /* This function is the heart of the algorithm for calculating 'i', 'a',
519  * 'b' and 'c' for each item in the tuple.
520  *
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
524  * our "i" we:
525  *
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
530  * one more addition.
531  *
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.
540  *
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:
544  *
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
547  *     'd'.
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'.
553  */
554 static void
555 tuple_generate_table (TupleInfo *info)
556 {
557   GVariantMemberInfo *items = info->members;
558   gsize i = -1, a = 0, b = 0, c = 0, d, e;
559
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)
563    */
564   while (tuple_get_item (info, items, &d, &e))
565     {
566       /* align to 'd' */
567       if (d <= b)
568         c = tuple_align (c, d);                   /* rule 1 */
569       else
570         a += tuple_align (c, b), b = d, c = 0;    /* rule 2 */
571
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.
574        */
575       tuple_table_append (&items, i, a, b, c);
576
577       /* "move past" the item by adding in its size. */
578       if (e == 0)
579         /* variable size:
580          *
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.
584          */
585         i++, a = b = c = 0;
586       else
587         /* fixed size */
588         c += e;                                   /* rule 3 */
589     }
590 }
591
592 static void
593 tuple_set_base_info (TupleInfo *info)
594 {
595   GVariantTypeInfo *base = &info->container.info;
596
597   if (info->n_members > 0)
598     {
599       GVariantMemberInfo *m;
600
601       /* the alignment requirement of the tuple is the alignment
602        * requirement of its largest item.
603        */
604       base->alignment = 0;
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
607          * by 'or'ing them
608          */
609         base->alignment |= m->type_info->alignment;
610
611       m--; /* take 'm' back to the last item */
612
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).
616        */
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
620          * fixed size.
621          *
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.
625          */
626         base->fixed_size =
627           tuple_align (((m->a & m->b) | m->c) + m->type_info->fixed_size,
628                        base->alignment);
629       else
630         /* else, the tuple is not fixed size */
631         base->fixed_size = 0;
632     }
633   else
634     {
635       /* the empty tuple: '()'.
636        *
637        * has a size of 1 and an no alignment requirement.
638        *
639        * It has a size of 1 (not 0) for two practical reasons:
640        *
641        *  1) So we can determine how many of them are in an array
642        *     without dividing by zero or without other tricks.
643        *
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.
653        */
654       base->alignment = 0;
655       base->fixed_size = 1;
656     }
657 }
658
659 static ContainerInfo *
660 tuple_info_new (const GVariantType *type)
661 {
662   TupleInfo *info;
663
664   info = g_slice_new (TupleInfo);
665   info->container.info.container_class = GV_TUPLE_INFO_CLASS;
666
667   tuple_allocate_members (type, &info->members, &info->n_members);
668   tuple_generate_table (info);
669   tuple_set_base_info (info);
670
671   return (ContainerInfo *) info;
672 }
673
674 /* < private >
675  * g_variant_type_info_n_members:
676  * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
677  *
678  * Returns the number of members in a tuple or dictionary entry type.
679  * For a dictionary entry this will always be 2.
680  */
681 gsize
682 g_variant_type_info_n_members (GVariantTypeInfo *info)
683 {
684   return GV_TUPLE_INFO (info)->n_members;
685 }
686
687 /* < private >
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
691  *
692  * Returns the #GVariantMemberInfo for a given member.  See
693  * documentation for that structure for why you would want this
694  * information.
695  *
696  * @index must refer to a valid child (ie: strictly less than
697  * g_variant_type_info_n_members() returns).
698  */
699 const GVariantMemberInfo *
700 g_variant_type_info_member_info (GVariantTypeInfo *info,
701                                  gsize             index)
702 {
703   TupleInfo *tuple_info = GV_TUPLE_INFO (info);
704
705   if (index < tuple_info->n_members)
706     return &tuple_info->members[index];
707
708   return NULL;
709 }
710
711 /* == new/ref/unref == */
712 static GRecMutex g_variant_type_info_lock;
713 static GHashTable *g_variant_type_info_table;
714
715 /* < private >
716  * g_variant_type_info_get:
717  * @type: a #GVariantType
718  *
719  * Returns a reference to a #GVariantTypeInfo for @type.
720  *
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.
724  *
725  * It is appropriate to call g_variant_type_info_unref() on the return
726  * value.
727  */
728 GVariantTypeInfo *
729 g_variant_type_info_get (const GVariantType *type)
730 {
731   char type_char;
732
733   type_char = g_variant_type_peek_string (type)[0];
734
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)
739     {
740       GVariantTypeInfo *info;
741       gchar *type_string;
742
743       type_string = g_variant_type_dup_string (type);
744
745       g_rec_mutex_lock (&g_variant_type_info_lock);
746
747       if (g_variant_type_info_table == NULL)
748         g_variant_type_info_table = g_hash_table_new (g_str_hash,
749                                                       g_str_equal);
750       info = g_hash_table_lookup (g_variant_type_info_table, type_string);
751
752       if (info == NULL)
753         {
754           ContainerInfo *container;
755
756           if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
757               type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY)
758             {
759               container = array_info_new (type);
760             }
761           else /* tuple or dict entry */
762             {
763               container = tuple_info_new (type);
764             }
765
766           info = (GVariantTypeInfo *) container;
767           container->type_string = type_string;
768           container->ref_count = 1;
769
770           g_hash_table_insert (g_variant_type_info_table, type_string, info);
771           type_string = NULL;
772         }
773       else
774         g_variant_type_info_ref (info);
775
776       g_rec_mutex_unlock (&g_variant_type_info_lock);
777       g_variant_type_info_check (info, 0);
778       g_free (type_string);
779
780       return info;
781     }
782   else
783     {
784       const GVariantTypeInfo *info;
785       int index;
786
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);
791
792       info = g_variant_type_info_basic_table + index;
793       g_variant_type_info_check (info, 0);
794
795       return (GVariantTypeInfo *) info;
796     }
797 }
798
799 /* < private >
800  * g_variant_type_info_ref:
801  * @info: a #GVariantTypeInfo
802  *
803  * Adds a reference to @info.
804  */
805 GVariantTypeInfo *
806 g_variant_type_info_ref (GVariantTypeInfo *info)
807 {
808   g_variant_type_info_check (info, 0);
809
810   if (info->container_class)
811     {
812       ContainerInfo *container = (ContainerInfo *) info;
813
814       g_assert_cmpint (container->ref_count, >, 0);
815       g_atomic_int_inc (&container->ref_count);
816     }
817
818   return info;
819 }
820
821 /* < private >
822  * g_variant_type_info_unref:
823  * @info: a #GVariantTypeInfo
824  *
825  * Releases a reference held on @info.  This may result in @info being
826  * freed.
827  */
828 void
829 g_variant_type_info_unref (GVariantTypeInfo *info)
830 {
831   g_variant_type_info_check (info, 0);
832
833   if (info->container_class)
834     {
835       ContainerInfo *container = (ContainerInfo *) info;
836
837       g_rec_mutex_lock (&g_variant_type_info_lock);
838       if (g_atomic_int_dec_and_test (&container->ref_count))
839         {
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)
843             {
844               g_hash_table_unref (g_variant_type_info_table);
845               g_variant_type_info_table = NULL;
846             }
847           g_rec_mutex_unlock (&g_variant_type_info_lock);
848
849           g_free (container->type_string);
850
851           if (info->container_class == GV_ARRAY_INFO_CLASS)
852             array_info_free (info);
853
854           else if (info->container_class == GV_TUPLE_INFO_CLASS)
855             tuple_info_free (info);
856
857           else
858             g_assert_not_reached ();
859         }
860       else
861         g_rec_mutex_unlock (&g_variant_type_info_lock);
862     }
863 }
864
865 void
866 g_variant_type_info_assert_no_infos (void)
867 {
868   g_assert (g_variant_type_info_table == NULL);
869 }