nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 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' */ { not_a_type },
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", " ", " ", "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 }