Blame glib/gvarianttypeinfo.c

Packit ae235b
/*
Packit ae235b
 * Copyright © 2008 Ryan Lortie
Packit ae235b
 * Copyright © 2010 Codethink Limited
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General Public
Packit ae235b
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 *
Packit ae235b
 * Author: Ryan Lortie <desrt@desrt.ca>
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include "gvarianttypeinfo.h"
Packit ae235b
Packit ae235b
#include <glib/gtestutils.h>
Packit ae235b
#include <glib/gthread.h>
Packit ae235b
#include <glib/gslice.h>
Packit ae235b
#include <glib/ghash.h>
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * GVariantTypeInfo:
Packit ae235b
 *
Packit ae235b
 * This structure contains the necessary information to facilitate the
Packit ae235b
 * serialisation and fast deserialisation of a given type of GVariant
Packit ae235b
 * value.  A GVariant instance holds a pointer to one of these
Packit ae235b
 * structures to provide for efficient operation.
Packit ae235b
 *
Packit ae235b
 * The GVariantTypeInfo structures for all of the base types, plus the
Packit ae235b
 * "variant" type are stored in a read-only static array.
Packit ae235b
 *
Packit ae235b
 * For container types, a hash table and reference counting is used to
Packit ae235b
 * ensure that only one of these structures exists for any given type.
Packit ae235b
 * In general, a container GVariantTypeInfo will exist for a given type
Packit ae235b
 * only if one or more GVariant instances of that type exist or if
Packit ae235b
 * another GVariantTypeInfo has that type as a subtype.  For example, if
Packit ae235b
 * a process contains a single GVariant instance with type "(asv)", then
Packit ae235b
 * container GVariantTypeInfo structures will exist for "(asv)" and
Packit ae235b
 * for "as" (note that "s" and "v" always exist in the static array).
Packit ae235b
 *
Packit ae235b
 * The trickiest part of GVariantTypeInfo (and in fact, the major reason
Packit ae235b
 * for its existence) is the storage of somewhat magical constants that
Packit ae235b
 * allow for O(1) lookups of items in tuples.  This is described below.
Packit ae235b
 *
Packit ae235b
 * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is
Packit ae235b
 * contained inside of an ArrayInfo or TupleInfo, respectively.  This
Packit ae235b
 * allows the storage of the necessary additional information.
Packit ae235b
 *
Packit ae235b
 * 'fixed_size' is set to the fixed size of the type, if applicable, or
Packit ae235b
 * 0 otherwise (since no type has a fixed size of 0).
Packit ae235b
 *
Packit ae235b
 * 'alignment' is set to one less than the alignment requirement for
Packit ae235b
 * this type.  This makes many operations much more convenient.
Packit ae235b
 */
Packit ae235b
struct _GVariantTypeInfo
Packit ae235b
{
Packit ae235b
  gsize fixed_size;
Packit ae235b
  guchar alignment;
Packit ae235b
  guchar container_class;
Packit ae235b
};
Packit ae235b
Packit ae235b
/* Container types are reference counted.  They also need to have their
Packit ae235b
 * type string stored explicitly since it is not merely a single letter.
Packit ae235b
 */
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  GVariantTypeInfo info;
Packit ae235b
Packit ae235b
  gchar *type_string;
Packit ae235b
  gint ref_count;
Packit ae235b
} ContainerInfo;
Packit ae235b
Packit ae235b
/* For 'array' and 'maybe' types, we store some extra information on the
Packit ae235b
 * end of the GVariantTypeInfo struct -- the element type (ie: "s" for
Packit ae235b
 * "as").  The container GVariantTypeInfo structure holds a reference to
Packit ae235b
 * the element typeinfo.
Packit ae235b
 */
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  ContainerInfo container;
Packit ae235b
Packit ae235b
  GVariantTypeInfo *element;
Packit ae235b
} ArrayInfo;
Packit ae235b
Packit ae235b
/* For 'tuple' and 'dict entry' types, we store extra information for
Packit ae235b
 * each member -- its type and how to find it inside the serialised data
Packit ae235b
 * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'.  See the
Packit ae235b
 * comment on GVariantMemberInfo in gvarianttypeinfo.h.
Packit ae235b
 */
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  ContainerInfo container;
Packit ae235b
Packit ae235b
  GVariantMemberInfo *members;
Packit ae235b
  gsize n_members;
Packit ae235b
} TupleInfo;
Packit ae235b
Packit ae235b
Packit ae235b
/* Hard-code the base types in a constant array */
Packit ae235b
static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
Packit ae235b
#define fixed_aligned(x)  x, x - 1
Packit ae235b
#define not_a_type             0,
Packit ae235b
#define unaligned         0, 0
Packit ae235b
#define aligned(x)        0, x - 1
Packit ae235b
  /* 'b' */ { fixed_aligned(1) },   /* boolean */
Packit ae235b
  /* 'c' */ { not_a_type },
Packit ae235b
  /* 'd' */ { fixed_aligned(8) },   /* double */
Packit ae235b
  /* 'e' */ { not_a_type },
Packit ae235b
  /* 'f' */ { not_a_type },
Packit ae235b
  /* 'g' */ { unaligned        },   /* signature string */
Packit ae235b
  /* 'h' */ { fixed_aligned(4) },   /* file handle (int32) */
Packit ae235b
  /* 'i' */ { fixed_aligned(4) },   /* int32 */
Packit ae235b
  /* 'j' */ { not_a_type },
Packit ae235b
  /* 'k' */ { not_a_type },
Packit ae235b
  /* 'l' */ { not_a_type },
Packit ae235b
  /* 'm' */ { not_a_type },
Packit ae235b
  /* 'n' */ { fixed_aligned(2) },   /* int16 */
Packit ae235b
  /* 'o' */ { unaligned        },   /* object path string */
Packit ae235b
  /* 'p' */ { not_a_type },
Packit ae235b
  /* 'q' */ { fixed_aligned(2) },   /* uint16 */
Packit ae235b
  /* 'r' */ { not_a_type },
Packit ae235b
  /* 's' */ { unaligned        },   /* string */
Packit ae235b
  /* 't' */ { fixed_aligned(8) },   /* uint64 */
Packit ae235b
  /* 'u' */ { fixed_aligned(4) },   /* uint32 */
Packit ae235b
  /* 'v' */ { aligned(8)       },   /* variant */
Packit ae235b
  /* 'w' */ { not_a_type },
Packit ae235b
  /* 'x' */ { fixed_aligned(8) },   /* int64 */
Packit ae235b
  /* 'y' */ { fixed_aligned(1) },   /* byte */
Packit ae235b
#undef fixed_aligned
Packit ae235b
#undef not_a_type
Packit ae235b
#undef unaligned
Packit ae235b
#undef aligned
Packit ae235b
};
Packit ae235b
Packit ae235b
/* We need to have type strings to return for the base types.  We store
Packit ae235b
 * those in another array.  Since all base type strings are single
Packit ae235b
 * characters this is easy.  By not storing pointers to strings into the
Packit ae235b
 * GVariantTypeInfo itself, we save a bunch of relocations.
Packit ae235b
 */
Packit ae235b
static const char g_variant_type_info_basic_chars[24][2] = {
Packit ae235b
  "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
Packit ae235b
  "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
Packit ae235b
};
Packit ae235b
Packit ae235b
/* sanity checks to make debugging easier */
Packit ae235b
static void
Packit ae235b
g_variant_type_info_check (const GVariantTypeInfo *info,
Packit ae235b
                           char                    container_class)
Packit ae235b
{
Packit ae235b
  g_assert (!container_class || info->container_class == container_class);
Packit ae235b
Packit ae235b
  /* alignment can only be one of these */
Packit ae235b
  g_assert (info->alignment == 0 || info->alignment == 1 ||
Packit ae235b
            info->alignment == 3 || info->alignment == 7);
Packit ae235b
Packit ae235b
  if (info->container_class)
Packit ae235b
    {
Packit ae235b
      ContainerInfo *container = (ContainerInfo *) info;
Packit ae235b
Packit ae235b
      /* extra checks for containers */
Packit ae235b
      g_assert_cmpint (container->ref_count, >, 0);
Packit ae235b
      g_assert (container->type_string != NULL);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      gint index;
Packit ae235b
Packit ae235b
      /* if not a container, then ensure that it is a valid member of
Packit ae235b
       * the basic types table
Packit ae235b
       */
Packit ae235b
      index = info - g_variant_type_info_basic_table;
Packit ae235b
Packit ae235b
      g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
Packit ae235b
      g_assert (G_N_ELEMENTS (g_variant_type_info_basic_chars) == 24);
Packit ae235b
      g_assert (0 <= index && index < 24);
Packit ae235b
      g_assert (g_variant_type_info_basic_chars[index][0] != ' ');
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_get_type_string:
Packit ae235b
 * @info: a #GVariantTypeInfo
Packit ae235b
 *
Packit ae235b
 * Gets the type string for @info.  The string is nul-terminated.
Packit ae235b
 */
Packit ae235b
const gchar *
Packit ae235b
g_variant_type_info_get_type_string (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  g_variant_type_info_check (info, 0);
Packit ae235b
Packit ae235b
  if (info->container_class)
Packit ae235b
    {
Packit ae235b
      ContainerInfo *container = (ContainerInfo *) info;
Packit ae235b
Packit ae235b
      /* containers have their type string stored inside them */
Packit ae235b
      return container->type_string;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      gint index;
Packit ae235b
Packit ae235b
      /* look up the type string in the base type array.  the call to
Packit ae235b
       * g_variant_type_info_check() above already ensured validity.
Packit ae235b
       */
Packit ae235b
      index = info - g_variant_type_info_basic_table;
Packit ae235b
Packit ae235b
      return g_variant_type_info_basic_chars[index];
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_query:
Packit ae235b
 * @info: a #GVariantTypeInfo
Packit ae235b
 * @alignment: (nullable): the location to store the alignment, or %NULL
Packit ae235b
 * @fixed_size: (nullable): the location to store the fixed size, or %NULL
Packit ae235b
 *
Packit ae235b
 * Queries @info to determine the alignment requirements and fixed size
Packit ae235b
 * (if any) of the type.
Packit ae235b
 *
Packit ae235b
 * @fixed_size, if non-%NULL is set to the fixed size of the type, or 0
Packit ae235b
 * to indicate that the type is a variable-sized type.  No type has a
Packit ae235b
 * fixed size of 0.
Packit ae235b
 *
Packit ae235b
 * @alignment, if non-%NULL, is set to one less than the required
Packit ae235b
 * alignment of the type.  For example, for a 32bit integer, @alignment
Packit ae235b
 * would be set to 3.  This allows you to round an integer up to the
Packit ae235b
 * proper alignment by performing the following efficient calculation:
Packit ae235b
 *
Packit ae235b
 *   offset += ((-offset) & alignment);
Packit ae235b
 */
Packit ae235b
void
Packit ae235b
g_variant_type_info_query (GVariantTypeInfo *info,
Packit ae235b
                           guint            *alignment,
Packit ae235b
                           gsize            *fixed_size)
Packit ae235b
{
Packit ae235b
  g_variant_type_info_check (info, 0);
Packit ae235b
Packit ae235b
  if (alignment)
Packit ae235b
    *alignment = info->alignment;
Packit ae235b
Packit ae235b
  if (fixed_size)
Packit ae235b
    *fixed_size = info->fixed_size;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* == array == */
Packit ae235b
#define GV_ARRAY_INFO_CLASS 'a'
Packit ae235b
static ArrayInfo *
Packit ae235b
GV_ARRAY_INFO (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  g_variant_type_info_check (info, GV_ARRAY_INFO_CLASS);
Packit ae235b
Packit ae235b
  return (ArrayInfo *) info;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
array_info_free (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  ArrayInfo *array_info;
Packit ae235b
Packit ae235b
  g_assert (info->container_class == GV_ARRAY_INFO_CLASS);
Packit ae235b
  array_info = (ArrayInfo *) info;
Packit ae235b
Packit ae235b
  g_variant_type_info_unref (array_info->element);
Packit ae235b
  g_slice_free (ArrayInfo, array_info);
Packit ae235b
}
Packit ae235b
Packit ae235b
static ContainerInfo *
Packit ae235b
array_info_new (const GVariantType *type)
Packit ae235b
{
Packit ae235b
  ArrayInfo *info;
Packit ae235b
Packit ae235b
  info = g_slice_new (ArrayInfo);
Packit ae235b
  info->container.info.container_class = GV_ARRAY_INFO_CLASS;
Packit ae235b
Packit ae235b
  info->element = g_variant_type_info_get (g_variant_type_element (type));
Packit ae235b
  info->container.info.alignment = info->element->alignment;
Packit ae235b
  info->container.info.fixed_size = 0;
Packit ae235b
Packit ae235b
  return (ContainerInfo *) info;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_element:
Packit ae235b
 * @info: a #GVariantTypeInfo for an array or maybe type
Packit ae235b
 *
Packit ae235b
 * Returns the element type for the array or maybe type.  A reference is
Packit ae235b
 * not added, so the caller must add their own.
Packit ae235b
 */
Packit ae235b
GVariantTypeInfo *
Packit ae235b
g_variant_type_info_element (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  return GV_ARRAY_INFO (info)->element;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_query_element:
Packit ae235b
 * @info: a #GVariantTypeInfo for an array or maybe type
Packit ae235b
 * @alignment: (nullable): the location to store the alignment, or %NULL
Packit ae235b
 * @fixed_size: (nullable): the location to store the fixed size, or %NULL
Packit ae235b
 *
Packit ae235b
 * Returns the alignment requires and fixed size (if any) for the
Packit ae235b
 * element type of the array.  This call is a convenience wrapper around
Packit ae235b
 * g_variant_type_info_element() and g_variant_type_info_query().
Packit ae235b
 */
Packit ae235b
void
Packit ae235b
g_variant_type_info_query_element (GVariantTypeInfo *info,
Packit ae235b
                                   guint            *alignment,
Packit ae235b
                                   gsize            *fixed_size)
Packit ae235b
{
Packit ae235b
  g_variant_type_info_query (GV_ARRAY_INFO (info)->element,
Packit ae235b
                             alignment, fixed_size);
Packit ae235b
}
Packit ae235b
Packit ae235b
/* == tuple == */
Packit ae235b
#define GV_TUPLE_INFO_CLASS 'r'
Packit ae235b
static TupleInfo *
Packit ae235b
GV_TUPLE_INFO (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  g_variant_type_info_check (info, GV_TUPLE_INFO_CLASS);
Packit ae235b
Packit ae235b
  return (TupleInfo *) info;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
tuple_info_free (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  TupleInfo *tuple_info;
Packit ae235b
  gint i;
Packit ae235b
Packit ae235b
  g_assert (info->container_class == GV_TUPLE_INFO_CLASS);
Packit ae235b
  tuple_info = (TupleInfo *) info;
Packit ae235b
Packit ae235b
  for (i = 0; i < tuple_info->n_members; i++)
Packit ae235b
    g_variant_type_info_unref (tuple_info->members[i].type_info);
Packit ae235b
Packit ae235b
  g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members,
Packit ae235b
                 tuple_info->members);
Packit ae235b
  g_slice_free (TupleInfo, tuple_info);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
tuple_allocate_members (const GVariantType  *type,
Packit ae235b
                        GVariantMemberInfo **members,
Packit ae235b
                        gsize               *n_members)
Packit ae235b
{
Packit ae235b
  const GVariantType *item_type;
Packit ae235b
  gsize i = 0;
Packit ae235b
Packit ae235b
  *n_members = g_variant_type_n_items (type);
Packit ae235b
  *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members);
Packit ae235b
Packit ae235b
  item_type = g_variant_type_first (type);
Packit ae235b
  while (item_type)
Packit ae235b
    {
Packit ae235b
      GVariantMemberInfo *member = &(*members)[i++];
Packit ae235b
Packit ae235b
      member->type_info = g_variant_type_info_get (item_type);
Packit ae235b
      item_type = g_variant_type_next (item_type);
Packit ae235b
Packit ae235b
      if (member->type_info->fixed_size)
Packit ae235b
        member->ending_type = G_VARIANT_MEMBER_ENDING_FIXED;
Packit ae235b
      else if (item_type == NULL)
Packit ae235b
        member->ending_type = G_VARIANT_MEMBER_ENDING_LAST;
Packit ae235b
      else
Packit ae235b
        member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_assert (i == *n_members);
Packit ae235b
}
Packit ae235b
Packit ae235b
/* this is g_variant_type_info_query for a given member of the tuple.
Packit ae235b
 * before the access is done, it is ensured that the item is within
Packit ae235b
 * range and %FALSE is returned if not.
Packit ae235b
 */
Packit ae235b
static gboolean
Packit ae235b
tuple_get_item (TupleInfo          *info,
Packit ae235b
                GVariantMemberInfo *item,
Packit ae235b
                gsize              *d,
Packit ae235b
                gsize              *e)
Packit ae235b
{
Packit ae235b
  if (&info->members[info->n_members] == item)
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  *d = item->type_info->alignment;
Packit ae235b
  *e = item->type_info->fixed_size;
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* Read the documentation for #GVariantMemberInfo in gvarianttype.h
Packit ae235b
 * before attempting to understand this.
Packit ae235b
 *
Packit ae235b
 * This function adds one set of "magic constant" values (for one item
Packit ae235b
 * in the tuple) to the table.
Packit ae235b
 *
Packit ae235b
 * The algorithm in tuple_generate_table() calculates values of 'a', 'b'
Packit ae235b
 * and 'c' for each item, such that the procedure for finding the item
Packit ae235b
 * is to start at the end of the previous variable-sized item, add 'a',
Packit ae235b
 * then round up to the nearest multiple of 'b', then then add 'c'.
Packit ae235b
 * Note that 'b' is stored in the usual "one less than" form.  ie:
Packit ae235b
 *
Packit ae235b
 *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
Packit ae235b
 *
Packit ae235b
 * We tweak these values a little to allow for a slightly easier
Packit ae235b
 * computation and more compact storage.
Packit ae235b
 */
Packit ae235b
static void
Packit ae235b
tuple_table_append (GVariantMemberInfo **items,
Packit ae235b
                    gsize                i,
Packit ae235b
                    gsize                a,
Packit ae235b
                    gsize                b,
Packit ae235b
                    gsize                c)
Packit ae235b
{
Packit ae235b
  GVariantMemberInfo *item = (*items)++;
Packit ae235b
Packit ae235b
  /* We can shift multiples of the alignment size from 'c' into 'a'.
Packit ae235b
   * As long as we're shifting whole multiples, it won't affect the
Packit ae235b
   * result.  This means that we can take the "aligned" portion off of
Packit ae235b
   * 'c' and add it into 'a'.
Packit ae235b
   *
Packit ae235b
   *  Imagine (for sake of clarity) that ROUND_10 rounds up to the
Packit ae235b
   *  nearest 10.  It is clear that:
Packit ae235b
   *
Packit ae235b
   *   ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10)
Packit ae235b
   *
Packit ae235b
   * ie: remove the 10s portion of 'c' and add it onto 'a'.
Packit ae235b
   *
Packit ae235b
   * To put some numbers on it, imagine we start with a = 34 and c = 27:
Packit ae235b
   *
Packit ae235b
   *  ROUND_10(34) + 27 = 40 + 27 = 67
Packit ae235b
   *
Packit ae235b
   * but also, we can split 27 up into 20 and 7 and do this:
Packit ae235b
   *
Packit ae235b
   *  ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67
Packit ae235b
   *                ^^    ^
Packit ae235b
   * without affecting the result.  We do that here.
Packit ae235b
   *
Packit ae235b
   * This reduction in the size of 'c' means that we can store it in a
Packit ae235b
   * gchar instead of a gsize.  Due to how the structure is packed, this
Packit ae235b
   * ends up saving us 'two pointer sizes' per item in each tuple when
Packit ae235b
   * allocating using GSlice.
Packit ae235b
   */
Packit ae235b
  a += ~b & c;    /* take the "aligned" part of 'c' and add to 'a' */
Packit ae235b
  c &= b;         /* chop 'c' to contain only the unaligned part */
Packit ae235b
Packit ae235b
Packit ae235b
  /* Finally, we made one last adjustment.  Recall:
Packit ae235b
   *
Packit ae235b
   *   start = ROUND_UP(prev_end + a, (b + 1)) + c;
Packit ae235b
   *
Packit ae235b
   * Forgetting the '+ c' for the moment:
Packit ae235b
   *
Packit ae235b
   *   ROUND_UP(prev_end + a, (b + 1));
Packit ae235b
   *
Packit ae235b
   * we can do a "round up" operation by adding 1 less than the amount
Packit ae235b
   * to round up to, then rounding down.  ie:
Packit ae235b
   *
Packit ae235b
   *   #define ROUND_UP(x, y)    ROUND_DOWN(x + (y-1), y)
Packit ae235b
   *
Packit ae235b
   * Of course, for rounding down to a power of two, we can just mask
Packit ae235b
   * out the appropriate number of low order bits:
Packit ae235b
   *
Packit ae235b
   *   #define ROUND_DOWN(x, y)  (x & ~(y - 1))
Packit ae235b
   *
Packit ae235b
   * Which gives us
Packit ae235b
   *
Packit ae235b
   *   #define ROUND_UP(x, y)    (x + (y - 1) & ~(y - 1))
Packit ae235b
   *
Packit ae235b
   * but recall that our alignment value 'b' is already "one less".
Packit ae235b
   * This means that to round 'prev_end + a' up to 'b' we can just do:
Packit ae235b
   *
Packit ae235b
   *   ((prev_end + a) + b) & ~b
Packit ae235b
   *
Packit ae235b
   * Associativity, and putting the 'c' back on:
Packit ae235b
   *
Packit ae235b
   *   (prev_end + (a + b)) & ~b + c
Packit ae235b
   *
Packit ae235b
   * Now, since (a + b) is constant, we can just add 'b' to 'a' now and
Packit ae235b
   * store that as the number to add to prev_end.  Then we use ~b as the
Packit ae235b
   * number to take a bitwise 'and' with.  Finally, 'c' is added on.
Packit ae235b
   *
Packit ae235b
   * Note, however, that all the low order bits of the 'aligned' value
Packit ae235b
   * are masked out and that all of the high order bits of 'c' have been
Packit ae235b
   * "moved" to 'a' (in the previous step).  This means that there are
Packit ae235b
   * no overlapping bits in the addition -- so we can do a bitwise 'or'
Packit ae235b
   * equivalently.
Packit ae235b
   *
Packit ae235b
   * This means that we can now compute the start address of a given
Packit ae235b
   * item in the tuple using the algorithm given in the documentation
Packit ae235b
   * for #GVariantMemberInfo:
Packit ae235b
   *
Packit ae235b
   *   item_start = ((prev_end + a) & b) | c;
Packit ae235b
   */
Packit ae235b
Packit ae235b
  item->i = i;
Packit ae235b
  item->a = a + b;
Packit ae235b
  item->b = ~b;
Packit ae235b
  item->c = c;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gsize
Packit ae235b
tuple_align (gsize offset,
Packit ae235b
             guint alignment)
Packit ae235b
{
Packit ae235b
  return offset + ((-offset) & alignment);
Packit ae235b
}
Packit ae235b
Packit ae235b
/* This function is the heart of the algorithm for calculating 'i', 'a',
Packit ae235b
 * 'b' and 'c' for each item in the tuple.
Packit ae235b
 *
Packit ae235b
 * Imagine we want to find the start of the "i" in the type "(su(qx)ni)".
Packit ae235b
 * That's a string followed by a uint32, then a tuple containing a
Packit ae235b
 * uint16 and a int64, then an int16, then our "i".  In order to get to
Packit ae235b
 * our "i" we:
Packit ae235b
 *
Packit ae235b
 * Start at the end of the string, align to 4 (for the uint32), add 4.
Packit ae235b
 * Align to 8, add 16 (for the tuple).  Align to 2, add 2 (for the
Packit ae235b
 * int16).  Then we're there.  It turns out that, given 3 simple rules,
Packit ae235b
 * we can flatten this iteration into one addition, one alignment, then
Packit ae235b
 * one more addition.
Packit ae235b
 *
Packit ae235b
 * The loop below plays through each item in the tuple, querying its
Packit ae235b
 * alignment and fixed_size into 'd' and 'e', respectively.  At all
Packit ae235b
 * times the variables 'a', 'b', and 'c' are maintained such that in
Packit ae235b
 * order to get to the current point, you add 'a', align to 'b' then add
Packit ae235b
 * 'c'.  'b' is kept in "one less than" form.  For each item, the proper
Packit ae235b
 * alignment is applied to find the values of 'a', 'b' and 'c' to get to
Packit ae235b
 * the start of that item.  Those values are recorded into the table.
Packit ae235b
 * The fixed size of the item (if applicable) is then added on.
Packit ae235b
 *
Packit ae235b
 * These 3 rules are how 'a', 'b' and 'c' are modified for alignment and
Packit ae235b
 * addition of fixed size.  They have been proven correct but are
Packit ae235b
 * presented here, without proof:
Packit ae235b
 *
Packit ae235b
 *  1) in order to "align to 'd'" where 'd' is less than or equal to the
Packit ae235b
 *     largest level of alignment seen so far ('b'), you align 'c' to
Packit ae235b
 *     'd'.
Packit ae235b
 *  2) in order to "align to 'd'" where 'd' is greater than the largest
Packit ae235b
 *     level of alignment seen so far, you add 'c' aligned to 'b' to the
Packit ae235b
 *     value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment
Packit ae235b
 *     seen') and reset 'c' to 0.
Packit ae235b
 *  3) in order to "add 'e'", just add 'e' to 'c'.
Packit ae235b
 */
Packit ae235b
static void
Packit ae235b
tuple_generate_table (TupleInfo *info)
Packit ae235b
{
Packit ae235b
  GVariantMemberInfo *items = info->members;
Packit ae235b
  gsize i = -1, a = 0, b = 0, c = 0, d, e;
Packit ae235b
Packit ae235b
  /* iterate over each item in the tuple.
Packit ae235b
   *   'd' will be the alignment of the item (in one-less form)
Packit ae235b
   *   'e' will be the fixed size (or 0 for variable-size items)
Packit ae235b
   */
Packit ae235b
  while (tuple_get_item (info, items, &d, &e))
Packit ae235b
    {
Packit ae235b
      /* align to 'd' */
Packit ae235b
      if (d <= b)
Packit ae235b
        c = tuple_align (c, d);                   /* rule 1 */
Packit ae235b
      else
Packit ae235b
        a += tuple_align (c, b), b = d, c = 0;    /* rule 2 */
Packit ae235b
Packit ae235b
      /* the start of the item is at this point (ie: right after we
Packit ae235b
       * have aligned for it).  store this information in the table.
Packit ae235b
       */
Packit ae235b
      tuple_table_append (&items, i, a, b, c);
Packit ae235b
Packit ae235b
      /* "move past" the item by adding in its size. */
Packit ae235b
      if (e == 0)
Packit ae235b
        /* variable size:
Packit ae235b
         *
Packit ae235b
         * we'll have an offset stored to mark the end of this item, so
Packit ae235b
         * just bump the offset index to give us a new starting point
Packit ae235b
         * and reset all the counters.
Packit ae235b
         */
Packit ae235b
        i++, a = b = c = 0;
Packit ae235b
      else
Packit ae235b
        /* fixed size */
Packit ae235b
        c += e;                                   /* rule 3 */
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
tuple_set_base_info (TupleInfo *info)
Packit ae235b
{
Packit ae235b
  GVariantTypeInfo *base = &info->container.info;
Packit ae235b
Packit ae235b
  if (info->n_members > 0)
Packit ae235b
    {
Packit ae235b
      GVariantMemberInfo *m;
Packit ae235b
Packit ae235b
      /* the alignment requirement of the tuple is the alignment
Packit ae235b
       * requirement of its largest item.
Packit ae235b
       */
Packit ae235b
      base->alignment = 0;
Packit ae235b
      for (m = info->members; m < &info->members[info->n_members]; m++)
Packit ae235b
        /* can find the max of a list of "one less than" powers of two
Packit ae235b
         * by 'or'ing them
Packit ae235b
         */
Packit ae235b
        base->alignment |= m->type_info->alignment;
Packit ae235b
Packit ae235b
      m--; /* take 'm' back to the last item */
Packit ae235b
Packit ae235b
      /* the structure only has a fixed size if no variable-size
Packit ae235b
       * offsets are stored and the last item is fixed-sized too (since
Packit ae235b
       * an offset is never stored for the last item).
Packit ae235b
       */
Packit ae235b
      if (m->i == -1 && m->type_info->fixed_size)
Packit ae235b
        /* in that case, the fixed size can be found by finding the
Packit ae235b
         * start of the last item (in the usual way) and adding its
Packit ae235b
         * fixed size.
Packit ae235b
         *
Packit ae235b
         * if a tuple has a fixed size then it is always a multiple of
Packit ae235b
         * the alignment requirement (to make packing into arrays
Packit ae235b
         * easier) so we round up to that here.
Packit ae235b
         */
Packit ae235b
        base->fixed_size =
Packit ae235b
          tuple_align (((m->a & m->b) | m->c) + m->type_info->fixed_size,
Packit ae235b
                       base->alignment);
Packit ae235b
      else
Packit ae235b
        /* else, the tuple is not fixed size */
Packit ae235b
        base->fixed_size = 0;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      /* the empty tuple: '()'.
Packit ae235b
       *
Packit ae235b
       * has a size of 1 and an no alignment requirement.
Packit ae235b
       *
Packit ae235b
       * It has a size of 1 (not 0) for two practical reasons:
Packit ae235b
       *
Packit ae235b
       *  1) So we can determine how many of them are in an array
Packit ae235b
       *     without dividing by zero or without other tricks.
Packit ae235b
       *
Packit ae235b
       *  2) Even if we had some trick to know the number of items in
Packit ae235b
       *     the array (as GVariant did at one time) this would open a
Packit ae235b
       *     potential denial of service attack: an attacker could send
Packit ae235b
       *     you an extremely small array (in terms of number of bytes)
Packit ae235b
       *     containing trillions of zero-sized items.  If you iterated
Packit ae235b
       *     over this array you would effectively infinite-loop your
Packit ae235b
       *     program.  By forcing a size of at least one, we bound the
Packit ae235b
       *     amount of computation done in response to a message to a
Packit ae235b
       *     reasonable function of the size of that message.
Packit ae235b
       */
Packit ae235b
      base->alignment = 0;
Packit ae235b
      base->fixed_size = 1;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static ContainerInfo *
Packit ae235b
tuple_info_new (const GVariantType *type)
Packit ae235b
{
Packit ae235b
  TupleInfo *info;
Packit ae235b
Packit ae235b
  info = g_slice_new (TupleInfo);
Packit ae235b
  info->container.info.container_class = GV_TUPLE_INFO_CLASS;
Packit ae235b
Packit ae235b
  tuple_allocate_members (type, &info->members, &info->n_members);
Packit ae235b
  tuple_generate_table (info);
Packit ae235b
  tuple_set_base_info (info);
Packit ae235b
Packit ae235b
  return (ContainerInfo *) info;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_n_members:
Packit ae235b
 * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
Packit ae235b
 *
Packit ae235b
 * Returns the number of members in a tuple or dictionary entry type.
Packit ae235b
 * For a dictionary entry this will always be 2.
Packit ae235b
 */
Packit ae235b
gsize
Packit ae235b
g_variant_type_info_n_members (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  return GV_TUPLE_INFO (info)->n_members;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_member_info:
Packit ae235b
 * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
Packit ae235b
 * @index: the member to fetch information for
Packit ae235b
 *
Packit ae235b
 * Returns the #GVariantMemberInfo for a given member.  See
Packit ae235b
 * documentation for that structure for why you would want this
Packit ae235b
 * information.
Packit ae235b
 *
Packit ae235b
 * @index must refer to a valid child (ie: strictly less than
Packit ae235b
 * g_variant_type_info_n_members() returns).
Packit ae235b
 */
Packit ae235b
const GVariantMemberInfo *
Packit ae235b
g_variant_type_info_member_info (GVariantTypeInfo *info,
Packit ae235b
                                 gsize             index)
Packit ae235b
{
Packit ae235b
  TupleInfo *tuple_info = GV_TUPLE_INFO (info);
Packit ae235b
Packit ae235b
  if (index < tuple_info->n_members)
Packit ae235b
    return &tuple_info->members[index];
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* == new/ref/unref == */
Packit ae235b
static GRecMutex g_variant_type_info_lock;
Packit ae235b
static GHashTable *g_variant_type_info_table;
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_get:
Packit ae235b
 * @type: a #GVariantType
Packit ae235b
 *
Packit ae235b
 * Returns a reference to a #GVariantTypeInfo for @type.
Packit ae235b
 *
Packit ae235b
 * If an info structure already exists for this type, a new reference is
Packit ae235b
 * returned.  If not, the required calculations are performed and a new
Packit ae235b
 * info structure is returned.
Packit ae235b
 *
Packit ae235b
 * It is appropriate to call g_variant_type_info_unref() on the return
Packit ae235b
 * value.
Packit ae235b
 */
Packit ae235b
GVariantTypeInfo *
Packit ae235b
g_variant_type_info_get (const GVariantType *type)
Packit ae235b
{
Packit ae235b
  char type_char;
Packit ae235b
Packit ae235b
  type_char = g_variant_type_peek_string (type)[0];
Packit ae235b
Packit ae235b
  if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
Packit ae235b
      type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY ||
Packit ae235b
      type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE ||
Packit ae235b
      type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY)
Packit ae235b
    {
Packit ae235b
      GVariantTypeInfo *info;
Packit ae235b
      gchar *type_string;
Packit ae235b
Packit ae235b
      type_string = g_variant_type_dup_string (type);
Packit ae235b
Packit ae235b
      g_rec_mutex_lock (&g_variant_type_info_lock);
Packit ae235b
Packit ae235b
      if (g_variant_type_info_table == NULL)
Packit ae235b
        g_variant_type_info_table = g_hash_table_new (g_str_hash,
Packit ae235b
                                                      g_str_equal);
Packit ae235b
      info = g_hash_table_lookup (g_variant_type_info_table, type_string);
Packit ae235b
Packit ae235b
      if (info == NULL)
Packit ae235b
        {
Packit ae235b
          ContainerInfo *container;
Packit ae235b
Packit ae235b
          if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
Packit ae235b
              type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY)
Packit ae235b
            {
Packit ae235b
              container = array_info_new (type);
Packit ae235b
            }
Packit ae235b
          else /* tuple or dict entry */
Packit ae235b
            {
Packit ae235b
              container = tuple_info_new (type);
Packit ae235b
            }
Packit ae235b
Packit ae235b
          info = (GVariantTypeInfo *) container;
Packit ae235b
          container->type_string = type_string;
Packit ae235b
          container->ref_count = 1;
Packit ae235b
Packit ae235b
          g_hash_table_insert (g_variant_type_info_table, type_string, info);
Packit ae235b
          type_string = NULL;
Packit ae235b
        }
Packit ae235b
      else
Packit ae235b
        g_variant_type_info_ref (info);
Packit ae235b
Packit ae235b
      g_rec_mutex_unlock (&g_variant_type_info_lock);
Packit ae235b
      g_variant_type_info_check (info, 0);
Packit ae235b
      g_free (type_string);
Packit ae235b
Packit ae235b
      return info;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      const GVariantTypeInfo *info;
Packit ae235b
      int index;
Packit ae235b
Packit ae235b
      index = type_char - 'b';
Packit ae235b
      g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
Packit ae235b
      g_assert_cmpint (0, <=, index);
Packit ae235b
      g_assert_cmpint (index, <, 24);
Packit ae235b
Packit ae235b
      info = g_variant_type_info_basic_table + index;
Packit ae235b
      g_variant_type_info_check (info, 0);
Packit ae235b
Packit ae235b
      return (GVariantTypeInfo *) info;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_ref:
Packit ae235b
 * @info: a #GVariantTypeInfo
Packit ae235b
 *
Packit ae235b
 * Adds a reference to @info.
Packit ae235b
 */
Packit ae235b
GVariantTypeInfo *
Packit ae235b
g_variant_type_info_ref (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  g_variant_type_info_check (info, 0);
Packit ae235b
Packit ae235b
  if (info->container_class)
Packit ae235b
    {
Packit ae235b
      ContainerInfo *container = (ContainerInfo *) info;
Packit ae235b
Packit ae235b
      g_assert_cmpint (container->ref_count, >, 0);
Packit ae235b
      g_atomic_int_inc (&container->ref_count);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return info;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_variant_type_info_unref:
Packit ae235b
 * @info: a #GVariantTypeInfo
Packit ae235b
 *
Packit ae235b
 * Releases a reference held on @info.  This may result in @info being
Packit ae235b
 * freed.
Packit ae235b
 */
Packit ae235b
void
Packit ae235b
g_variant_type_info_unref (GVariantTypeInfo *info)
Packit ae235b
{
Packit ae235b
  g_variant_type_info_check (info, 0);
Packit ae235b
Packit ae235b
  if (info->container_class)
Packit ae235b
    {
Packit ae235b
      ContainerInfo *container = (ContainerInfo *) info;
Packit ae235b
Packit ae235b
      g_rec_mutex_lock (&g_variant_type_info_lock);
Packit ae235b
      if (g_atomic_int_dec_and_test (&container->ref_count))
Packit ae235b
        {
Packit ae235b
          g_hash_table_remove (g_variant_type_info_table,
Packit ae235b
                               container->type_string);
Packit ae235b
          if (g_hash_table_size (g_variant_type_info_table) == 0)
Packit ae235b
            {
Packit ae235b
              g_hash_table_unref (g_variant_type_info_table);
Packit ae235b
              g_variant_type_info_table = NULL;
Packit ae235b
            }
Packit ae235b
          g_rec_mutex_unlock (&g_variant_type_info_lock);
Packit ae235b
Packit ae235b
          g_free (container->type_string);
Packit ae235b
Packit ae235b
          if (info->container_class == GV_ARRAY_INFO_CLASS)
Packit ae235b
            array_info_free (info);
Packit ae235b
Packit ae235b
          else if (info->container_class == GV_TUPLE_INFO_CLASS)
Packit ae235b
            tuple_info_free (info);
Packit ae235b
Packit ae235b
          else
Packit ae235b
            g_assert_not_reached ();
Packit ae235b
        }
Packit ae235b
      else
Packit ae235b
        g_rec_mutex_unlock (&g_variant_type_info_lock);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
void
Packit ae235b
g_variant_type_info_assert_no_infos (void)
Packit ae235b
{
Packit ae235b
  g_assert (g_variant_type_info_table == NULL);
Packit ae235b
}