mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-14 19:55:12 +01:00
merge GVariantTypeInfo
This commit is contained in:
parent
4c58a85dd1
commit
0f246e28ca
@ -172,6 +172,8 @@ libglib_2_0_la_SOURCES = \
|
||||
gunicodeprivate.h \
|
||||
gurifuncs.c \
|
||||
gutils.c \
|
||||
gvarianttypeinfo.h \
|
||||
gvarianttypeinfo.c \
|
||||
gvarianttype.c \
|
||||
gdebug.h \
|
||||
gprintf.c \
|
||||
|
841
glib/gvarianttypeinfo.c
Normal file
841
glib/gvarianttypeinfo.c
Normal file
@ -0,0 +1,841 @@
|
||||
/*
|
||||
* Copyright © 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "gvarianttypeinfo.h"
|
||||
#include <glib.h>
|
||||
|
||||
#include "galias.h"
|
||||
|
||||
/* < private >
|
||||
* GVariantTypeInfo:
|
||||
*
|
||||
* This structure contains the necessary information to facilitate the
|
||||
* serialisation and fast deserialisation of a given type of GVariant
|
||||
* 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 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
|
||||
* another GVariantTypeInfo has that type as a subtype. For example, if
|
||||
* a process contains a single GVariant instance with type "(asv)", then
|
||||
* container GVariantTypeInfo structures will exist for "(asv)" and
|
||||
* for "as" (note that "s" and "v" always exist in the static array).
|
||||
*
|
||||
* 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
|
||||
{
|
||||
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;
|
||||
|
||||
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
|
||||
* "as"). The container GVariantTypeInfo structure holds a reference to
|
||||
* the element typeinfo.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
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'. See the
|
||||
* comment on GVariantMemberInfo in gvarianttypeinfo.h.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
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 fixed_aligned(x) x, x - 1
|
||||
#define unaligned 0, 0
|
||||
#define aligned(x) 0, x - 1
|
||||
/* 'b' */ { fixed_aligned(1) }, /* boolean */
|
||||
/* 'c' */ { },
|
||||
/* 'd' */ { fixed_aligned(8) }, /* double */
|
||||
/* 'e' */ { },
|
||||
/* 'f' */ { },
|
||||
/* 'g' */ { unaligned }, /* signature string */
|
||||
/* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */
|
||||
/* 'i' */ { fixed_aligned(4) }, /* int32 */
|
||||
/* 'j' */ { },
|
||||
/* 'k' */ { },
|
||||
/* 'l' */ { },
|
||||
/* 'm' */ { },
|
||||
/* 'n' */ { fixed_aligned(2) }, /* int16 */
|
||||
/* 'o' */ { unaligned }, /* object path string */
|
||||
/* 'p' */ { },
|
||||
/* 'q' */ { fixed_aligned(2) }, /* uint16 */
|
||||
/* 'r' */ { },
|
||||
/* 's' */ { unaligned }, /* string */
|
||||
/* 't' */ { fixed_aligned(8) }, /* uint64 */
|
||||
/* 'u' */ { fixed_aligned(4) }, /* uint32 */
|
||||
/* 'v' */ { aligned(8) }, /* variant */
|
||||
/* 'w' */ { },
|
||||
/* '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 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);
|
||||
|
||||
if (info->container_class)
|
||||
{
|
||||
ContainerInfo *container = (ContainerInfo *) info;
|
||||
|
||||
/* extra checks for containers */
|
||||
g_assert_cmpint (container->ref_count, >, 0);
|
||||
g_assert (container->type_string != NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gint index;
|
||||
|
||||
/* 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_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] != ' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* < 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);
|
||||
|
||||
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)
|
||||
{
|
||||
g_variant_type_info_check (info, 0);
|
||||
|
||||
if (alignment)
|
||||
*alignment = info->alignment;
|
||||
|
||||
if (fixed_size)
|
||||
*fixed_size = info->fixed_size;
|
||||
}
|
||||
|
||||
/* == array == */
|
||||
#define ARRAY_INFO_CLASS 'a'
|
||||
static ArrayInfo *
|
||||
ARRAY_INFO (GVariantTypeInfo *info)
|
||||
{
|
||||
g_variant_type_info_check (info, ARRAY_INFO_CLASS);
|
||||
|
||||
return (ArrayInfo *) info;
|
||||
}
|
||||
|
||||
static void
|
||||
array_info_free (GVariantTypeInfo *info)
|
||||
{
|
||||
ArrayInfo *array_info;
|
||||
|
||||
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 ContainerInfo *
|
||||
array_info_new (const GVariantType *type)
|
||||
{
|
||||
ArrayInfo *info;
|
||||
|
||||
info = g_slice_new (ArrayInfo);
|
||||
info->container.info.container_class = ARRAY_INFO_CLASS;
|
||||
|
||||
info->element = g_variant_type_info_get (g_variant_type_element (type));
|
||||
info->container.info.alignment = info->element->alignment;
|
||||
info->container.info.fixed_size = 0;
|
||||
|
||||
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,
|
||||
gsize *fixed_size)
|
||||
{
|
||||
g_variant_type_info_query (ARRAY_INFO (info)->element,
|
||||
alignment, fixed_size);
|
||||
}
|
||||
|
||||
/* == tuple == */
|
||||
#define TUPLE_INFO_CLASS 'r'
|
||||
static TupleInfo *
|
||||
TUPLE_INFO (GVariantTypeInfo *info)
|
||||
{
|
||||
g_variant_type_info_check (info, TUPLE_INFO_CLASS);
|
||||
|
||||
return (TupleInfo *) info;
|
||||
}
|
||||
|
||||
static void
|
||||
tuple_info_free (GVariantTypeInfo *info)
|
||||
{
|
||||
TupleInfo *tuple_info;
|
||||
gint i;
|
||||
|
||||
g_assert (info->container_class == TUPLE_INFO_CLASS);
|
||||
tuple_info = (TupleInfo *) info;
|
||||
|
||||
for (i = 0; i < tuple_info->n_members; i++)
|
||||
g_variant_type_info_unref (tuple_info->members[i].type);
|
||||
|
||||
g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members,
|
||||
tuple_info->members);
|
||||
g_slice_free (TupleInfo, tuple_info);
|
||||
}
|
||||
|
||||
static void
|
||||
tuple_allocate_members (const GVariantType *type,
|
||||
GVariantMemberInfo **members,
|
||||
gsize *n_members)
|
||||
{
|
||||
const GVariantType *item_type;
|
||||
gsize i = 0;
|
||||
|
||||
*n_members = g_variant_type_n_items (type);
|
||||
*members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members);
|
||||
|
||||
item_type = g_variant_type_first (type);
|
||||
while (item_type)
|
||||
{
|
||||
(*members)[i++].type = g_variant_type_info_get (item_type);
|
||||
item_type = g_variant_type_next (item_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,
|
||||
GVariantMemberInfo *item,
|
||||
gsize *d,
|
||||
gsize *e)
|
||||
{
|
||||
if (&info->members[info->n_members] == item)
|
||||
return FALSE;
|
||||
|
||||
*d = item->type->alignment;
|
||||
*e = item->type->fixed_size;
|
||||
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,
|
||||
gsize a,
|
||||
gsize b,
|
||||
gsize c)
|
||||
{
|
||||
GVariantMemberInfo *item = (*items)++;
|
||||
|
||||
/* 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 + b;
|
||||
item->b = ~b;
|
||||
item->c = c;
|
||||
}
|
||||
|
||||
static gsize
|
||||
tuple_align (gsize offset,
|
||||
guint alignment)
|
||||
{
|
||||
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 loop below plays through each item in the tuple, querying its
|
||||
* alignment and fixed_size into 'd' and 'e', respectively. At all
|
||||
* times the variables 'a', 'b', and 'c' are maintained 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. For each item, the proper
|
||||
* alignment is applied to find the values of 'a', 'b' and 'c' to get to
|
||||
* the start of that item. Those values are recorded into the table.
|
||||
* The fixed size of the item (if applicable) is then added on.
|
||||
*
|
||||
* These 3 rules are how 'a', 'b' and 'c' are modified for alignment and
|
||||
* addition of fixed size. They have been proven correct but are
|
||||
* presented 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;
|
||||
|
||||
/* 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); /* rule 1 */
|
||||
else
|
||||
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
|
||||
/* fixed size */
|
||||
c += e; /* rule 3 */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tuple_set_base_info (TupleInfo *info)
|
||||
{
|
||||
GVariantTypeInfo *base = &info->container.info;
|
||||
|
||||
if (info->n_members > 0)
|
||||
{
|
||||
GVariantMemberInfo *m;
|
||||
|
||||
/* 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++)
|
||||
/* 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)
|
||||
/* 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
|
||||
/* else, the tuple is not fixed size */
|
||||
base->fixed_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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 ContainerInfo *
|
||||
tuple_info_new (const GVariantType *type)
|
||||
{
|
||||
TupleInfo *info;
|
||||
|
||||
info = g_slice_new (TupleInfo);
|
||||
info->container.info.container_class = TUPLE_INFO_CLASS;
|
||||
|
||||
tuple_allocate_members (type, &info->members, &info->n_members);
|
||||
tuple_generate_table (info);
|
||||
tuple_set_base_info (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);
|
||||
|
||||
if (index < tuple_info->n_members)
|
||||
return &tuple_info->members[index];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* == new/ref/unref == */
|
||||
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)
|
||||
{
|
||||
char type_char;
|
||||
|
||||
type_char = g_variant_type_peek_string (type)[0];
|
||||
|
||||
if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
|
||||
type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY ||
|
||||
type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE ||
|
||||
type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY)
|
||||
{
|
||||
GVariantTypeInfo *info;
|
||||
gchar *type_string;
|
||||
|
||||
if G_UNLIKELY (g_variant_type_info_table == NULL)
|
||||
g_variant_type_info_table = g_hash_table_new (g_str_hash,
|
||||
g_str_equal);
|
||||
|
||||
type_string = g_variant_type_dup_string (type);
|
||||
|
||||
g_static_rec_mutex_lock (&g_variant_type_info_lock);
|
||||
info = g_hash_table_lookup (g_variant_type_info_table, type_string);
|
||||
|
||||
if (info == NULL)
|
||||
{
|
||||
ContainerInfo *container;
|
||||
|
||||
if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
|
||||
type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY)
|
||||
{
|
||||
container = array_info_new (type);
|
||||
}
|
||||
else /* tuple or dict entry */
|
||||
{
|
||||
container = tuple_info_new (type);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
g_variant_type_info_ref (info);
|
||||
|
||||
g_static_rec_mutex_unlock (&g_variant_type_info_lock);
|
||||
g_variant_type_info_check (info, 0);
|
||||
g_free (type_string);
|
||||
|
||||
return info;
|
||||
}
|
||||
else
|
||||
{
|
||||
const GVariantTypeInfo *info;
|
||||
int index;
|
||||
|
||||
index = type_char - 'b';
|
||||
g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
|
||||
g_assert_cmpint (0, <=, index);
|
||||
g_assert_cmpint (index, <, 24);
|
||||
|
||||
info = g_variant_type_info_basic_table + index;
|
||||
g_variant_type_info_check (info, 0);
|
||||
|
||||
return (GVariantTypeInfo *) info;
|
||||
}
|
||||
}
|
||||
|
||||
/* < 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->container_class)
|
||||
{
|
||||
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->container_class)
|
||||
{
|
||||
ContainerInfo *container = (ContainerInfo *) info;
|
||||
|
||||
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,
|
||||
container->type_string);
|
||||
g_static_rec_mutex_unlock (&g_variant_type_info_lock);
|
||||
|
||||
g_free (container->type_string);
|
||||
|
||||
if (info->container_class == ARRAY_INFO_CLASS)
|
||||
array_info_free (info);
|
||||
|
||||
else if (info->container_class == TUPLE_INFO_CLASS)
|
||||
tuple_info_free (info);
|
||||
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
140
glib/gvarianttypeinfo.h
Normal file
140
glib/gvarianttypeinfo.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright © 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_VARIANT_TYPE_INFO_H__
|
||||
#define __G_VARIANT_TYPE_INFO_H__
|
||||
|
||||
#include <glib/gvarianttype.h>
|
||||
|
||||
#define G_VARIANT_TYPE_INFO_CHAR_MAYBE 'm'
|
||||
#define G_VARIANT_TYPE_INFO_CHAR_ARRAY 'a'
|
||||
#define G_VARIANT_TYPE_INFO_CHAR_TUPLE '('
|
||||
#define G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY '{'
|
||||
#define G_VARIANT_TYPE_INFO_CHAR_VARIANT 'v'
|
||||
#define g_variant_type_info_get_type_char(info) \
|
||||
(g_variant_type_info_get_type_string(info)[0])
|
||||
|
||||
typedef struct _GVariantTypeInfo GVariantTypeInfo;
|
||||
|
||||
/* < private >
|
||||
* GVariantMemberInfo:
|
||||
*
|
||||
* This structure describes how to construct a GVariant instance
|
||||
* corresponding to a given child of a tuple or dictionary entry in a
|
||||
* very short constant time. It contains the typeinfo of the child,
|
||||
* along with 4 constants that allow the bounds of the child's
|
||||
* serialised data within the container's serialised data to be found
|
||||
* very efficiently.
|
||||
*
|
||||
* Since dictionary entries are serialised as if they were tuples of 2
|
||||
* items, the term "tuple" will be used here in the general sense to
|
||||
* refer to tuples and dictionary entries.
|
||||
*
|
||||
* BACKGROUND:
|
||||
* The serialised data for a tuple contains an array of "offsets" at
|
||||
* the end. There is one "offset" in this array for each
|
||||
* variable-sized item in the tuple (except for the last one). The
|
||||
* offset points to the end point of that item's serialised data. The
|
||||
* procedure for finding the start point is described below. An
|
||||
* offset is not needed for the last item because the end point of the
|
||||
* last item is merely the end point of the container itself (after
|
||||
* the offsets array has been accounted for). An offset is not needed
|
||||
* for fixed-sized items (like integers) because, due to their fixed
|
||||
* size, the end point is a constant addition to the start point.
|
||||
*
|
||||
* It is clear that the starting point of a given item in the tuple is
|
||||
* determined by the items that preceed it in the tuple. Logically,
|
||||
* the start point of a particular item in a given type of tuple can
|
||||
* be determined entirely by the end point of the nearest
|
||||
* variable-sized item that came before it (or from the start of the
|
||||
* container itself in case there is no preceeding variable-sized
|
||||
* item). In the case of "(isis)" for example, in order to find out
|
||||
* the start point of the last string, one must start at the end point
|
||||
* of the first string, align to 4 (for the integer's alignment) and
|
||||
* then add 4 (for storing the integer). That's the point where the
|
||||
* string starts (since no special alignment is required for strings).
|
||||
*
|
||||
* Of course, this process requires iterating over the types in the
|
||||
* tuple up to the item of interest. As it turns out, it is possible
|
||||
* to determine 3 constants 'a', 'b', and 'c' for each item in the
|
||||
* tuple, such that, given the ending offset of the nearest previous
|
||||
* variable-sized item (prev_end), a very simple calculation can be
|
||||
* performed to determine the start of the item of interest.
|
||||
*
|
||||
* The constants in this structure are used as follows:
|
||||
*
|
||||
* First, among the array of offets contained in the tuple, 'i' is the
|
||||
* index of the offset that refers to the end of the variable-sized item
|
||||
* preceeding the item of interest. If no variable-sized items preceed
|
||||
* this item, then 'i' will be -1.
|
||||
*
|
||||
* Let 'prev_end' be the end offset of the previous item (or 0 in the
|
||||
* case that there was no such item). The start address of this item
|
||||
* can then be calculate using 'a', 'b', and 'c':
|
||||
*
|
||||
* item_start = ((prev_end + a) & b) | c;
|
||||
*
|
||||
* For details about how 'a', 'b' and 'c' are calculated, see the
|
||||
* comments at the point of the implementation in gvariantypeinfo.c.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GVariantTypeInfo *type;
|
||||
|
||||
gsize i, a;
|
||||
gint8 b, c;
|
||||
} GVariantMemberInfo;
|
||||
|
||||
/* query */
|
||||
G_GNUC_INTERNAL
|
||||
const gchar * g_variant_type_info_get_type_string (GVariantTypeInfo *typeinfo);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void g_variant_type_info_query (GVariantTypeInfo *typeinfo,
|
||||
guint *alignment,
|
||||
gsize *size);
|
||||
|
||||
/* array */
|
||||
G_GNUC_INTERNAL
|
||||
GVariantTypeInfo * g_variant_type_info_element (GVariantTypeInfo *typeinfo);
|
||||
G_GNUC_INTERNAL
|
||||
void g_variant_type_info_query_element (GVariantTypeInfo *typeinfo,
|
||||
guint *alignment,
|
||||
gsize *size);
|
||||
|
||||
/* structure */
|
||||
G_GNUC_INTERNAL
|
||||
gsize g_variant_type_info_n_members (GVariantTypeInfo *typeinfo);
|
||||
G_GNUC_INTERNAL
|
||||
const GVariantMemberInfo * g_variant_type_info_member_info (GVariantTypeInfo *typeinfo,
|
||||
gsize index);
|
||||
|
||||
/* new/ref/unref */
|
||||
G_GNUC_INTERNAL
|
||||
GVariantTypeInfo * g_variant_type_info_get (const GVariantType *type);
|
||||
G_GNUC_INTERNAL
|
||||
GVariantTypeInfo * g_variant_type_info_ref (GVariantTypeInfo *typeinfo);
|
||||
G_GNUC_INTERNAL
|
||||
void g_variant_type_info_unref (GVariantTypeInfo *typeinfo);
|
||||
|
||||
#endif /* __G_VARIANT_TYPE_INFO_H__ */
|
@ -27,20 +27,22 @@ randomly (gdouble prob)
|
||||
}
|
||||
|
||||
/* corecursion */
|
||||
static GVariantType *append_tuple_type_string (GString *, GString *, gint);
|
||||
static GVariantType *
|
||||
append_tuple_type_string (GString *, GString *, gboolean, gint);
|
||||
|
||||
/* append a random GVariantType to a GString
|
||||
* append a description of the type to another GString
|
||||
* return what the type is
|
||||
*/
|
||||
static GVariantType *
|
||||
append_type_string (GString *string,
|
||||
GString *description,
|
||||
gint depth)
|
||||
append_type_string (GString *string,
|
||||
GString *description,
|
||||
gboolean definite,
|
||||
gint depth)
|
||||
{
|
||||
if (!depth-- || randomly (0.3))
|
||||
{
|
||||
gchar b = BASIC[g_test_rand_int_range (0, N_BASIC)];
|
||||
gchar b = BASIC[g_test_rand_int_range (0, N_BASIC - definite)];
|
||||
g_string_append_c (string, b);
|
||||
g_string_append_c (description, b);
|
||||
|
||||
@ -82,7 +84,7 @@ append_type_string (GString *string,
|
||||
{
|
||||
GVariantType *result;
|
||||
|
||||
switch (g_test_rand_int_range (0, 7))
|
||||
switch (g_test_rand_int_range (0, definite ? 5 : 7))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
@ -90,7 +92,8 @@ append_type_string (GString *string,
|
||||
|
||||
g_string_append_c (string, 'a');
|
||||
g_string_append (description, "a of ");
|
||||
element = append_type_string (string, description, depth);
|
||||
element = append_type_string (string, description,
|
||||
definite, depth);
|
||||
result = g_variant_type_new_array (element);
|
||||
g_variant_type_free (element);
|
||||
}
|
||||
@ -104,7 +107,8 @@ append_type_string (GString *string,
|
||||
|
||||
g_string_append_c (string, 'm');
|
||||
g_string_append (description, "m of ");
|
||||
element = append_type_string (string, description, depth);
|
||||
element = append_type_string (string, description,
|
||||
definite, depth);
|
||||
result = g_variant_type_new_maybe (element);
|
||||
g_variant_type_free (element);
|
||||
}
|
||||
@ -113,7 +117,8 @@ append_type_string (GString *string,
|
||||
break;
|
||||
|
||||
case 2:
|
||||
result = append_tuple_type_string (string, description, depth);
|
||||
result = append_tuple_type_string (string, description,
|
||||
definite, depth);
|
||||
|
||||
g_assert (g_variant_type_is_tuple (result));
|
||||
break;
|
||||
@ -124,9 +129,9 @@ append_type_string (GString *string,
|
||||
|
||||
g_string_append_c (string, '{');
|
||||
g_string_append (description, "e of [");
|
||||
key = append_type_string (string, description, 0);
|
||||
key = append_type_string (string, description, definite, 0);
|
||||
g_string_append (description, ", ");
|
||||
value = append_type_string (string, description, depth);
|
||||
value = append_type_string (string, description, definite, depth);
|
||||
g_string_append_c (description, ']');
|
||||
g_string_append_c (string, '}');
|
||||
result = g_variant_type_new_dict_entry (key, value);
|
||||
@ -167,9 +172,10 @@ append_type_string (GString *string,
|
||||
}
|
||||
|
||||
static GVariantType *
|
||||
append_tuple_type_string (GString *string,
|
||||
GString *description,
|
||||
gint depth)
|
||||
append_tuple_type_string (GString *string,
|
||||
GString *description,
|
||||
gboolean definite,
|
||||
gint depth)
|
||||
{
|
||||
GVariantType *result, *other_result;
|
||||
GVariantType **types;
|
||||
@ -184,7 +190,7 @@ append_tuple_type_string (GString *string,
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
types[i] = append_type_string (string, description, depth);
|
||||
types[i] = append_type_string (string, description, definite, depth);
|
||||
|
||||
if (i < size - 1)
|
||||
g_string_append (description, ", ");
|
||||
@ -493,13 +499,13 @@ generate_subtype (const gchar *type_string)
|
||||
|
||||
/* then store the replacement in the GString */
|
||||
if (type_string[l] == 'r')
|
||||
replacement = append_tuple_type_string (result, junk, 3);
|
||||
replacement = append_tuple_type_string (result, junk, FALSE, 3);
|
||||
|
||||
else if (type_string[l] == '?')
|
||||
replacement = append_type_string (result, junk, 0);
|
||||
replacement = append_type_string (result, junk, FALSE, 0);
|
||||
|
||||
else if (type_string[l] == '*')
|
||||
replacement = append_type_string (result, junk, 3);
|
||||
replacement = append_type_string (result, junk, FALSE, 3);
|
||||
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
@ -584,7 +590,7 @@ test_gvarianttype (void)
|
||||
*
|
||||
* exercises type constructor functions and g_variant_type_copy()
|
||||
*/
|
||||
type = append_type_string (type_string, description, 6);
|
||||
type = append_type_string (type_string, description, FALSE, 6);
|
||||
|
||||
/* convert the type string to a type and ensure that it is equal
|
||||
* to the one produced with the type constructor routines
|
||||
@ -630,7 +636,7 @@ test_gvarianttype (void)
|
||||
/* concatenate another type to the type string and ensure that
|
||||
* the result is recognised as being invalid
|
||||
*/
|
||||
other_type = append_type_string (type_string, description, 2);
|
||||
other_type = append_type_string (type_string, description, FALSE, 2);
|
||||
|
||||
g_string_free (description, TRUE);
|
||||
g_string_free (type_string, TRUE);
|
||||
@ -639,12 +645,395 @@ test_gvarianttype (void)
|
||||
}
|
||||
}
|
||||
|
||||
#undef G_GNUC_INTERNAL
|
||||
#define G_GNUC_INTERNAL static
|
||||
|
||||
#define DISABLE_VISIBILITY
|
||||
#include <glib/gvarianttypeinfo.c>
|
||||
|
||||
#define ALIGNED(x, y) (((x + (y - 1)) / y) * y)
|
||||
|
||||
/* do our own calculation of the fixed_size and alignment of a type
|
||||
* using a simple algorithm to make sure the "fancy" one in the
|
||||
* implementation is correct.
|
||||
*/
|
||||
static void
|
||||
calculate_type_info (const GVariantType *type,
|
||||
gsize *fixed_size,
|
||||
guint *alignment)
|
||||
{
|
||||
if (g_variant_type_is_array (type) ||
|
||||
g_variant_type_is_maybe (type))
|
||||
{
|
||||
calculate_type_info (g_variant_type_element (type), NULL, alignment);
|
||||
|
||||
if (fixed_size)
|
||||
*fixed_size = 0;
|
||||
}
|
||||
else if (g_variant_type_is_tuple (type) ||
|
||||
g_variant_type_is_dict_entry (type))
|
||||
{
|
||||
if (g_variant_type_n_items (type))
|
||||
{
|
||||
const GVariantType *sub;
|
||||
gboolean variable;
|
||||
gsize size;
|
||||
guint al;
|
||||
|
||||
variable = FALSE;
|
||||
size = 0;
|
||||
al = 0;
|
||||
|
||||
sub = g_variant_type_first (type);
|
||||
do
|
||||
{
|
||||
gsize this_fs;
|
||||
guint this_al;
|
||||
|
||||
calculate_type_info (sub, &this_fs, &this_al);
|
||||
|
||||
al = MAX (al, this_al);
|
||||
|
||||
if (!this_fs)
|
||||
{
|
||||
variable = TRUE;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
if (!variable)
|
||||
{
|
||||
size = ALIGNED (size, this_al);
|
||||
size += this_fs;
|
||||
}
|
||||
}
|
||||
while ((sub = g_variant_type_next (sub)));
|
||||
|
||||
size = ALIGNED (size, al);
|
||||
|
||||
if (alignment)
|
||||
*alignment = al;
|
||||
|
||||
if (fixed_size)
|
||||
*fixed_size = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fixed_size)
|
||||
*fixed_size = 1;
|
||||
|
||||
if (alignment)
|
||||
*alignment = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gint fs, al;
|
||||
|
||||
if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
|
||||
{
|
||||
al = fs = 1;
|
||||
}
|
||||
|
||||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
|
||||
{
|
||||
al = fs = 2;
|
||||
}
|
||||
|
||||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_UINT32) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
|
||||
{
|
||||
al = fs = 4;
|
||||
}
|
||||
|
||||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_UINT64) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
|
||||
{
|
||||
al = fs = 8;
|
||||
}
|
||||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH) ||
|
||||
g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
|
||||
{
|
||||
al = 1;
|
||||
fs = 0;
|
||||
}
|
||||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT))
|
||||
{
|
||||
al = 8;
|
||||
fs = 0;
|
||||
}
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
if (fixed_size)
|
||||
*fixed_size = fs;
|
||||
|
||||
if (alignment)
|
||||
*alignment = al;
|
||||
}
|
||||
}
|
||||
|
||||
/* same as the describe_type() function above, but iterates over
|
||||
* typeinfo instead of types.
|
||||
*/
|
||||
static gchar *
|
||||
describe_info (GVariantTypeInfo *info)
|
||||
{
|
||||
gchar *result;
|
||||
|
||||
switch (g_variant_type_info_get_type_char (info))
|
||||
{
|
||||
case G_VARIANT_TYPE_INFO_CHAR_MAYBE:
|
||||
{
|
||||
gchar *element;
|
||||
|
||||
element = describe_info (g_variant_type_info_element (info));
|
||||
result = g_strdup_printf ("m of %s", element);
|
||||
g_free (element);
|
||||
}
|
||||
break;
|
||||
|
||||
case G_VARIANT_TYPE_INFO_CHAR_ARRAY:
|
||||
{
|
||||
gchar *element;
|
||||
|
||||
element = describe_info (g_variant_type_info_element (info));
|
||||
result = g_strdup_printf ("a of %s", element);
|
||||
g_free (element);
|
||||
}
|
||||
break;
|
||||
|
||||
case G_VARIANT_TYPE_INFO_CHAR_TUPLE:
|
||||
{
|
||||
const gchar *sep = "";
|
||||
GString *string;
|
||||
gint length;
|
||||
gint i;
|
||||
|
||||
string = g_string_new ("t of [");
|
||||
length = g_variant_type_info_n_members (info);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
const GVariantMemberInfo *minfo;
|
||||
gchar *subtype;
|
||||
|
||||
g_string_append (string, sep);
|
||||
sep = ", ";
|
||||
|
||||
minfo = g_variant_type_info_member_info (info, i);
|
||||
subtype = describe_info (minfo->type);
|
||||
g_string_append (string, subtype);
|
||||
g_free (subtype);
|
||||
}
|
||||
|
||||
g_string_append_c (string, ']');
|
||||
|
||||
result = g_string_free (string, FALSE);
|
||||
}
|
||||
break;
|
||||
|
||||
case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY:
|
||||
{
|
||||
const GVariantMemberInfo *keyinfo, *valueinfo;
|
||||
gchar *key, *value;
|
||||
|
||||
g_assert_cmpint (g_variant_type_info_n_members (info), ==, 2);
|
||||
keyinfo = g_variant_type_info_member_info (info, 0);
|
||||
valueinfo = g_variant_type_info_member_info (info, 1);
|
||||
key = describe_info (keyinfo->type);
|
||||
value = describe_info (valueinfo->type);
|
||||
result = g_strjoin ("", "e of [", key, ", ", value, "]", NULL);
|
||||
g_free (key);
|
||||
g_free (value);
|
||||
}
|
||||
break;
|
||||
|
||||
case G_VARIANT_TYPE_INFO_CHAR_VARIANT:
|
||||
result = g_strdup ("V");
|
||||
break;
|
||||
|
||||
default:
|
||||
result = g_strdup (g_variant_type_info_get_type_string (info));
|
||||
g_assert_cmpint (strlen (result), ==, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* check that the O(1) method of calculating offsets meshes with the
|
||||
* results of simple iteration.
|
||||
*/
|
||||
static void
|
||||
check_offsets (GVariantTypeInfo *info,
|
||||
const GVariantType *type)
|
||||
{
|
||||
gint flavour;
|
||||
gint length;
|
||||
|
||||
length = g_variant_type_info_n_members (info);
|
||||
g_assert_cmpint (length, ==, g_variant_type_n_items (type));
|
||||
|
||||
/* the 'flavour' is the low order bits of the ending point of
|
||||
* variable-size items in the tuple. this lets us test that the type
|
||||
* info is correct for various starting alignments.
|
||||
*/
|
||||
for (flavour = 0; flavour < 8; flavour++)
|
||||
{
|
||||
const GVariantType *subtype;
|
||||
gsize last_offset_index;
|
||||
gsize last_offset;
|
||||
gsize position;
|
||||
gint i;
|
||||
|
||||
subtype = g_variant_type_first (type);
|
||||
last_offset_index = -1;
|
||||
last_offset = 0;
|
||||
position = 0;
|
||||
|
||||
/* go through the tuple, keeping track of our position */
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
gsize fixed_size;
|
||||
guint alignment;
|
||||
|
||||
calculate_type_info (subtype, &fixed_size, &alignment);
|
||||
|
||||
position = ALIGNED (position, alignment);
|
||||
|
||||
/* compare our current aligned position (ie: the start of this
|
||||
* item) to the start offset that would be calculated if we
|
||||
* used the type info
|
||||
*/
|
||||
{
|
||||
const GVariantMemberInfo *member;
|
||||
gsize start;
|
||||
|
||||
member = g_variant_type_info_member_info (info, i);
|
||||
g_assert_cmpint (member->i, ==, last_offset_index);
|
||||
|
||||
/* do the calculation using the typeinfo */
|
||||
start = last_offset;
|
||||
start += member->a;
|
||||
start &= member->b;
|
||||
start |= member->c;
|
||||
|
||||
/* did we reach the same spot? */
|
||||
g_assert_cmpint (start, ==, position);
|
||||
}
|
||||
|
||||
if (fixed_size)
|
||||
{
|
||||
/* fixed size. add that size. */
|
||||
position += fixed_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* variable size. do the flavouring. */
|
||||
while ((position & 0x7) != flavour)
|
||||
position++;
|
||||
|
||||
/* and store the offset, just like it would be in the
|
||||
* serialised data.
|
||||
*/
|
||||
last_offset = position;
|
||||
last_offset_index++;
|
||||
}
|
||||
|
||||
/* next type */
|
||||
subtype = g_variant_type_next (subtype);
|
||||
}
|
||||
|
||||
/* make sure we used up exactly all the types */
|
||||
g_assert (subtype == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_gvarianttypeinfo (void)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 2000; i++)
|
||||
{
|
||||
GString *type_string, *description;
|
||||
gsize fixed_size1, fixed_size2;
|
||||
guint alignment1, alignment2;
|
||||
GVariantTypeInfo *info;
|
||||
GVariantType *type;
|
||||
gchar *desc;
|
||||
|
||||
type_string = g_string_new (NULL);
|
||||
description = g_string_new (NULL);
|
||||
|
||||
/* random type */
|
||||
type = append_type_string (type_string, description, TRUE, 6);
|
||||
|
||||
/* create a typeinfo for it */
|
||||
info = g_variant_type_info_get (type);
|
||||
|
||||
/* make sure the typeinfo has the right type string */
|
||||
g_assert_cmpstr (g_variant_type_info_get_type_string (info), ==,
|
||||
type_string->str);
|
||||
|
||||
/* calculate the alignment and fixed size, compare to the
|
||||
* typeinfo's calculations
|
||||
*/
|
||||
calculate_type_info (type, &fixed_size1, &alignment1);
|
||||
g_variant_type_info_query (info, &alignment2, &fixed_size2);
|
||||
g_assert_cmpint (fixed_size1, ==, fixed_size2);
|
||||
g_assert_cmpint (alignment1, ==, alignment2 + 1);
|
||||
|
||||
/* test the iteration functions over typeinfo structures by
|
||||
* "describing" the typeinfo and verifying equality.
|
||||
*/
|
||||
desc = describe_info (info);
|
||||
g_assert_cmpstr (desc, ==, description->str);
|
||||
|
||||
/* do extra checks for containers */
|
||||
if (g_variant_type_is_array (type) ||
|
||||
g_variant_type_is_maybe (type))
|
||||
{
|
||||
const GVariantType *element;
|
||||
gsize efs1, efs2;
|
||||
guint ea1, ea2;
|
||||
|
||||
element = g_variant_type_element (type);
|
||||
calculate_type_info (element, &efs1, &ea1);
|
||||
g_variant_type_info_query_element (info, &ea2, &efs2);
|
||||
g_assert_cmpint (efs1, ==, efs2);
|
||||
g_assert_cmpint (ea1, ==, ea2 + 1);
|
||||
|
||||
g_assert_cmpint (ea1, ==, alignment1);
|
||||
g_assert_cmpint (0, ==, fixed_size1);
|
||||
}
|
||||
else if (g_variant_type_is_tuple (type) ||
|
||||
g_variant_type_is_dict_entry (type))
|
||||
{
|
||||
/* make sure the "magic constants" are working */
|
||||
check_offsets (info, type);
|
||||
}
|
||||
|
||||
g_string_free (type_string, TRUE);
|
||||
g_string_free (description, TRUE);
|
||||
g_variant_type_info_unref (info);
|
||||
g_variant_type_free (type);
|
||||
g_free (desc);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/gvariant/type", test_gvarianttype);
|
||||
g_test_add_func ("/gvariant/typeinfo", test_gvarianttypeinfo);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user