X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgvariant-core.c;h=a9db9a11c39790b6c413d84af6a8a9f941f6cb29;hb=2a53b4d0e2c98a14aedf31e38f0ad1fb2e8fe26f;hp=e3597e484d8ce83369f8e50257082a5124dfde3a;hpb=73007021796f33d7ccec4e5f2bb2b2f8660347f2;p=platform%2Fupstream%2Fglib.git
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
index e3597e4..a9db9a1 100644
--- a/glib/gvariant-core.c
+++ b/glib/gvariant-core.c
@@ -13,23 +13,23 @@
* 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.
+ * License along with this library; if not, see .
*/
+#include "config.h"
+
#include
+#include "glib-private.h"
#include
#include
#include
#include
-#include
+#include
#include
#include
#include
-#include "galias.h"
/*
* This file includes the structure definition for GVariant and a small
@@ -62,7 +62,7 @@ struct _GVariant
{
struct
{
- GBuffer *buffer;
+ GBytes *bytes;
gconstpointer data;
} serialised;
@@ -107,21 +107,11 @@ struct _GVariant
* The type_info field never changes during the life of the
* instance, so it can be accessed without a lock.
*
- * size: this is the size of the serialised form for the instance, if it
- * is known. If the instance is in serialised form then it is, by
- * definition, known. If the instance is in tree form then it may
- * be unknown (in which case it is -1). It is possible for the
- * size to be known when in tree form if, for example, the user
- * has called g_variant_get_size() without calling
- * g_variant_get_data(). Additionally, even when the user calls
- * g_variant_get_data() the size of the data must first be
- * determined so that a large enough buffer can be allocated for
- * the data.
- *
- * Once the size is known, it can never become unknown again.
- * g_variant_ensure_size() is used to ensure that the size is in
- * the known state -- it calculates the size if needed. After
- * that, the size field can be accessed without a lock.
+ * size: this is the size of the serialised form for the instance. It
+ * is known for serialised instances and also tree-form instances
+ * (for which it is calculated at construction time, from the
+ * known sizes of the children used). After construction, it
+ * never changes and therefore can be accessed without a lock.
*
* contents: a union containing either the information associated with
* holding a value in serialised form or holding a value in
@@ -134,16 +124,16 @@ struct _GVariant
* never be changed. It is therefore valid to access
* them without holding a lock.
*
- * .buffer: the #GBuffer that contains the memory pointed to by
+ * .bytes: the #GBytes that contains the memory pointed to by
* .data, or %NULL if .data is %NULL. In the event that
* the instance was deserialised from another instance,
- * then the buffer will be shared by both of them. When
+ * then the bytes will be shared by both of them. When
* the instance is freed, this reference must be released
- * with g_buffer_unref().
+ * with g_bytes_unref().
*
* .data: the serialised data (of size 'size') of the instance.
* This pointer should not be freed or modified in any way.
- * #GBuffer is responsible for memory management.
+ * #GBytes is responsible for memory management.
*
* This pointer may be %NULL in two cases:
*
@@ -260,6 +250,31 @@ g_variant_release_children (GVariant *value)
g_free (value->contents.tree.children);
}
+/* < private >
+ * g_variant_lock_in_tree_form:
+ * @value: a #GVariant
+ *
+ * Locks @value if it is in tree form.
+ *
+ * Returns: %TRUE if @value is now in tree form with the lock acquired
+ */
+static gboolean
+g_variant_lock_in_tree_form (GVariant *value)
+{
+ if (g_atomic_int_get (&value->state) & STATE_SERIALISED)
+ return FALSE;
+
+ g_variant_lock (value);
+
+ if (value->state & STATE_SERIALISED)
+ {
+ g_variant_unlock (value);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/* This begins the main body of the recursive serialiser.
*
* There are 3 functions here that work as a team with the serialiser to
@@ -277,31 +292,19 @@ g_variant_release_children (GVariant *value)
* instances are always in serialised form. For these instances,
* storing their serialised form merely involves a memcpy().
*
- * Serialisation is a two-step process. First, the size of the
- * serialised data must be calculated so that an appropriately-sized
- * buffer can be allocated. Second, the data is written into the
- * buffer.
- *
- * Determining the size:
- * The process of determining the size is triggered by a call to
- * g_variant_ensure_size() on a container. This invokes the
- * serialiser code to determine the size. The serialiser is passed
- * g_variant_fill_gvs() as a callback.
+ * Converting to serialised form:
*
- * g_variant_fill_gvs() is called by the serialiser on each child of
- * the container which, in turn, calls g_variant_ensure_size() on
- * itself and fills in the result of its own size calculation.
+ * The first step in the process of converting a GVariant to
+ * serialised form is to allocate a buffer. The size of the buffer is
+ * always known because we computed at construction time of the
+ * GVariant.
*
- * The serialiser uses the size information from the children to
- * calculate the size needed for the entire container.
- *
- * Writing the data:
* After the buffer has been allocated, g_variant_serialise() is
* called on the container. This invokes the serialiser code to write
- * the bytes to the container. The serialiser is, again, passed
+ * the bytes to the container. The serialiser is passed
* g_variant_fill_gvs() as a callback.
*
- * This time, when g_variant_fill_gvs() is called for each child, the
+ * At the time that g_variant_fill_gvs() is called for each child, the
* child is given a pointer to a sub-region of the allocated buffer
* where it should write its data. This is done by calling
* g_variant_store(). In the event that the instance is in serialised
@@ -315,34 +318,6 @@ g_variant_release_children (GVariant *value)
static void g_variant_fill_gvs (GVariantSerialised *, gpointer);
/* < private >
- * g_variant_ensure_size:
- * @value: a #GVariant
- *
- * Ensures that the ->size field of @value is filled in properly. This
- * must be done as a precursor to any serialisation of the value in
- * order to know how large of a buffer is needed to store the data.
- *
- * The current thread must hold the lock on @value.
- */
-static void
-g_variant_ensure_size (GVariant *value)
-{
- g_assert (value->state & STATE_LOCKED);
-
- if (value->size == (gssize) -1)
- {
- gpointer *children;
- gsize n_children;
-
- children = (gpointer *) value->contents.tree.children;
- n_children = value->contents.tree.n_children;
- value->size = g_variant_serialiser_needed_size (value->type_info,
- g_variant_fill_gvs,
- children, n_children);
- }
-}
-
-/* < private >
* g_variant_serialise:
* @value: a #GVariant
* @data: an appropriately-sized buffer
@@ -387,9 +362,12 @@ g_variant_serialise (GVariant *value,
*
* - reporting its type
*
- * - reporting its serialised size (requires knowing the size first)
+ * - reporting its serialised size
*
* - possibly storing its serialised form into the provided buffer
+ *
+ * This callback is also used during g_variant_new_from_children() in
+ * order to discover the size and type of each child.
*/
static void
g_variant_fill_gvs (GVariantSerialised *serialised,
@@ -397,10 +375,6 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
{
GVariant *value = data;
- g_variant_lock (value);
- g_variant_ensure_size (value);
- g_variant_unlock (value);
-
if (serialised->type_info == NULL)
serialised->type_info = value->type_info;
g_assert (serialised->type_info == value->type_info);
@@ -424,121 +398,205 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
*
* Ensures that @value is in serialised form.
*
- * If @value is in tree form then this function ensures that the
- * serialised size is known and then allocates a buffer of that size and
- * serialises the instance into the buffer. The 'children' array is
- * then released and the instance is set to serialised form based on the
- * contents of the buffer.
- *
- * The current thread must hold the lock on @value.
+ * If @value is in tree form then this function allocates a buffer of
+ * that size and serialises the instance into the buffer. The
+ * 'children' array is then released and the instance is set to
+ * serialised form based on the contents of the buffer.
*/
static void
g_variant_ensure_serialised (GVariant *value)
{
- g_assert (value->state & STATE_LOCKED);
-
- if (~value->state & STATE_SERIALISED)
+ if (g_variant_lock_in_tree_form (value))
{
- GBuffer *buffer;
+ GBytes *bytes;
gpointer data;
- g_variant_ensure_size (value);
data = g_malloc (value->size);
g_variant_serialise (value, data);
g_variant_release_children (value);
- buffer = g_buffer_new_take_data (data, value->size);
- value->contents.serialised.data = buffer->data;
- value->contents.serialised.buffer = buffer;
+ bytes = g_bytes_new_take (data, value->size);
+ value->contents.serialised.data = g_bytes_get_data (bytes, NULL);
+ value->contents.serialised.bytes = bytes;
value->state |= STATE_SERIALISED;
+
+ g_variant_unlock (value);
+ }
+}
+
+/* Now we have the code to recursively serialise a GVariant into a
+ * GVariantVectors structure.
+ *
+ * We want to do this in cases where the GVariant contains large chunks
+ * of serialised data in order to avoid having to copy this data.
+ *
+ * This generally works the same as normal serialising (co-recursion
+ * with the serialiser) but instead of using a callback we just hard-code
+ * the callback with the name g_variant_callback_write_to_vectors().
+ *
+ * This is a private API that will be used by GDBus.
+ */
+gsize
+g_variant_callback_write_to_vectors (GVariantVectors *vectors,
+ gpointer data,
+ GVariantTypeInfo **type_info)
+{
+ GVariant *value = data;
+
+ if (g_variant_lock_in_tree_form (value))
+ {
+ g_variant_serialiser_write_to_vectors (vectors, value->type_info, value->size,
+ (gpointer *) value->contents.tree.children,
+ value->contents.tree.n_children);
+
+ g_variant_unlock (value);
}
+ else
+ g_variant_vectors_append_gbytes (vectors, value->contents.serialised.bytes,
+ value->contents.serialised.data, value->size);
+
+ if (type_info)
+ *type_info = value->type_info;
+
+ return value->size;
+}
+
+/* < private >
+ * g_variant_serialise_to_vectors:
+ * @value: a #GVariant
+ * @vectors: (out): the result
+ *
+ * Serialises @value into @vectors.
+ *
+ * The caller must free @vectors.
+ */
+void
+g_variant_to_vectors (GVariant *value,
+ GVariantVectors *vectors)
+{
+ g_variant_vectors_init (vectors);
+
+ g_variant_callback_write_to_vectors (vectors, value, NULL);
}
/* < private >
* g_variant_alloc:
- * @type: the type of the new instance
+ * @type_info: (transfer full) the type info of the new instance
* @serialised: if the instance will be in serialised form
* @trusted: if the instance will be trusted
- * @returns: a new #GVariant with a floating reference
*
* Allocates a #GVariant instance and does some common work (such as
* looking up and filling in the type info), setting the state field,
* and setting the ref_count to 1.
+ *
+ * Returns: a new #GVariant with a floating reference
*/
static GVariant *
-g_variant_alloc (const GVariantType *type,
- gboolean serialised,
- gboolean trusted)
+g_variant_alloc (GVariantTypeInfo *type_info,
+ gboolean serialised,
+ gboolean trusted)
{
GVariant *value;
value = g_slice_new (GVariant);
- value->type_info = g_variant_type_info_get (type);
+ value->type_info = type_info;
value->state = (serialised ? STATE_SERIALISED : 0) |
(trusted ? STATE_TRUSTED : 0) |
STATE_FLOATING;
- value->size = (gssize) -1;
value->ref_count = 1;
return value;
}
/* -- internal -- */
+
/* < internal >
- * g_variant_new_from_buffer:
- * @type: a #GVariantType
- * @buffer: a #GBuffer
- * @trusted: if the contents of @buffer are trusted
- * @returns: a new #GVariant with a floating reference
+ * g_variant_new_from_children:
+ * @type_info: (transfer full) a #GVariantTypeInfo
+ * @children: an array of #GVariant pointers. Consumed.
+ * @n_children: the length of @children
+ * @trusted: %TRUE if every child in @children in trusted
*
- * Constructs a new serialised-mode #GVariant instance. This is the
- * inner interface for creation of new serialised values that gets
- * called from various functions in gvariant.c.
+ * Constructs a new tree-mode #GVariant instance. This is the inner
+ * interface for creation of new tree-mode values that gets called from
+ * various functions in gvariant.c.
*
- * A reference is taken on @buffer.
+ * @children is consumed by this function. g_free() will be called on
+ * it some time later.
+ *
+ * Returns: a new #GVariant with a floating reference
*/
GVariant *
-g_variant_new_from_buffer (const GVariantType *type,
- GBuffer *buffer,
- gboolean trusted)
+g_variant_new_from_children (GVariantTypeInfo *type_info,
+ GVariant **children,
+ gsize n_children,
+ gboolean trusted)
{
GVariant *value;
- value = g_variant_alloc (type, TRUE, trusted);
- value->contents.serialised.buffer = g_buffer_ref (buffer);
- value->contents.serialised.data = buffer->data;
- value->size = buffer->size;
+ value = g_variant_alloc (type_info, FALSE, trusted);
+ value->contents.tree.children = children;
+ value->contents.tree.n_children = n_children;
+ value->size = g_variant_serialiser_needed_size (value->type_info, g_variant_fill_gvs,
+ (gpointer *) children, n_children);
return value;
}
/* < internal >
- * g_variant_new_from_children:
- * @type: a #GVariantType
- * @children: an array of #GVariant pointers. Consumed.
- * @n_children: the length of @children
- * @trusted: %TRUE if every child in @children in trusted
- * @returns: a new #GVariant with a floating reference
- *
- * Constructs a new tree-mode #GVariant instance. This is the inner
+ * g_variant_new_serialised:
+ * @type_info: (transfer full): a #GVariantTypeInfo
+ * @bytes: (transfer full): the #GBytes holding @data
+ * @data: a pointer to the serialised data
+ * @size: the size of @data, in bytes
+ * @trusted: %TRUE if @data is trusted
+ *
+ * Constructs a new serialised #GVariant instance. This is the inner
* interface for creation of new serialised values that gets called from
* various functions in gvariant.c.
*
- * @children is consumed by this function. g_free() will be called on
- * it some time later.
+ * @bytes is consumed by this function. g_bytes_unref() will be called
+ * on it some time later.
+ *
+ * Returns: a new #GVariant with a floating reference
*/
GVariant *
-g_variant_new_from_children (const GVariantType *type,
- GVariant **children,
- gsize n_children,
- gboolean trusted)
+g_variant_new_serialised (GVariantTypeInfo *type_info,
+ GBytes *bytes,
+ gconstpointer data,
+ gsize size,
+ gboolean trusted)
{
GVariant *value;
+ gsize fixed_size;
- value = g_variant_alloc (type, FALSE, trusted);
- value->contents.tree.children = children;
- value->contents.tree.n_children = n_children;
+ value = g_variant_alloc (type_info, TRUE, trusted);
+ value->contents.serialised.bytes = bytes;
+ value->contents.serialised.data = data;
+ value->size = size;
+
+ g_variant_type_info_query (value->type_info, NULL, &fixed_size);
+ if G_UNLIKELY (fixed_size && size != fixed_size)
+ {
+ /* Creating a fixed-sized GVariant with a bytes of the wrong
+ * size.
+ *
+ * We should do the equivalent of pulling a fixed-sized child out
+ * of a broken container (ie: data is NULL size is equal to the correct
+ * fixed size).
+ *
+ * This really ought not to happen if the data is trusted...
+ */
+ if (trusted)
+ g_error ("Attempting to create a trusted GVariant instance out of invalid data");
+
+ /* We hang on to the GBytes (even though we don't use it anymore)
+ * because every GVariant must have a GBytes.
+ */
+ value->contents.serialised.data = NULL;
+ value->size = fixed_size;
+ }
return value;
}
@@ -546,11 +604,12 @@ g_variant_new_from_children (const GVariantType *type,
/* < internal >
* g_variant_get_type_info:
* @value: a #GVariant
- * @returns: the #GVariantTypeInfo for @value
*
* Returns the #GVariantTypeInfo corresponding to the type of @value. A
* reference is not added, so the return value is only good for the
* duration of the life of @value.
+ *
+ * Returns: the #GVariantTypeInfo for @value
*/
GVariantTypeInfo *
g_variant_get_type_info (GVariant *value)
@@ -561,7 +620,6 @@ g_variant_get_type_info (GVariant *value)
/* < internal >
* g_variant_is_trusted:
* @value: a #GVariant
- * @returns: if @value is trusted
*
* Determines if @value is trusted by #GVariant to contain only
* fully-valid data. All values constructed solely via #GVariant APIs
@@ -572,6 +630,8 @@ g_variant_get_type_info (GVariant *value)
* skipped. For example, we don't need to check that a string is
* properly nul-terminated or that an object path is actually a
* properly-formatted object path.
+ *
+ * Returns: if @value is trusted
*/
gboolean
g_variant_is_trusted (GVariant *value)
@@ -579,6 +639,191 @@ g_variant_is_trusted (GVariant *value)
return (value->state & STATE_TRUSTED) != 0;
}
+/* < internal >
+ * g_variant_get_serialised:
+ * @value: a #GVariant
+ * @bytes: (out) (transfer none): a location to store the #GBytes
+ * @size: (out): a location to store the size of the returned data
+ *
+ * Ensures that @value is in serialised form and returns information
+ * about it. This is called from various APIs in gvariant.c
+ *
+ * Returns: data, of length @size
+ */
+gconstpointer
+g_variant_get_serialised (GVariant *value,
+ GBytes **bytes,
+ gsize *size)
+{
+ g_variant_ensure_serialised (value);
+
+ if (bytes)
+ *bytes = value->contents.serialised.bytes;
+
+ *size = value->size;
+
+ return value->contents.serialised.data;
+}
+
+static GVariant *
+g_variant_vector_deserialise (GVariantTypeInfo *type_info,
+ GVariantVector *first_vector,
+ GVariantVector *last_vector,
+ gsize size,
+ gboolean trusted,
+ GArray *unpacked_children)
+{
+ g_assert (size > 0);
+
+ if (first_vector < last_vector)
+ {
+ GVariantVector *vector = first_vector;
+ const guchar *end_pointer;
+ GVariant **children;
+ guint save_point;
+ guint n_children;
+ gboolean failed;
+ guint i;
+
+ end_pointer = last_vector->data.pointer + last_vector->size;
+ save_point = unpacked_children->len;
+
+ if (!g_variant_serialiser_unpack_all (type_info, end_pointer, last_vector->size, size, unpacked_children))
+ {
+ for (i = save_point; i < unpacked_children->len; i++)
+ g_variant_type_info_unref (g_array_index (unpacked_children, GVariantUnpacked, i).type_info);
+ g_array_set_size (unpacked_children, save_point);
+
+ g_variant_type_info_unref (type_info);
+
+ return NULL;
+ }
+
+ n_children = unpacked_children->len - save_point;
+ children = g_new (GVariant *, n_children);
+ failed = FALSE;
+
+ for (i = 0; i < n_children; i++)
+ {
+ GVariantUnpacked *unpacked = &g_array_index (unpacked_children, GVariantUnpacked, save_point + i);
+ const guchar *resume_at_data;
+ gsize resume_at_size;
+ GVariantVector *fv;
+
+ /* Skip the alignment.
+ *
+ * We can destroy vectors because we won't be going back.
+ *
+ * We do a >= compare because we want to go to the next vector
+ * if it is the start of our child.
+ */
+ while (unpacked->skip >= vector->size)
+ {
+ unpacked->skip -= vector->size;
+ vector++;
+ }
+ g_assert (vector <= last_vector);
+
+ fv = vector;
+ fv->data.pointer += unpacked->skip;
+ fv->size -= unpacked->skip;
+
+ if (unpacked->size == 0)
+ {
+ children[i] = g_variant_new_serialised (unpacked->type_info, g_bytes_new (NULL, 0), NULL, 0, trusted);
+ g_variant_ref_sink (children[i]);
+ continue;
+ }
+
+ /* Now skip to the end, according to 'size'.
+ *
+ * We cannot destroy everything here because we will probably
+ * end up reusing the last one.
+ *
+ * We do a > compare because we want to stay on this vector if
+ * it is the end of our child.
+ */
+ size = unpacked->size;
+ while (unpacked->size > vector->size)
+ {
+ unpacked->size -= vector->size;
+ vector++;
+ }
+ g_assert (vector <= last_vector);
+
+ /* We have to modify the vectors for the benefit of the
+ * recursive step. We also have to remember where we left
+ * off, keeping in mind that the recursive step may itself
+ * modify the vectors.
+ */
+ resume_at_data = vector->data.pointer + unpacked->size;
+ resume_at_size = vector->size - unpacked->size;
+ vector->size = unpacked->size;
+
+ children[i] = g_variant_vector_deserialise (unpacked->type_info, fv, vector,
+ size, trusted, unpacked_children);
+
+ vector->data.pointer = resume_at_data;
+ vector->size = resume_at_size;
+
+ if (children[i])
+ g_variant_ref_sink (children[i]);
+ else
+ failed = TRUE;
+ }
+
+ /* We consumed all the type infos */
+ g_array_set_size (unpacked_children, save_point);
+
+ if G_UNLIKELY (failed)
+ {
+ for (i = 0; i < n_children; i++)
+ if (children[i])
+ g_variant_unref (children[i]);
+
+ g_variant_type_info_unref (type_info);
+ g_free (children);
+
+ return NULL;
+ }
+
+ return g_variant_new_from_children (type_info, children, n_children, trusted);
+ }
+ else
+ {
+ g_assert (first_vector == last_vector);
+ g_assert (size == first_vector->size);
+
+ return g_variant_new_serialised (type_info, g_bytes_ref (first_vector->gbytes),
+ first_vector->data.pointer, size, trusted);
+ }
+}
+
+GVariant *
+g_variant_from_vectors (const GVariantType *type,
+ GVariantVector *vectors,
+ gsize n_vectors,
+ gsize size,
+ gboolean trusted)
+{
+ GVariant *result;
+ GArray *tmp;
+
+ g_return_val_if_fail (vectors != NULL || n_vectors == 0, NULL);
+
+ if (size == 0)
+ return g_variant_new_serialised (g_variant_type_info_get (type), g_bytes_new (NULL, 0), NULL, 0, trusted);
+
+ tmp = g_array_new (FALSE, FALSE, sizeof (GVariantUnpacked));
+ result = g_variant_vector_deserialise (g_variant_type_info_get (type),
+ vectors, vectors + n_vectors - 1, size, trusted, tmp);
+ if (result)
+ g_variant_ref_sink (result);
+ g_array_free (tmp, TRUE);
+
+ return result;
+}
+
/* -- public -- */
/**
@@ -593,6 +838,9 @@ g_variant_is_trusted (GVariant *value)
void
g_variant_unref (GVariant *value)
{
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (value->ref_count > 0);
+
if (g_atomic_int_dec_and_test (&value->ref_count))
{
if G_UNLIKELY (value->state & STATE_LOCKED)
@@ -604,10 +852,11 @@ g_variant_unref (GVariant *value)
g_variant_type_info_unref (value->type_info);
if (value->state & STATE_SERIALISED)
- g_buffer_unref (value->contents.serialised.buffer);
+ g_bytes_unref (value->contents.serialised.bytes);
else
g_variant_release_children (value);
+ memset (value, 0, sizeof (GVariant));
g_slice_free (GVariant, value);
}
}
@@ -615,15 +864,19 @@ g_variant_unref (GVariant *value)
/**
* g_variant_ref:
* @value: a #GVariant
- * @returns: the same @value
*
* Increases the reference count of @value.
*
+ * Returns: the same @value
+ *
* Since: 2.24
**/
GVariant *
g_variant_ref (GVariant *value)
{
+ g_return_val_if_fail (value != NULL, NULL);
+ g_return_val_if_fail (value->ref_count > 0, NULL);
+
g_atomic_int_inc (&value->ref_count);
return value;
@@ -632,10 +885,9 @@ g_variant_ref (GVariant *value)
/**
* g_variant_ref_sink:
* @value: a #GVariant
- * @returns: the same @value
*
* #GVariant uses a floating reference count system. All functions with
- * names starting with g_variant_new_ return floating
+ * names starting with `g_variant_new_` return floating
* references.
*
* Calling g_variant_ref_sink() on a #GVariant with a floating reference
@@ -657,11 +909,16 @@ g_variant_ref (GVariant *value)
* maintaining normal refcounting semantics in situations where values
* are not floating.
*
+ * Returns: the same @value
+ *
* Since: 2.24
**/
GVariant *
g_variant_ref_sink (GVariant *value)
{
+ g_return_val_if_fail (value != NULL, NULL);
+ g_return_val_if_fail (value->ref_count > 0, NULL);
+
g_variant_lock (value);
if (~value->state & STATE_FLOATING)
@@ -675,9 +932,84 @@ g_variant_ref_sink (GVariant *value)
}
/**
+ * g_variant_take_ref:
+ * @value: a #GVariant
+ *
+ * If @value is floating, sink it. Otherwise, do nothing.
+ *
+ * Typically you want to use g_variant_ref_sink() in order to
+ * automatically do the correct thing with respect to floating or
+ * non-floating references, but there is one specific scenario where
+ * this function is helpful.
+ *
+ * The situation where this function is helpful is when creating an API
+ * that allows the user to provide a callback function that returns a
+ * #GVariant. We certainly want to allow the user the flexibility to
+ * return a non-floating reference from this callback (for the case
+ * where the value that is being returned already exists).
+ *
+ * At the same time, the style of the #GVariant API makes it likely that
+ * for newly-created #GVariant instances, the user can be saved some
+ * typing if they are allowed to return a #GVariant with a floating
+ * reference.
+ *
+ * Using this function on the return value of the user's callback allows
+ * the user to do whichever is more convenient for them. The caller
+ * will alway receives exactly one full reference to the value: either
+ * the one that was returned in the first place, or a floating reference
+ * that has been converted to a full reference.
+ *
+ * This function has an odd interaction when combined with
+ * g_variant_ref_sink() running at the same time in another thread on
+ * the same #GVariant instance. If g_variant_ref_sink() runs first then
+ * the result will be that the floating reference is converted to a hard
+ * reference. If g_variant_take_ref() runs first then the result will
+ * be that the floating reference is converted to a hard reference and
+ * an additional reference on top of that one is added. It is best to
+ * avoid this situation.
+ *
+ * Returns: the same @value
+ **/
+GVariant *
+g_variant_take_ref (GVariant *value)
+{
+ g_return_val_if_fail (value != NULL, NULL);
+ g_return_val_if_fail (value->ref_count > 0, NULL);
+
+ g_atomic_int_and (&value->state, ~STATE_FLOATING);
+
+ return value;
+}
+
+/**
+ * g_variant_is_floating:
+ * @value: a #GVariant
+ *
+ * Checks whether @value has a floating reference count.
+ *
+ * This function should only ever be used to assert that a given variant
+ * is or is not floating, or for debug purposes. To acquire a reference
+ * to a variant that might be floating, always use g_variant_ref_sink()
+ * or g_variant_take_ref().
+ *
+ * See g_variant_ref_sink() for more information about floating reference
+ * counts.
+ *
+ * Returns: whether @value is floating
+ *
+ * Since: 2.26
+ **/
+gboolean
+g_variant_is_floating (GVariant *value)
+{
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ return (value->state & STATE_FLOATING) != 0;
+}
+
+/**
* g_variant_get_size:
* @value: a #GVariant instance
- * @returns: the serialised size of @value
*
* Determines the number of bytes that would be required to store @value
* with g_variant_store().
@@ -691,22 +1023,19 @@ g_variant_ref_sink (GVariant *value)
* operation which is approximately O(n) in the number of values
* involved.
*
+ * Returns: the serialised size of @value
+ *
* Since: 2.24
**/
gsize
g_variant_get_size (GVariant *value)
{
- g_variant_lock (value);
- g_variant_ensure_size (value);
- g_variant_unlock (value);
-
return value->size;
}
/**
* g_variant_get_data:
* @value: a #GVariant instance
- * @returns: the serialised form of @value, or %NULL
*
* Returns a pointer to the serialised form of a #GVariant instance.
* The returned data may not be in fully-normalised form if read from an
@@ -724,22 +1053,32 @@ g_variant_get_size (GVariant *value)
* serialisation occurs implicitly and is approximately O(n) in the size
* of the result.
*
+ * To deserialise the data returned by this function, in addition to the
+ * serialised data, you must know the type of the #GVariant, and (if the
+ * machine might be different) the endianness of the machine that stored
+ * it. As a result, file formats or network messages that incorporate
+ * serialised #GVariants must include this information either
+ * implicitly (for instance "the file always contains a
+ * %G_VARIANT_TYPE_VARIANT and it is always in little-endian order") or
+ * explicitly (by storing the type and/or endianness in addition to the
+ * serialised data).
+ *
+ * Returns: (transfer none): the serialised form of @value, or %NULL
+ *
* Since: 2.24
**/
gconstpointer
g_variant_get_data (GVariant *value)
{
- g_variant_lock (value);
g_variant_ensure_serialised (value);
- g_variant_unlock (value);
return value->contents.serialised.data;
}
+
/**
* g_variant_n_children:
* @value: a container #GVariant
- * @returns: the number of children in the container
*
* Determines the number of children in a container #GVariant instance.
* This includes variants, maybes, arrays, tuples and dictionary
@@ -753,6 +1092,8 @@ g_variant_get_data (GVariant *value)
*
* This function is O(1).
*
+ * Returns: the number of children in the container
+ *
* Since: 2.24
**/
gsize
@@ -760,9 +1101,12 @@ g_variant_n_children (GVariant *value)
{
gsize n_children;
- g_variant_lock (value);
-
- if (value->state & STATE_SERIALISED)
+ if (g_variant_lock_in_tree_form (value))
+ {
+ n_children = value->contents.tree.n_children;
+ g_variant_unlock (value);
+ }
+ else
{
GVariantSerialised serialised = {
value->type_info,
@@ -772,10 +1116,6 @@ g_variant_n_children (GVariant *value)
n_children = g_variant_serialised_n_children (serialised);
}
- else
- n_children = value->contents.tree.n_children;
-
- g_variant_unlock (value);
return n_children;
}
@@ -784,7 +1124,6 @@ g_variant_n_children (GVariant *value)
* g_variant_get_child_value:
* @value: a container #GVariant
* @index_: the index of the child to fetch
- * @returns: the child at the specified index
*
* Reads a child item out of a container #GVariant instance. This
* includes variants, maybes, arrays, tuples and dictionary
@@ -794,19 +1133,30 @@ g_variant_n_children (GVariant *value)
* It is an error if @index_ is greater than the number of child items
* in the container. See g_variant_n_children().
*
+ * The returned value is never floating. You should free it with
+ * g_variant_unref() when you're done with it.
+ *
* This function is O(1).
*
+ * Returns: (transfer full): the child at the specified index
+ *
* Since: 2.24
**/
GVariant *
g_variant_get_child_value (GVariant *value,
gsize index_)
{
- GVariant *child = NULL;
+ GVariant *child;
- g_variant_lock (value);
+ g_return_val_if_fail (index_ < g_variant_n_children (value), NULL);
- if (value->state & STATE_SERIALISED)
+ if (g_variant_lock_in_tree_form (value))
+ {
+
+ child = g_variant_ref (value->contents.tree.children[index_]);
+ g_variant_unlock (value);
+ }
+ else
{
GVariantSerialised serialised = {
value->type_info,
@@ -821,20 +1171,12 @@ g_variant_get_child_value (GVariant *value,
s_child = g_variant_serialised_get_child (serialised, index_);
/* create a new serialised instance out of it */
- child = g_slice_new (GVariant);
- child->type_info = s_child.type_info;
- child->state = (value->state & STATE_TRUSTED) |
- STATE_SERIALISED;
- child->size = s_child.size;
- child->ref_count = 1;
- child->contents.serialised.buffer =
- g_buffer_ref (value->contents.serialised.buffer);
- child->contents.serialised.data = s_child.data;
- }
- else
- child = g_variant_ref (value->contents.tree.children[index_]);
-
- g_variant_unlock (value);
+ child = g_variant_new_serialised (s_child.type_info,
+ g_bytes_ref (value->contents.serialised.bytes),
+ s_child.data, s_child.size,
+ value->state & STATE_TRUSTED);
+ child->state &= ~STATE_FLOATING;
+ }
return child;
}
@@ -849,7 +1191,11 @@ g_variant_get_child_value (GVariant *value,
*
* The stored data is in machine native byte order but may not be in
* fully-normalised form if read from an untrusted source. See
- * g_variant_normalise() for a solution.
+ * g_variant_get_normal_form() for a solution.
+ *
+ * As with g_variant_get_data(), to be able to deserialise the
+ * serialised variant successfully, its type and (if the destination
+ * machine might be different) its endianness must also be available.
*
* This function is approximately O(n) in the size of @data.
*
@@ -859,45 +1205,49 @@ void
g_variant_store (GVariant *value,
gpointer data)
{
- g_variant_lock (value);
-
- if (value->state & STATE_SERIALISED)
+ if (g_variant_lock_in_tree_form (value))
+ {
+ g_variant_serialise (value, data);
+ g_variant_unlock (value);
+ }
+ else
{
if (value->contents.serialised.data != NULL)
memcpy (data, value->contents.serialised.data, value->size);
else
memset (data, 0, value->size);
}
- else
- g_variant_serialise (value, data);
-
- g_variant_unlock (value);
}
/**
* g_variant_is_normal_form:
* @value: a #GVariant instance
- * @returns: %TRUE if @value is in normal form
*
* Checks if @value is in normal form.
*
* The main reason to do this is to detect if a given chunk of
* serialised data is in normal form: load the data into a #GVariant
- * using g_variant_create_from_data() and then use this function to
+ * using g_variant_new_from_data() and then use this function to
* check.
*
* If @value is found to be in normal form then it will be marked as
* being trusted. If the value was already marked as being trusted then
* this function will immediately return %TRUE.
*
+ * Returns: %TRUE if @value is in normal form
+ *
* Since: 2.24
**/
gboolean
g_variant_is_normal_form (GVariant *value)
{
- if (value->state & STATE_TRUSTED)
+ if (g_atomic_int_get (&value->state) & STATE_TRUSTED)
return TRUE;
+ /* We always take the lock here because we expect to find that the
+ * value is in normal form and in that case, we need to update the
+ * state, which requires holding the lock.
+ */
g_variant_lock (value);
if (value->state & STATE_SERIALISED)
@@ -927,6 +1277,3 @@ g_variant_is_normal_form (GVariant *value)
return (value->state & STATE_TRUSTED) != 0;
}
-
-#define __G_VARIANT_CORE_C__
-#include "galiasdef.c"