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