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