[glib/gvariant] gvarianttypeinfo fixups



commit ebfda9316aeb7e64cc7993593eafc402fb02f3dd
Author: Ryan Lortie <desrt desrt ca>
Date:   Fri Jan 29 14:36:54 2010 -0500

    gvarianttypeinfo fixups

 glib/gvarianttypeinfo.c |  503 ++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 415 insertions(+), 88 deletions(-)
---
diff --git a/glib/gvarianttypeinfo.c b/glib/gvarianttypeinfo.c
index 8ad2437..f9f7fad 100644
--- a/glib/gvarianttypeinfo.c
+++ b/glib/gvarianttypeinfo.c
@@ -27,14 +27,13 @@
  *
  * This structure contains the necessary information to facilitate the
  * serialisation and fast deserialisation of a given type of GVariant
- * value.  It also contains the type string itself.  A GVariant instance
- * holds a pointer to one of these structures to provide for efficient
- * operation.
+ * value.  A GVariant instance holds a pointer to one of these
+ * structures to provide for efficient operation.
  *
  * The GVariantTypeInfo structures for all of the base types, plus the
  * "variant" type are stored in a read-only static array.
  *
- * For containe types, a hash table and reference counting is used to
+ * For container types, a hash table and reference counting is used to
  * ensure that only one of these structures exists for any given type.
  * In general, a container GVariantTypeInfo will exist for a given type
  * only if one or more GVariant instances of that type exist or if
@@ -46,17 +45,34 @@
  * The trickiest part of GVariantTypeInfo (and in fact, the major reason
  * for its existance) is the storage of somewhat magical constants that
  * allow for O(1) lookups of items in tuples.  This is described below.
+ *
+ * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is
+ * contained inside of an ArrayInfo or TupleInfo, respectively.  This
+ * allows the storage of the necessary additional information.
+ *
+ * 'fixed_size' is set to the fixed size of the type, if applicable, or
+ * 0 otherwise (since no type has a fixed size of 0).
+ *
+ * 'alignment' is set to one less than the alignment requirement for
+ * this type.  This makes many operations much more convenient.
  */
-
 struct _GVariantTypeInfo
 {
-  gchar *type_string;
   gsize fixed_size;
   guchar alignment;
+  guchar container_class;
+};
+
+/* Container types are reference counted.  They also need to have their
+ * type string stored explicitly since it is not merely a single letter.
+ */
+typedef struct
+{
+  GVariantTypeInfo info;
 
-  guchar info_class;
+  gchar *type_string;
   gint ref_count;
-};
+} ContainerInfo;
 
 /* For 'array' and 'maybe' types, we store some extra information on the
  * end of the GVariantTypeInfo struct -- the element type (ie: "s" for
@@ -65,95 +81,159 @@ struct _GVariantTypeInfo
  */
 typedef struct
 {
-  GVariantTypeInfo self;
+  ContainerInfo container;
 
   GVariantTypeInfo *element;
 } ArrayInfo;
 
 /* For 'tuple' and 'dict entry' types, we store extra information for
  * each member -- its type and how to find it inside the serialised data
- * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'.
+ * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'.  See the
+ * comment on GVariantMemberInfo in gvarianttypeinfo.h.
  */
 typedef struct
 {
-  GVariantTypeInfo self;
+  ContainerInfo container;
 
   GVariantMemberInfo *members;
   gsize n_members;
 } TupleInfo;
 
+
+/* Hard-code the base types in a constant array */
 static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
-#define type_string(x)    ((gchar *) (x))
 #define fixed_aligned(x)  x, x - 1
 #define unaligned         0, 0
 #define aligned(x)        0, x - 1
-  /* 'b' */ { type_string(G_VARIANT_TYPE_BOOLEAN),      fixed_aligned(1) },
+  /* 'b' */ { fixed_aligned(1) },   /* boolean */
   /* 'c' */ { },
-  /* 'd' */ { type_string(G_VARIANT_TYPE_DOUBLE),       fixed_aligned(8) },
+  /* 'd' */ { fixed_aligned(8) },   /* double */
   /* 'e' */ { },
   /* 'f' */ { },
-  /* 'g' */ { type_string(G_VARIANT_TYPE_SIGNATURE),    unaligned        },
-  /* 'h' */ { type_string(G_VARIANT_TYPE_HANDLE),       fixed_aligned(4) },
-  /* 'i' */ { type_string(G_VARIANT_TYPE_INT32),        fixed_aligned(4) },
+  /* 'g' */ { unaligned        },   /* signature string */
+  /* 'h' */ { fixed_aligned(4) },   /* file handle (int32) */
+  /* 'i' */ { fixed_aligned(4) },   /* int32 */
   /* 'j' */ { },
   /* 'k' */ { },
   /* 'l' */ { },
   /* 'm' */ { },
-  /* 'n' */ { type_string(G_VARIANT_TYPE_INT16),        fixed_aligned(2) },
-  /* 'o' */ { type_string(G_VARIANT_TYPE_OBJECT_PATH),  unaligned        },
+  /* 'n' */ { fixed_aligned(2) },   /* int16 */
+  /* 'o' */ { unaligned        },   /* object path string */
   /* 'p' */ { },
-  /* 'q' */ { type_string(G_VARIANT_TYPE_UINT16),       fixed_aligned(2) },
+  /* 'q' */ { fixed_aligned(2) },   /* uint16 */
   /* 'r' */ { },
-  /* 's' */ { type_string(G_VARIANT_TYPE_STRING),       unaligned        },
-  /* 't' */ { type_string(G_VARIANT_TYPE_UINT64),       fixed_aligned(8) },
-  /* 'u' */ { type_string(G_VARIANT_TYPE_UINT32),       fixed_aligned(4) },
-  /* 'v' */ { type_string(G_VARIANT_TYPE_VARIANT),      aligned(8)       },
+  /* 's' */ { unaligned        },   /* string */
+  /* 't' */ { fixed_aligned(8) },   /* uint64 */
+  /* 'u' */ { fixed_aligned(4) },   /* uint32 */
+  /* 'v' */ { aligned(8)       },   /* variant */
   /* 'w' */ { },
-  /* 'x' */ { type_string(G_VARIANT_TYPE_INT64),        fixed_aligned(8) },
-  /* 'y' */ { type_string(G_VARIANT_TYPE_BYTE),         fixed_aligned(1) },
-#undef type_string
+  /* 'x' */ { fixed_aligned(8) },   /* int64 */
+  /* 'y' */ { fixed_aligned(1) },   /* byte */
 #undef fixed_aligned
 #undef unaligned
 #undef aligned
 };
 
+/* We need to have type strings to return for the base types.  We store
+ * those in another array.  Since all base type strings are single
+ * characters this is easy.  By not storing pointers to strings into the
+ * GVariantTypeInfo itself, we save a bunch of relocations.
+ */
+static const char g_variant_type_info_basic_chars[24][2] = {
+  "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
+  "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
+};
+
+/* sanity checks to make debugging easier */
 static void
 g_variant_type_info_check (const GVariantTypeInfo *info,
-                           char                    info_class)
+                           char                    container_class)
 {
+  g_assert (!container_class || info->container_class == container_class);
+
+  /* alignment can only be one of these */
   g_assert (info->alignment == 0 || info->alignment == 1 ||
             info->alignment == 3 || info->alignment == 7);
-  g_assert (info->type_string != NULL);
-  g_assert (!info_class || info->info_class == info_class);
 
-  if (info->info_class)
+  if (info->container_class)
     {
-      g_assert_cmpint (info->ref_count, >, 0);
+      ContainerInfo *container = (ContainerInfo *) info;
+
+      /* extra checks for containers */
+      g_assert_cmpint (container->ref_count, >, 0);
+      g_assert (container->type_string != NULL);
     }
   else
     {
       gint index;
 
-      g_assert_cmpint (info->ref_count, ==, 0);
-      index = info->type_string[0] - 'b';
+      /* if not a container, then ensure that it is a valid member of
+       * the basic types table
+       */
+      index = info - g_variant_type_info_basic_table;
 
-      g_assert (g_variant_type_info_basic_table + index == info);
+      g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
+      g_assert (G_N_ELEMENTS (g_variant_type_info_basic_chars) == 24);
+      g_assert (0 <= index && index < 24);
+      g_assert (g_variant_type_info_basic_chars[index][0] != ' ');
     }
 }
 
-/* == query == */
+/* < private >
+ * g_variant_type_info_get_type_string:
+ * @info: a #GVariantTypeInfo
+ *
+ * Gets the type string for @info.  The string is nul-terminated.
+ */
 const gchar *
 g_variant_type_info_get_type_string (GVariantTypeInfo *info)
 {
   g_variant_type_info_check (info, 0);
 
-  return info->type_string;
+  if (info->container_class)
+    {
+      ContainerInfo *container = (ContainerInfo *) info;
+
+      /* containers have their type string stored inside them */
+      return container->type_string;
+    }
+  else
+    {
+      gint index;
+
+      /* look up the type string in the base type array.  the call to
+       * g_variant_type_info_check() above already ensured validity.
+       */
+      index = info - g_variant_type_info_basic_table;
+
+      return g_variant_type_info_basic_chars[index];
+    }
 }
 
+/* < private >
+ * g_variant_type_info_query:
+ * @info: a #GVariantTypeInfo
+ * @alignment: the location to store the alignment, or %NULL
+ * @fixed_size: the location to store the fixed size, or %NULL
+ *
+ * Queries @info to determine the alignment requirements and fixed size
+ * (if any) of the type.
+ *
+ * @fixed_size, if non-%NULL is set to the fixed size of the type, or 0
+ * to indicate that the type is a variable-sized type.  No type has a
+ * fixed size of 0.
+ *
+ * @alignment, if non-%NULL, is set to one less than the required
+ * alignment of the type.  For example, for a 32bit integer, @alignment
+ * would be set to 3.  This allows you to round an integer up to the
+ * proper alignment by performing the following efficient calculation:
+ *
+ *   offset += ((-offset) & alignment);
+ */
 void
 g_variant_type_info_query (GVariantTypeInfo *info,
                            guint            *alignment,
-                           gsize           *fixed_size)
+                           gsize            *fixed_size)
 {
   g_variant_type_info_check (info, 0);
 
@@ -179,34 +259,51 @@ array_info_free (GVariantTypeInfo *info)
 {
   ArrayInfo *array_info;
 
-  g_assert (info->info_class == ARRAY_INFO_CLASS);
+  g_assert (info->container_class == ARRAY_INFO_CLASS);
   array_info = (ArrayInfo *) info;
 
   g_variant_type_info_unref (array_info->element);
   g_slice_free (ArrayInfo, array_info);
 }
 
-static GVariantTypeInfo *
+static ContainerInfo *
 array_info_new (const GVariantType *type)
 {
   ArrayInfo *info;
 
   info = g_slice_new (ArrayInfo);
-  info->self.info_class = ARRAY_INFO_CLASS;
+  info->container.info.container_class = ARRAY_INFO_CLASS;
 
   info->element = g_variant_type_info_get (g_variant_type_element (type));
-  info->self.alignment = info->element->alignment;
-  info->self.fixed_size = 0;
+  info->container.info.alignment = info->element->alignment;
+  info->container.info.fixed_size = 0;
 
-  return (GVariantTypeInfo *) info;
+  return (ContainerInfo *) info;
 }
 
+/* < private >
+ * g_variant_type_info_element:
+ * @info: a #GVariantTypeInfo for an array or maybe type
+ *
+ * Returns the element type for the array or maybe type.  A reference is
+ * not added, so the caller must add their own.
+ */
 GVariantTypeInfo *
 g_variant_type_info_element (GVariantTypeInfo *info)
 {
   return ARRAY_INFO (info)->element;
 }
 
+/* < private >
+ * g_variant_type_query_element:
+ * @info: a #GVariantTypeInfo for an array or maybe type
+ * @alignment: the location to store the alignment, or %NULL
+ * @fixed_size: the location to store the fixed size, or %NULL
+ *
+ * Returns the alignment requires and fixed size (if any) for the
+ * element type of the array.  This call is a convenience wrapper around
+ * g_variant_type_info_element() and g_variant_type_info_query().
+ */
 void
 g_variant_type_info_query_element (GVariantTypeInfo *info,
                                    guint            *alignment,
@@ -232,7 +329,7 @@ tuple_info_free (GVariantTypeInfo *info)
   TupleInfo *tuple_info;
   gint i;
 
-  g_assert (info->info_class == TUPLE_INFO_CLASS);
+  g_assert (info->container_class == TUPLE_INFO_CLASS);
   tuple_info = (TupleInfo *) info;
 
   for (i = 0; i < tuple_info->n_members; i++)
@@ -264,8 +361,12 @@ tuple_allocate_members (const GVariantType  *type,
   g_assert (i == *n_members);
 }
 
+/* this is g_variant_type_info_query for a given member of the tuple.
+ * before the access is done, it is ensured that the item is within
+ * range and %FALSE is returned if not.
+ */
 static gboolean
-tuple_get_item (TupleInfo         *info,
+tuple_get_item (TupleInfo          *info,
                 GVariantMemberInfo *item,
                 gsize              *d,
                 gsize              *e)
@@ -278,6 +379,23 @@ tuple_get_item (TupleInfo         *info,
   return TRUE;
 }
 
+/* Read the documentation for #GVariantMemberInfo in gvarianttype.h
+ * before attempting to understand this.
+ *
+ * This function adds one set of "magic constant" values (for one item
+ * in the tuple) to the table.
+ *
+ * The algorithm in tuple_generate_table() calculates values of 'a', 'b'
+ * and 'c' for each item, such that the procedure for finding the item
+ * is to start at the end of the previous variable-sized item, add 'a',
+ * then round up to the nearest multiple of 'b', then then add 'c'.
+ * Note that 'b' is stored in the usual "one less than" form.  ie:
+ *
+ *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
+ *
+ * We tweak these values a little to allow for a slightly easier
+ * computation and more compact storage.
+ */
 static void
 tuple_table_append (GVariantMemberInfo **items,
                     gsize                i,
@@ -287,17 +405,88 @@ tuple_table_append (GVariantMemberInfo **items,
 {
   GVariantMemberInfo *item = (*items)++;
 
-  /* §4.1.3 */
-  a += ~b & c;
-  c &= b;
-
-  /* XXX not documented anywhere */
-  a += b;
-  b = ~b;
+  /* We can shift multiples of the alignment size from 'c' into 'a'.
+   * As long as we're shifting whole multiples, it won't affect the
+   * result.  This means that we can take the "aligned" portion off of
+   * 'c' and add it into 'a'.
+   *
+   *  Imagine (for sake of clarity) that ROUND_10 rounds up to the
+   *  nearest 10.  It is clear that:
+   *
+   *   ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10)
+   *
+   * ie: remove the 10s portion of 'c' and add it onto 'a'.
+   *
+   * To put some numbers on it, imagine we start with a = 34 and c = 27:
+   *
+   *  ROUND_10(34) + 27 = 40 + 27 = 67
+   *
+   * but also, we can split 27 up into 20 and 7 and do this:
+   *
+   *  ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67
+   *                ^^    ^
+   * without affecting the result.  We do that here.
+   *
+   * This reduction in the size of 'c' means that we can store it in a
+   * gchar instead of a gsize.  Due to how the structure is packed, this
+   * ends up saving us 'two pointer sizes' per item in each tuple when
+   * allocating using GSlice.
+   */
+  a += ~b & c;    /* take the "aligned" part of 'c' and add to 'a' */
+  c &= b;         /* chop 'c' to contain only the unaligned part */
+
+
+  /* Finally, we made one last adjustment.  Recall:
+   *
+   *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
+   *
+   * Forgetting the '+ c' for the moment:
+   *
+   *   ROUND_UP(prev_end + a, (b + 1));
+   *
+   * we can do a "round up" operation by adding 1 less than the amount
+   * to round up to, then rounding down.  ie:
+   *
+   *   #define ROUND_UP(x, y)    ROUND_DOWN(x + (y-1), y)
+   *
+   * Of course, for rounding down to a power of two, we can just mask
+   * out the appropriate number of low order bits:
+   *
+   *   #define ROUND_DOWN(x, y)  (x & ~(y - 1))
+   *
+   * Which gives us
+   *
+   *   #define ROUND_UP(x, y)    (x + (y - 1) & ~(y - 1))
+   *
+   * but recall that our alignment value 'b' is already "one less".
+   * This means that to round 'prev_end + a' up to 'b' we can just do:
+   *
+   *   ((prev_end + a) + b) & ~b
+   *
+   * Associativity, and putting the 'c' back on:
+   *
+   *   (prev_end + (a + b)) & ~b + c
+   *
+   * Now, since (a + b) is constant, we can just add 'b' to 'a' now and
+   * store that as the number to add to prev_end.  Then we use ~b as the
+   * number to take a bitwise 'and' with.  Finally, 'c' is added on.
+   *
+   * Note, however, that all the low order bits of the 'aligned' value
+   * are masked out and that all of the high order bits of 'c' have been
+   * "moved" to 'a' (in the previous step).  This means that there are
+   * no overlapping bits in the addition -- so we can do a bitwise 'or'
+   * equivalently.
+   *
+   * This means that we can now compute the start address of a given
+   * item in the tuple using the algorithm given in the documentation
+   * for #GVariantMemberInfo:
+   *
+   *   item_start = ((prev_end + a) & b) | c;
+   */
 
   item->i = i;
-  item->a = a;
-  item->b = b;
+  item->a = a + b;
+  item->b = ~b;
   item->c = c;
 }
 
@@ -308,83 +497,189 @@ tuple_align (gsize offset,
   return offset + ((-offset) & alignment);
 }
 
+/* This function is the heart of the algorithm for calculating 'i', 'a',
+ * 'b' and 'c' for each item in the tuple.
+ *
+ * Imagine we want to find the start of the "i" in the type "(su(qx)ni)".
+ * That's a string followed by a uint32, then a tuple containing a
+ * uint16 and a int64, then an int16, then our "i".  In order to get to
+ * our "i" we:
+ *
+ * Start at the end of the string, align to 4 (for the uint32), add 4.
+ * Align to 8, add 16 (for the tuple).  Align to 2, add 2 (for the
+ * int16).  Then we're there.  It turns out that, given 3 simple rules,
+ * we can flatten this iteration into one addition, one alignment, then
+ * one more addition.
+ *
+ * The algorithm keeps track of the values 'a', 'b', and 'c' such that
+ * in order to get to the current point, you add 'a', align to 'b' then
+ * add 'c'.  'b' is kept in "one less than" form.  The algorithm then
+ * plays through the process of "align to X", "add Y", adjusting the
+ * values of 'a', 'b' and 'c' as necessary.
+ *
+ * The 3 rules have been proven correct, but are provided here without
+ * proof:
+ *
+ *  1) in order to "align to 'd'" where 'd' is less than or equal to the
+ *     largest level of alignment seen so far ('b'), you align 'c' to
+ *     'd'.
+ *  2) in order to "align to 'd'" where 'd' is greater than the largest
+ *     level of alignment seen so far, you add 'c' aligned to 'b' to the
+ *     value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment
+ *     seen') and reset 'c' to 0.
+ *  3) in order to "add 'e'", just add 'e' to 'c'.
+ */
 static void
 tuple_generate_table (TupleInfo *info)
 {
   GVariantMemberInfo *items = info->members;
   gsize i = -1, a = 0, b = 0, c = 0, d, e;
 
-  /* §4.1.2 */
+  /* iterate over each item in the tuple.
+   *   'd' will be the alignment of the item (in one-less form)
+   *   'e' will be the fixed size (or 0 for variable-size items)
+   */
   while (tuple_get_item (info, items, &d, &e))
     {
+      /* align to 'd' */
       if (d <= b)
-        c = tuple_align (c, d);
+        c = tuple_align (c, d);                   /* rule 1 */
       else
-        a += tuple_align (c, b), b = d, c = 0;
+        a += tuple_align (c, b), b = d, c = 0;    /* rule 2 */
 
+      /* the start of the item is at this point (ie: right after we
+       * have aligned for it).  store this information in the table.
+       */
       tuple_table_append (&items, i, a, b, c);
 
+      /* "move past" the item by adding in its size. */
       if (e == 0)
+        /* variable size:
+         *
+         * we'll have an offset stored to mark the end of this item, so
+         * just bump the offset index to give us a new starting point
+         * and reset all the counters.
+         */
         i++, a = b = c = 0;
       else
-        c += e;
+        /* fixed size */
+        c += e;                                   /* rule 3 */
     }
 }
 
 static void
-tuple_set_self_info (TupleInfo *info)
+tuple_set_base_info (TupleInfo *info)
 {
+  GVariantTypeInfo *base = &info->container.info;
+
   if (info->n_members > 0)
     {
       GVariantMemberInfo *m;
 
-      info->self.alignment = 0;
+      /* the alignment requirement of the tuple is the alignment
+       * requirement of its largest item.
+       */
+      base->alignment = 0;
       for (m = info->members; m < &info->members[info->n_members]; m++)
-        info->self.alignment |= m->type->alignment;
-      m--;
+        /* can find the max of a list of "one less than" powers of two
+         * by 'or'ing them
+         */
+        base->alignment |= m->type->alignment;
+
+      m--; /* take 'm' back to the last item */
 
+      /* the structure only has a fixed size if no variable-size
+       * offsets are stored and the last item is fixed-sized too (since
+       * an offset is never stored for the last item).
+       */
       if (m->i == -1 && m->type->fixed_size)
-        info->self.fixed_size = tuple_align (((m->a & m->b) | m->c) + m->type->fixed_size,
-                                              info->self.alignment);
+        /* in that case, the fixed size can be found by finding the
+         * start of the last item (in the usual way) and adding its
+         * fixed size.
+         *
+         * if a tuple has a fixed size then it is always a multiple of
+         * the alignment requirement (to make packing into arrays
+         * easier) so we round up to that here.
+         */
+        base->fixed_size =
+          tuple_align (((m->a & m->b) | m->c) + m->type->fixed_size,
+                       base->alignment);
       else
-        info->self.fixed_size = 0;
+        /* else, the tuple is not fixed size */
+        base->fixed_size = 0;
     }
   else
     {
-      info->self.alignment = 0;
-      info->self.fixed_size = 1;
+      /* the empty tuple: '()'.
+       *
+       * has a size of 1 and an no alignment requirement.
+       *
+       * It has a size of 1 (not 0) for two practical reasons:
+       *
+       *  1) So we can determine how many of them are in an array
+       *     without dividing by zero or without other tricks.
+       *
+       *  2) Even if we had some trick to know the number of items in
+       *     the array (as GVariant did at one time) this would open a
+       *     potential denial of service attack: an attacker could send
+       *     you an extremely small array (in terms of number of bytes)
+       *     containing trillions of zero-sized items.  If you iterated
+       *     over this array you would effectively infinite-loop your
+       *     program.  By forcing a size of at least one, we bound the
+       *     amount of computation done in response to a message to a
+       *     reasonable function of the size of that message.
+       */
+      base->alignment = 0;
+      base->fixed_size = 1;
     }
 }
 
-static GVariantTypeInfo *
+static ContainerInfo *
 tuple_info_new (const GVariantType *type)
 {
   TupleInfo *info;
 
   info = g_slice_new (TupleInfo);
-  info->self.info_class = TUPLE_INFO_CLASS;
+  info->container.info.container_class = TUPLE_INFO_CLASS;
 
   tuple_allocate_members (type, &info->members, &info->n_members);
   tuple_generate_table (info);
-  tuple_set_self_info (info);
+  tuple_set_base_info (info);
 
-  return (GVariantTypeInfo *) info;
+  return (ContainerInfo *) info;
 }
 
+/* < private >
+ * g_variant_type_info_n_members:
+ * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
+ *
+ * Returns the number of members in a tuple or dictionary entry type.
+ * For a dictionary entry this will always be 2.
+ */
 gsize
 g_variant_type_info_n_members (GVariantTypeInfo *info)
 {
   return TUPLE_INFO (info)->n_members;
 }
 
+/* < private >
+ * g_variant_type_info_member_info:
+ * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
+ * @index: the member to fetch information for
+ *
+ * Returns the #GVariantMemberInfo for a given member.  See
+ * documentation for that structure for why you would want this
+ * information.
+ *
+ * @index must refer to a valid child (ie: strictly less than
+ * g_variant_type_info_n_members() returns).
+ */
 const GVariantMemberInfo *
 g_variant_type_info_member_info (GVariantTypeInfo *info,
                                  gsize             index)
 {
   TupleInfo *tuple_info = TUPLE_INFO (info);
 
-  g_assert_cmpint (info->ref_count, >, 0);
-
   if (index < tuple_info->n_members)
     return &tuple_info->members[index];
 
@@ -395,6 +690,19 @@ g_variant_type_info_member_info (GVariantTypeInfo *info,
 static GStaticRecMutex g_variant_type_info_lock = G_STATIC_REC_MUTEX_INIT;
 static GHashTable *g_variant_type_info_table;
 
+/* < private >
+ * g_variant_type_info_get:
+ * @type: a #GVariantType
+ *
+ * Returns a reference to a #GVariantTypeInfo for @type.
+ *
+ * If an info structure already exists for this type, a new reference is
+ * returned.  If not, the required calculations are performed and a new
+ * info structure is returned.
+ *
+ * It is appropriate to call g_variant_type_info_unref() on the return
+ * value.
+ */
 GVariantTypeInfo *
 g_variant_type_info_get (const GVariantType *type)
 {
@@ -421,18 +729,21 @@ g_variant_type_info_get (const GVariantType *type)
 
       if (info == NULL)
         {
+          ContainerInfo *container;
+
           if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
               type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY)
             {
-              info = array_info_new (type);
+              container = array_info_new (type);
             }
           else /* tuple or dict entry */
             {
-              info = tuple_info_new (type);
+              container = tuple_info_new (type);
             }
 
-          info->type_string = type_string;
-          info->ref_count = 1;
+          info = (GVariantTypeInfo *) container;
+          container->type_string = type_string;
+          container->ref_count = 1;
 
           g_hash_table_insert (g_variant_type_info_table, type_string, info);
           type_string = NULL;
@@ -463,41 +774,57 @@ g_variant_type_info_get (const GVariantType *type)
     }
 }
 
+/* < private >
+ * g_variant_type_info_ref:
+ * @info: a #GVariantTypeInfo
+ *
+ * Adds a reference to @info.
+ */
 GVariantTypeInfo *
 g_variant_type_info_ref (GVariantTypeInfo *info)
 {
   g_variant_type_info_check (info, 0);
 
-  if (info->info_class)
+  if (info->container_class)
     {
-      g_assert_cmpint (info->ref_count, >, 0);
-      g_atomic_int_inc (&info->ref_count);
+      ContainerInfo *container = (ContainerInfo *) info;
+
+      g_assert_cmpint (container->ref_count, >, 0);
+      g_atomic_int_inc (&container->ref_count);
     }
 
   return info;
 }
 
+/* < private >
+ * g_variant_type_info_unref:
+ * @info: a #GVariantTypeInfo
+ *
+ * Releases a reference held on @info.  This may result in @info being
+ * freed.
+ */
 void
 g_variant_type_info_unref (GVariantTypeInfo *info)
 {
   g_variant_type_info_check (info, 0);
 
-  if (info->info_class)
+  if (info->container_class)
     {
-      g_assert_cmpint (info->ref_count, >, 0);
+      ContainerInfo *container = (ContainerInfo *) info;
 
-      if (g_atomic_int_dec_and_test (&info->ref_count))
+      if (g_atomic_int_dec_and_test (&container->ref_count))
         {
           g_static_rec_mutex_lock (&g_variant_type_info_lock);
-          g_hash_table_remove (g_variant_type_info_table, info->type_string);
+          g_hash_table_remove (g_variant_type_info_table,
+                               container->type_string);
           g_static_rec_mutex_unlock (&g_variant_type_info_lock);
 
-          g_free (info->type_string);
+          g_free (container->type_string);
 
-          if (info->info_class == ARRAY_INFO_CLASS)
+          if (info->container_class == ARRAY_INFO_CLASS)
             array_info_free (info);
 
-          else if (info->info_class == TUPLE_INFO_CLASS)
+          else if (info->container_class == TUPLE_INFO_CLASS)
             tuple_info_free (info);
 
           else



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]