X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=glib%2Fgvariant-core.c;h=45b876bc51fc9cac42536f69c37a44a41e9ff5b1;hb=13e15733f38a40c6ef6a1baede91cce81c86ebaa;hp=fdbe0341b225374acd4a64954d052822209239f3;hpb=22da18fa706c685da015c9b9a786d810270b3a5f;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c index fdbe034..45b876b 100644 --- a/glib/gvariant-core.c +++ b/glib/gvariant-core.c @@ -13,12 +13,13 @@ * 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 @@ -106,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 @@ -259,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 @@ -276,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. - * - * 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. + * Converting to serialised form: * - * The serialiser uses the size information from the children to - * calculate the size needed for the entire container. + * 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. * - * 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 @@ -314,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 @@ -386,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, @@ -396,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); @@ -423,37 +398,88 @@ 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)) { GBytes *bytes; gpointer data; - g_variant_ensure_size (value); data = g_malloc (value->size); g_variant_serialise (value, data); g_variant_release_children (value); bytes = g_bytes_new_take (data, value->size); - value->contents.serialised.data = g_bytes_get_data (bytes); + 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 @@ -478,14 +504,12 @@ g_variant_alloc (const GVariantType *type, 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_bytes: * @type: a #GVariantType * @bytes: a #GBytes @@ -497,7 +521,9 @@ g_variant_alloc (const GVariantType *type, * * A reference is taken on @bytes. * - * Returns: a new #GVariant with a floating reference + * Returns: (transfer none): a new #GVariant with a floating reference + * + * Since: 2.36 */ GVariant * g_variant_new_from_bytes (const GVariantType *type, @@ -529,13 +555,14 @@ g_variant_new_from_bytes (const GVariantType *type, } else { - value->contents.serialised.data = g_bytes_get_data (bytes); - value->size = g_bytes_get_size (bytes); + value->contents.serialised.data = g_bytes_get_data (bytes, &value->size); } return value; } +/* -- internal -- */ + /* < internal > * g_variant_new_from_children: * @type: a #GVariantType @@ -563,6 +590,8 @@ g_variant_new_from_children (const GVariantType *type, value = g_variant_alloc (type, 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; } @@ -668,7 +697,7 @@ g_variant_ref (GVariant *value) * @value: a #GVariant * * #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 @@ -811,10 +840,6 @@ g_variant_is_floating (GVariant *value) gsize g_variant_get_size (GVariant *value) { - g_variant_lock (value); - g_variant_ensure_size (value); - g_variant_unlock (value); - return value->size; } @@ -842,7 +867,7 @@ g_variant_get_size (GVariant *value) * 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 + * 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 @@ -855,14 +880,47 @@ g_variant_get_size (GVariant *value) 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_get_data_as_bytes: + * @value: a #GVariant + * + * Returns a pointer to the serialised form of a #GVariant instance. + * The semantics of this function are exactly the same as + * g_variant_get_data(), except that the returned #GBytes holds + * a reference to the variant data. + * + * Returns: (transfer full): A new #GBytes representing the variant data + * + * Since: 2.36 + */ +GBytes * +g_variant_get_data_as_bytes (GVariant *value) +{ + const gchar *bytes_data; + const gchar *data; + gsize bytes_size; + gsize size; + + g_variant_ensure_serialised (value); + + bytes_data = g_bytes_get_data (value->contents.serialised.bytes, &bytes_size); + data = value->contents.serialised.data; + size = value->size; + + if (data == bytes_data && size == bytes_size) + return g_bytes_ref (value->contents.serialised.bytes); + else + return g_bytes_new_from_bytes (value->contents.serialised.bytes, + data - bytes_data, size); +} + + +/** * g_variant_n_children: * @value: a container #GVariant * @@ -887,9 +945,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, @@ -899,10 +960,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; } @@ -933,52 +990,43 @@ GVariant * g_variant_get_child_value (GVariant *value, gsize index_) { + GVariant *child; + g_return_val_if_fail (index_ < g_variant_n_children (value), NULL); - if (~g_atomic_int_get (&value->state) & STATE_SERIALISED) + if (g_variant_lock_in_tree_form (value)) { - g_variant_lock (value); - - if (~value->state & STATE_SERIALISED) - { - GVariant *child; - - child = g_variant_ref (value->contents.tree.children[index_]); - g_variant_unlock (value); - - return child; - } + child = g_variant_ref (value->contents.tree.children[index_]); g_variant_unlock (value); } + else + { + GVariantSerialised serialised = { + value->type_info, + (gpointer) value->contents.serialised.data, + value->size + }; + GVariantSerialised s_child; - { - GVariantSerialised serialised = { - value->type_info, - (gpointer) value->contents.serialised.data, - value->size - }; - GVariantSerialised s_child; - GVariant *child; - - /* get the serialiser to extract the serialised data for the child - * from the serialised data for the container - */ - 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.bytes = - g_bytes_ref (value->contents.serialised.bytes); - child->contents.serialised.data = s_child.data; - - return child; - } + /* get the serialiser to extract the serialised data for the child + * from the serialised data for the container + */ + 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.bytes = + g_bytes_ref (value->contents.serialised.bytes); + child->contents.serialised.data = s_child.data; + } + + return child; } /** @@ -1005,19 +1053,18 @@ 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); } /** @@ -1042,9 +1089,13 @@ g_variant_store (GVariant *value, 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)