* 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 <http://www.gnu.org/licenses/>.
*/
+#include "config.h"
+
#include <glib/gvariant-core.h>
+#include "glib-private.h"
#include <glib/gvariant-serialiser.h>
#include <glib/gtestutils.h>
* 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
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
* 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
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
*
* - 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,
{
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);
*
* 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);
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
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
*
* 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,
return value;
}
+/* -- internal -- */
+
/* < internal >
* g_variant_new_from_children:
* @type: a #GVariantType
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;
}
* @value: a #GVariant
*
* #GVariant uses a floating reference count system. All functions with
- * names starting with <literal>g_variant_new_</literal> return floating
+ * names starting with `g_variant_new_` return floating
* references.
*
* Calling g_variant_ref_sink() on a #GVariant with a floating reference
gsize
g_variant_get_size (GVariant *value)
{
- g_variant_lock (value);
- g_variant_ensure_size (value);
- g_variant_unlock (value);
-
return value->size;
}
* 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 #GVariant<!---->s 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
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
*
{
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,
n_children = g_variant_serialised_n_children (serialised);
}
- else
- n_children = value->contents.tree.n_children;
-
- g_variant_unlock (value);
return n_children;
}
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;
}
/**
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);
}
/**
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)