/* * Copyright © 2007, 2008 Ryan Lortie * Copyright © 2010 Codethink Limited * * SPDX-License-Identifier: LGPL-2.1-or-later * * 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.1 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, see . * * Author: Ryan Lortie */ /* Prologue {{{1 */ #include "config.h" #include #include "gvariant-internal.h" #include #include #include #include #include #include #include /** * GVariant: * * `GVariant` is a variant datatype; it can contain one or more values * along with information about the type of the values. * * A `GVariant` may contain simple types, like an integer, or a boolean value; * or complex types, like an array of two strings, or a dictionary of key * value pairs. A `GVariant` is also immutable: once it’s been created neither * its type nor its content can be modified further. * * `GVariant` is useful whenever data needs to be serialized, for example when * sending method parameters in D-Bus, or when saving settings using * [`GSettings`](../gio/class.Settings.html). * * When creating a new `GVariant`, you pass the data you want to store in it * along with a string representing the type of data you wish to pass to it. * * For instance, if you want to create a `GVariant` holding an integer value you * can use: * * ```c * GVariant *v = g_variant_new ("u", 40); * ``` * * The string `u` in the first argument tells `GVariant` that the data passed to * the constructor (`40`) is going to be an unsigned integer. * * More advanced examples of `GVariant` in use can be found in documentation for * [`GVariant` format strings](gvariant-format-strings.html#pointers). * * The range of possible values is determined by the type. * * The type system used by `GVariant` is [type@GLib.VariantType]. * * `GVariant` instances always have a type and a value (which are given * at construction time). The type and value of a `GVariant` instance * can never change other than by the `GVariant` itself being * destroyed. A `GVariant` cannot contain a pointer. * * `GVariant` is reference counted using [method@GLib.Variant.ref] and * [method@GLib.Variant.unref]. `GVariant` also has floating reference counts — * see [method@GLib.Variant.ref_sink]. * * `GVariant` is completely threadsafe. A `GVariant` instance can be * concurrently accessed in any way from any number of threads without * problems. * * `GVariant` is heavily optimised for dealing with data in serialized * form. It works particularly well with data located in memory-mapped * files. It can perform nearly all deserialization operations in a * small constant time, usually touching only a single memory page. * Serialized `GVariant` data can also be sent over the network. * * `GVariant` is largely compatible with D-Bus. Almost all types of * `GVariant` instances can be sent over D-Bus. See [type@GLib.VariantType] for * exceptions. (However, `GVariant`’s serialization format is not the same * as the serialization format of a D-Bus message body: use * [GDBusMessage](../gio/class.DBusMessage.html), in the GIO library, for those.) * * For space-efficiency, the `GVariant` serialization format does not * automatically include the variant’s length, type or endianness, * which must either be implied from context (such as knowledge that a * particular file format always contains a little-endian * `G_VARIANT_TYPE_VARIANT` which occupies the whole length of the file) * or supplied out-of-band (for instance, a length, type and/or endianness * indicator could be placed at the beginning of a file, network message * or network stream). * * A `GVariant`’s size is limited mainly by any lower level operating * system constraints, such as the number of bits in `gsize`. For * example, it is reasonable to have a 2GB file mapped into memory * with [struct@GLib.MappedFile], and call [ctor@GLib.Variant.new_from_data] on * it. * * For convenience to C programmers, `GVariant` features powerful * varargs-based value construction and destruction. This feature is * designed to be embedded in other libraries. * * There is a Python-inspired text language for describing `GVariant` * values. `GVariant` includes a printer for this language and a parser * with type inferencing. * * ## Memory Use * * `GVariant` tries to be quite efficient with respect to memory use. * This section gives a rough idea of how much memory is used by the * current implementation. The information here is subject to change * in the future. * * The memory allocated by `GVariant` can be grouped into 4 broad * purposes: memory for serialized data, memory for the type * information cache, buffer management memory and memory for the * `GVariant` structure itself. * * ## Serialized Data Memory * * This is the memory that is used for storing `GVariant` data in * serialized form. This is what would be sent over the network or * what would end up on disk, not counting any indicator of the * endianness, or of the length or type of the top-level variant. * * The amount of memory required to store a boolean is 1 byte. 16, * 32 and 64 bit integers and double precision floating point numbers * use their ‘natural’ size. Strings (including object path and * signature strings) are stored with a nul terminator, and as such * use the length of the string plus 1 byte. * * ‘Maybe’ types use no space at all to represent the null value and * use the same amount of space (sometimes plus one byte) as the * equivalent non-maybe-typed value to represent the non-null case. * * Arrays use the amount of space required to store each of their * members, concatenated. Additionally, if the items stored in an * array are not of a fixed-size (ie: strings, other arrays, etc) * then an additional framing offset is stored for each item. The * size of this offset is either 1, 2 or 4 bytes depending on the * overall size of the container. Additionally, extra padding bytes * are added as required for alignment of child values. * * Tuples (including dictionary entries) use the amount of space * required to store each of their members, concatenated, plus one * framing offset (as per arrays) for each non-fixed-sized item in * the tuple, except for the last one. Additionally, extra padding * bytes are added as required for alignment of child values. * * Variants use the same amount of space as the item inside of the * variant, plus 1 byte, plus the length of the type string for the * item inside the variant. * * As an example, consider a dictionary mapping strings to variants. * In the case that the dictionary is empty, 0 bytes are required for * the serialization. * * If we add an item ‘width’ that maps to the int32 value of 500 then * we will use 4 bytes to store the int32 (so 6 for the variant * containing it) and 6 bytes for the string. The variant must be * aligned to 8 after the 6 bytes of the string, so that’s 2 extra * bytes. 6 (string) + 2 (padding) + 6 (variant) is 14 bytes used * for the dictionary entry. An additional 1 byte is added to the * array as a framing offset making a total of 15 bytes. * * If we add another entry, ‘title’ that maps to a nullable string * that happens to have a value of null, then we use 0 bytes for the * null value (and 3 bytes for the variant to contain it along with * its type string) plus 6 bytes for the string. Again, we need 2 * padding bytes. That makes a total of 6 + 2 + 3 = 11 bytes. * * We now require extra padding between the two items in the array. * After the 14 bytes of the first item, that’s 2 bytes required. * We now require 2 framing offsets for an extra two * bytes. 14 + 2 + 11 + 2 = 29 bytes to encode the entire two-item * dictionary. * * ## Type Information Cache * * For each `GVariant` type that currently exists in the program a type * information structure is kept in the type information cache. The * type information structure is required for rapid deserialization. * * Continuing with the above example, if a `GVariant` exists with the * type `a{sv}` then a type information struct will exist for * `a{sv}`, `{sv}`, `s`, and `v`. Multiple uses of the same type * will share the same type information. Additionally, all * single-digit types are stored in read-only static memory and do * not contribute to the writable memory footprint of a program using * `GVariant`. * * Aside from the type information structures stored in read-only * memory, there are two forms of type information. One is used for * container types where there is a single element type: arrays and * maybe types. The other is used for container types where there * are multiple element types: tuples and dictionary entries. * * Array type info structures are `6 * sizeof (void *)`, plus the * memory required to store the type string itself. This means that * on 32-bit systems, the cache entry for `a{sv}` would require 30 * bytes of memory (plus allocation overhead). * * Tuple type info structures are `6 * sizeof (void *)`, plus `4 * * sizeof (void *)` for each item in the tuple, plus the memory * required to store the type string itself. A 2-item tuple, for * example, would have a type information structure that consumed * writable memory in the size of `14 * sizeof (void *)` (plus type * string) This means that on 32-bit systems, the cache entry for * `{sv}` would require 61 bytes of memory (plus allocation overhead). * * This means that in total, for our `a{sv}` example, 91 bytes of * type information would be allocated. * * The type information cache, additionally, uses a [struct@GLib.HashTable] to * store and look up the cached items and stores a pointer to this * hash table in static storage. The hash table is freed when there * are zero items in the type cache. * * Although these sizes may seem large it is important to remember * that a program will probably only have a very small number of * different types of values in it and that only one type information * structure is required for many different values of the same type. * * ## Buffer Management Memory * * `GVariant` uses an internal buffer management structure to deal * with the various different possible sources of serialized data * that it uses. The buffer is responsible for ensuring that the * correct call is made when the data is no longer in use by * `GVariant`. This may involve a [func@GLib.free] or * even [method@GLib.MappedFile.unref]. * * One buffer management structure is used for each chunk of * serialized data. The size of the buffer management structure * is `4 * (void *)`. On 32-bit systems, that’s 16 bytes. * * ## GVariant structure * * The size of a `GVariant` structure is `6 * (void *)`. On 32-bit * systems, that’s 24 bytes. * * `GVariant` structures only exist if they are explicitly created * with API calls. For example, if a `GVariant` is constructed out of * serialized data for the example given above (with the dictionary) * then although there are 9 individual values that comprise the * entire dictionary (two keys, two values, two variants containing * the values, two dictionary entries, plus the dictionary itself), * only 1 `GVariant` instance exists — the one referring to the * dictionary. * * If calls are made to start accessing the other values then * `GVariant` instances will exist for those values only for as long * as they are in use (ie: until you call [method@GLib.Variant.unref]). The * type information is shared. The serialized data and the buffer * management structure for that serialized data is shared by the * child. * * ## Summary * * To put the entire example together, for our dictionary mapping * strings to variants (with two entries, as given above), we are * using 91 bytes of memory for type information, 29 bytes of memory * for the serialized data, 16 bytes for buffer management and 24 * bytes for the `GVariant` instance, or a total of 160 bytes, plus * allocation overhead. If we were to use [method@GLib.Variant.get_child_value] * to access the two dictionary entries, we would use an additional 48 * bytes. If we were to have other dictionaries of the same type, we * would use more memory for the serialized data and buffer * management for those dictionaries, but the type information would * be shared. * * Since: 2.24 */ /* definition of GVariant structure is in gvariant-core.c */ /* this is a g_return_val_if_fail() for making * sure a (GVariant *) has the required type. */ #define TYPE_CHECK(value, TYPE, val) \ if G_UNLIKELY (!g_variant_is_of_type (value, TYPE)) { \ g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, \ "g_variant_is_of_type (" #value \ ", " #TYPE ")"); \ return val; \ } /* Numeric Type Constructor/Getters {{{1 */ /* < private > * g_variant_new_from_trusted: * @type: the #GVariantType * @data: the data to use * @size: the size of @data * * Constructs a new trusted #GVariant instance from the provided data. * This is used to implement g_variant_new_* for all the basic types. * * Note: @data must be backed by memory that is aligned appropriately for the * @type being loaded. Otherwise this function will internally create a copy of * the memory (since GLib 2.60) or (in older versions) fail and exit the * process. * * Returns: a new floating #GVariant */ static GVariant * g_variant_new_from_trusted (const GVariantType *type, gconstpointer data, gsize size) { GVariant *value; GBytes *bytes; bytes = g_bytes_new (data, size); value = g_variant_new_from_bytes (type, bytes, TRUE); g_bytes_unref (bytes); return value; } /** * g_variant_new_boolean: * @value: a #gboolean value * * Creates a new boolean #GVariant instance -- either %TRUE or %FALSE. * * Returns: (transfer none): a floating reference to a new boolean #GVariant instance * * Since: 2.24 **/ GVariant * g_variant_new_boolean (gboolean value) { guchar v = value; return g_variant_new_from_trusted (G_VARIANT_TYPE_BOOLEAN, &v, 1); } /** * g_variant_get_boolean: * @value: a boolean #GVariant instance * * Returns the boolean value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_BOOLEAN. * * Returns: %TRUE or %FALSE * * Since: 2.24 **/ gboolean g_variant_get_boolean (GVariant *value) { const guchar *data; TYPE_CHECK (value, G_VARIANT_TYPE_BOOLEAN, FALSE); data = g_variant_get_data (value); return data != NULL ? *data != 0 : FALSE; } /* the constructors and accessors for byte, int{16,32,64}, handles and * doubles all look pretty much exactly the same, so we reduce * copy/pasting here. */ #define NUMERIC_TYPE(TYPE, type, ctype) \ GVariant *g_variant_new_##type (ctype value) { \ return g_variant_new_from_trusted (G_VARIANT_TYPE_##TYPE, \ &value, sizeof value); \ } \ ctype g_variant_get_##type (GVariant *value) { \ const ctype *data; \ TYPE_CHECK (value, G_VARIANT_TYPE_ ## TYPE, 0); \ data = g_variant_get_data (value); \ return data != NULL ? *data : 0; \ } /** * g_variant_new_byte: * @value: a #guint8 value * * Creates a new byte #GVariant instance. * * Returns: (transfer none): a floating reference to a new byte #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_byte: * @value: a byte #GVariant instance * * Returns the byte value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_BYTE. * * Returns: a #guint8 * * Since: 2.24 **/ NUMERIC_TYPE (BYTE, byte, guint8) /** * g_variant_new_int16: * @value: a #gint16 value * * Creates a new int16 #GVariant instance. * * Returns: (transfer none): a floating reference to a new int16 #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_int16: * @value: an int16 #GVariant instance * * Returns the 16-bit signed integer value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_INT16. * * Returns: a #gint16 * * Since: 2.24 **/ NUMERIC_TYPE (INT16, int16, gint16) /** * g_variant_new_uint16: * @value: a #guint16 value * * Creates a new uint16 #GVariant instance. * * Returns: (transfer none): a floating reference to a new uint16 #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_uint16: * @value: a uint16 #GVariant instance * * Returns the 16-bit unsigned integer value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_UINT16. * * Returns: a #guint16 * * Since: 2.24 **/ NUMERIC_TYPE (UINT16, uint16, guint16) /** * g_variant_new_int32: * @value: a #gint32 value * * Creates a new int32 #GVariant instance. * * Returns: (transfer none): a floating reference to a new int32 #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_int32: * @value: an int32 #GVariant instance * * Returns the 32-bit signed integer value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_INT32. * * Returns: a #gint32 * * Since: 2.24 **/ NUMERIC_TYPE (INT32, int32, gint32) /** * g_variant_new_uint32: * @value: a #guint32 value * * Creates a new uint32 #GVariant instance. * * Returns: (transfer none): a floating reference to a new uint32 #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_uint32: * @value: a uint32 #GVariant instance * * Returns the 32-bit unsigned integer value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_UINT32. * * Returns: a #guint32 * * Since: 2.24 **/ NUMERIC_TYPE (UINT32, uint32, guint32) /** * g_variant_new_int64: * @value: a #gint64 value * * Creates a new int64 #GVariant instance. * * Returns: (transfer none): a floating reference to a new int64 #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_int64: * @value: an int64 #GVariant instance * * Returns the 64-bit signed integer value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_INT64. * * Returns: a #gint64 * * Since: 2.24 **/ NUMERIC_TYPE (INT64, int64, gint64) /** * g_variant_new_uint64: * @value: a #guint64 value * * Creates a new uint64 #GVariant instance. * * Returns: (transfer none): a floating reference to a new uint64 #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_uint64: * @value: a uint64 #GVariant instance * * Returns the 64-bit unsigned integer value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_UINT64. * * Returns: a #guint64 * * Since: 2.24 **/ NUMERIC_TYPE (UINT64, uint64, guint64) /** * g_variant_new_handle: * @value: a #gint32 value * * Creates a new handle #GVariant instance. * * By convention, handles are indexes into an array of file descriptors * that are sent alongside a D-Bus message. If you're not interacting * with D-Bus, you probably don't need them. * * Returns: (transfer none): a floating reference to a new handle #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_handle: * @value: a handle #GVariant instance * * Returns the 32-bit signed integer value of @value. * * It is an error to call this function with a @value of any type other * than %G_VARIANT_TYPE_HANDLE. * * By convention, handles are indexes into an array of file descriptors * that are sent alongside a D-Bus message. If you're not interacting * with D-Bus, you probably don't need them. * * Returns: a #gint32 * * Since: 2.24 **/ NUMERIC_TYPE (HANDLE, handle, gint32) /** * g_variant_new_double: * @value: a #gdouble floating point value * * Creates a new double #GVariant instance. * * Returns: (transfer none): a floating reference to a new double #GVariant instance * * Since: 2.24 **/ /** * g_variant_get_double: * @value: a double #GVariant instance * * Returns the double precision floating point value of @value. * * It is an error to call this function with a @value of any type * other than %G_VARIANT_TYPE_DOUBLE. * * Returns: a #gdouble * * Since: 2.24 **/ NUMERIC_TYPE (DOUBLE, double, gdouble) /* Container type Constructor / Deconstructors {{{1 */ /** * g_variant_new_maybe: * @child_type: (nullable): the #GVariantType of the child, or %NULL * @child: (nullable): the child value, or %NULL * * Depending on if @child is %NULL, either wraps @child inside of a * maybe container or creates a Nothing instance for the given @type. * * At least one of @child_type and @child must be non-%NULL. * If @child_type is non-%NULL then it must be a definite type. * If they are both non-%NULL then @child_type must be the type * of @child. * * If @child is a floating reference (see g_variant_ref_sink()), the new * instance takes ownership of @child. * * Returns: (transfer none): a floating reference to a new #GVariant maybe instance * * Since: 2.24 **/ GVariant * g_variant_new_maybe (const GVariantType *child_type, GVariant *child) { GVariantType *maybe_type; GVariant *value; g_return_val_if_fail (child_type == NULL || g_variant_type_is_definite (child_type), 0); g_return_val_if_fail (child_type != NULL || child != NULL, NULL); g_return_val_if_fail (child_type == NULL || child == NULL || g_variant_is_of_type (child, child_type), NULL); if (child_type == NULL) child_type = g_variant_get_type (child); maybe_type = g_variant_type_new_maybe (child_type); if (child != NULL) { GVariant **children; gboolean trusted; children = g_new (GVariant *, 1); children[0] = g_variant_ref_sink (child); trusted = g_variant_is_trusted (children[0]); value = g_variant_new_from_children (maybe_type, children, 1, trusted); } else value = g_variant_new_from_children (maybe_type, NULL, 0, TRUE); g_variant_type_free (maybe_type); return value; } /** * g_variant_get_maybe: * @value: a maybe-typed value * * Given a maybe-typed #GVariant instance, extract its value. If the * value is Nothing, then this function returns %NULL. * * Returns: (nullable) (transfer full): the contents of @value, or %NULL * * Since: 2.24 **/ GVariant * g_variant_get_maybe (GVariant *value) { TYPE_CHECK (value, G_VARIANT_TYPE_MAYBE, NULL); if (g_variant_n_children (value)) return g_variant_get_child_value (value, 0); return NULL; } /** * g_variant_new_variant: (constructor) * @value: a #GVariant instance * * Boxes @value. The result is a #GVariant instance representing a * variant containing the original value. * * If @child is a floating reference (see g_variant_ref_sink()), the new * instance takes ownership of @child. * * Returns: (transfer none): a floating reference to a new variant #GVariant instance * * Since: 2.24 **/ GVariant * g_variant_new_variant (GVariant *value) { g_return_val_if_fail (value != NULL, NULL); g_variant_ref_sink (value); return g_variant_new_from_children (G_VARIANT_TYPE_VARIANT, g_memdup2 (&value, sizeof value), 1, g_variant_is_trusted (value)); } /** * g_variant_get_variant: * @value: a variant #GVariant instance * * Unboxes @value. The result is the #GVariant instance that was * contained in @value. * * Returns: (transfer full): the item contained in the variant * * Since: 2.24 **/ GVariant * g_variant_get_variant (GVariant *value) { TYPE_CHECK (value, G_VARIANT_TYPE_VARIANT, NULL); return g_variant_get_child_value (value, 0); } /** * g_variant_new_array: * @child_type: (nullable): the element type of the new array * @children: (nullable) (array length=n_children): an array of * #GVariant pointers, the children * @n_children: the length of @children * * Creates a new #GVariant array from @children. * * @child_type must be non-%NULL if @n_children is zero. Otherwise, the * child type is determined by inspecting the first element of the * @children array. If @child_type is non-%NULL then it must be a * definite type. * * The items of the array are taken from the @children array. No entry * in the @children array may be %NULL. * * All items in the array must have the same type, which must be the * same as @child_type, if given. * * If the @children are floating references (see g_variant_ref_sink()), the * new instance takes ownership of them as if via g_variant_ref_sink(). * * Returns: (transfer none): a floating reference to a new #GVariant array * * Since: 2.24 **/ GVariant * g_variant_new_array (const GVariantType *child_type, GVariant * const *children, gsize n_children) { GVariantType *array_type; GVariant **my_children; gboolean trusted; GVariant *value; gsize i; g_return_val_if_fail (n_children > 0 || child_type != NULL, NULL); g_return_val_if_fail (n_children == 0 || children != NULL, NULL); g_return_val_if_fail (child_type == NULL || g_variant_type_is_definite (child_type), NULL); my_children = g_new (GVariant *, n_children); trusted = TRUE; if (child_type == NULL) child_type = g_variant_get_type (children[0]); array_type = g_variant_type_new_array (child_type); for (i = 0; i < n_children; i++) { gboolean is_of_child_type = g_variant_is_of_type (children[i], child_type); if G_UNLIKELY (!is_of_child_type) { while (i != 0) g_variant_unref (my_children[--i]); g_free (my_children); g_return_val_if_fail (is_of_child_type, NULL); } my_children[i] = g_variant_ref_sink (children[i]); trusted &= g_variant_is_trusted (children[i]); } value = g_variant_new_from_children (array_type, my_children, n_children, trusted); g_variant_type_free (array_type); return value; } /*< private > * g_variant_make_tuple_type: * @children: (array length=n_children): an array of GVariant * * @n_children: the length of @children * * Return the type of a tuple containing @children as its items. **/ static GVariantType * g_variant_make_tuple_type (GVariant * const *children, gsize n_children) { const GVariantType **types; GVariantType *type; gsize i; types = g_new (const GVariantType *, n_children); for (i = 0; i < n_children; i++) types[i] = g_variant_get_type (children[i]); type = g_variant_type_new_tuple (types, n_children); g_free (types); return type; } /** * g_variant_new_tuple: * @children: (array length=n_children): the items to make the tuple out of * @n_children: the length of @children * * Creates a new tuple #GVariant out of the items in @children. The * type is determined from the types of @children. No entry in the * @children array may be %NULL. * * If @n_children is 0 then the unit tuple is constructed. * * If the @children are floating references (see g_variant_ref_sink()), the * new instance takes ownership of them as if via g_variant_ref_sink(). * * Returns: (transfer none): a floating reference to a new #GVariant tuple * * Since: 2.24 **/ GVariant * g_variant_new_tuple (GVariant * const *children, gsize n_children) { GVariantType *tuple_type; GVariant **my_children; gboolean trusted; GVariant *value; gsize i; g_return_val_if_fail (n_children == 0 || children != NULL, NULL); my_children = g_new (GVariant *, n_children); trusted = TRUE; for (i = 0; i < n_children; i++) { my_children[i] = g_variant_ref_sink (children[i]); trusted &= g_variant_is_trusted (children[i]); } tuple_type = g_variant_make_tuple_type (children, n_children); value = g_variant_new_from_children (tuple_type, my_children, n_children, trusted); g_variant_type_free (tuple_type); return value; } /*< private > * g_variant_make_dict_entry_type: * @key: a #GVariant, the key * @val: a #GVariant, the value * * Return the type of a dictionary entry containing @key and @val as its * children. **/ static GVariantType * g_variant_make_dict_entry_type (GVariant *key, GVariant *val) { return g_variant_type_new_dict_entry (g_variant_get_type (key), g_variant_get_type (val)); } /** * g_variant_new_dict_entry: (constructor) * @key: a basic #GVariant, the key * @value: a #GVariant, the value * * Creates a new dictionary entry #GVariant. @key and @value must be * non-%NULL. @key must be a value of a basic type (ie: not a container). * * If the @key or @value are floating references (see g_variant_ref_sink()), * the new instance takes ownership of them as if via g_variant_ref_sink(). * * Returns: (transfer none): a floating reference to a new dictionary entry #GVariant * * Since: 2.24 **/ GVariant * g_variant_new_dict_entry (GVariant *key, GVariant *value) { GVariantType *dict_type; GVariant **children; gboolean trusted; g_return_val_if_fail (key != NULL && value != NULL, NULL); g_return_val_if_fail (!g_variant_is_container (key), NULL); children = g_new (GVariant *, 2); children[0] = g_variant_ref_sink (key); children[1] = g_variant_ref_sink (value); trusted = g_variant_is_trusted (key) && g_variant_is_trusted (value); dict_type = g_variant_make_dict_entry_type (key, value); value = g_variant_new_from_children (dict_type, children, 2, trusted); g_variant_type_free (dict_type); return value; } /** * g_variant_lookup: (skip) * @dictionary: a dictionary #GVariant * @key: the key to look up in the dictionary * @format_string: a GVariant format string * @...: the arguments to unpack the value into * * Looks up a value in a dictionary #GVariant. * * This function is a wrapper around g_variant_lookup_value() and * g_variant_get(). In the case that %NULL would have been returned, * this function returns %FALSE. Otherwise, it unpacks the returned * value and returns %TRUE. * * @format_string determines the C types that are used for unpacking * the values and also determines if the values are copied or borrowed, * see the section on * [`GVariant` format strings](gvariant-format-strings.html#pointers). * * This function is currently implemented with a linear scan. If you * plan to do many lookups then #GVariantDict may be more efficient. * * Returns: %TRUE if a value was unpacked * * Since: 2.28 */ gboolean g_variant_lookup (GVariant *dictionary, const gchar *key, const gchar *format_string, ...) { GVariantType *type; GVariant *value; /* flatten */ g_variant_get_data (dictionary); type = g_variant_format_string_scan_type (format_string, NULL, NULL); value = g_variant_lookup_value (dictionary, key, type); g_variant_type_free (type); if (value) { va_list ap; va_start (ap, format_string); g_variant_get_va (value, format_string, NULL, &ap); g_variant_unref (value); va_end (ap); return TRUE; } else return FALSE; } /** * g_variant_lookup_value: * @dictionary: a dictionary #GVariant * @key: the key to look up in the dictionary * @expected_type: (nullable): a #GVariantType, or %NULL * * Looks up a value in a dictionary #GVariant. * * This function works with dictionaries of the type a{s*} (and equally * well with type a{o*}, but we only further discuss the string case * for sake of clarity). * * In the event that @dictionary has the type a{sv}, the @expected_type * string specifies what type of value is expected to be inside of the * variant. If the value inside the variant has a different type then * %NULL is returned. In the event that @dictionary has a value type other * than v then @expected_type must directly match the value type and it is * used to unpack the value directly or an error occurs. * * In either case, if @key is not found in @dictionary, %NULL is returned. * * If the key is found and the value has the correct type, it is * returned. If @expected_type was specified then any non-%NULL return * value will have this type. * * This function is currently implemented with a linear scan. If you * plan to do many lookups then #GVariantDict may be more efficient. * * Returns: (transfer full): the value of the dictionary key, or %NULL * * Since: 2.28 */ GVariant * g_variant_lookup_value (GVariant *dictionary, const gchar *key, const GVariantType *expected_type) { GVariantIter iter; GVariant *entry; GVariant *value; g_return_val_if_fail (g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{s*}")) || g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{o*}")), NULL); g_variant_iter_init (&iter, dictionary); while ((entry = g_variant_iter_next_value (&iter))) { GVariant *entry_key; gboolean matches; entry_key = g_variant_get_child_value (entry, 0); matches = strcmp (g_variant_get_string (entry_key, NULL), key) == 0; g_variant_unref (entry_key); if (matches) break; g_variant_unref (entry); } if (entry == NULL) return NULL; value = g_variant_get_child_value (entry, 1); g_variant_unref (entry); if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) { GVariant *tmp; tmp = g_variant_get_variant (value); g_variant_unref (value); if (expected_type && !g_variant_is_of_type (tmp, expected_type)) { g_variant_unref (tmp); tmp = NULL; } value = tmp; } g_return_val_if_fail (expected_type == NULL || value == NULL || g_variant_is_of_type (value, expected_type), NULL); return value; } /** * g_variant_get_fixed_array: * @value: a #GVariant array with fixed-sized elements * @n_elements: (out): a pointer to the location to store the number of items * @element_size: the size of each element * * Provides access to the serialized data for an array of fixed-sized * items. * * @value must be an array with fixed-sized elements. Numeric types are * fixed-size, as are tuples containing only other fixed-sized types. * * @element_size must be the size of a single element in the array, * as given by the section on * [serialized data memory][gvariant-serialized-data-memory]. * * In particular, arrays of these fixed-sized types can be interpreted * as an array of the given C type, with @element_size set to the size * the appropriate type: * - %G_VARIANT_TYPE_INT16 (etc.): #gint16 (etc.) * - %G_VARIANT_TYPE_BOOLEAN: #guchar (not #gboolean!) * - %G_VARIANT_TYPE_BYTE: #guint8 * - %G_VARIANT_TYPE_HANDLE: #guint32 * - %G_VARIANT_TYPE_DOUBLE: #gdouble * * For example, if calling this function for an array of 32-bit integers, * you might say `sizeof(gint32)`. This value isn't used except for the purpose * of a double-check that the form of the serialized data matches the caller's * expectation. * * @n_elements, which must be non-%NULL, is set equal to the number of * items in the array. * * Returns: (array length=n_elements) (transfer none): a pointer to * the fixed array * * Since: 2.24 **/ gconstpointer g_variant_get_fixed_array (GVariant *value, gsize *n_elements, gsize element_size) { GVariantTypeInfo *array_info; gsize array_element_size; gconstpointer data; gsize size; TYPE_CHECK (value, G_VARIANT_TYPE_ARRAY, NULL); g_return_val_if_fail (n_elements != NULL, NULL); g_return_val_if_fail (element_size > 0, NULL); array_info = g_variant_get_type_info (value); g_variant_type_info_query_element (array_info, NULL, &array_element_size); g_return_val_if_fail (array_element_size, NULL); if G_UNLIKELY (array_element_size != element_size) { if (array_element_size) g_critical ("g_variant_get_fixed_array: assertion " "'g_variant_array_has_fixed_size (value, element_size)' " "failed: array size %"G_GSIZE_FORMAT" does not match " "given element_size %"G_GSIZE_FORMAT".", array_element_size, element_size); else g_critical ("g_variant_get_fixed_array: assertion " "'g_variant_array_has_fixed_size (value, element_size)' " "failed: array does not have fixed size."); } data = g_variant_get_data (value); size = g_variant_get_size (value); if (size % element_size) *n_elements = 0; else *n_elements = size / element_size; if (*n_elements) return data; return NULL; } /** * g_variant_new_fixed_array: * @element_type: the #GVariantType of each element * @elements: a pointer to the fixed array of contiguous elements * @n_elements: the number of elements * @element_size: the size of each element * * Constructs a new array #GVariant instance, where the elements are * of @element_type type. * * @elements must be an array with fixed-sized elements. Numeric types are * fixed-size as are tuples containing only other fixed-sized types. * * @element_size must be the size of a single element in the array. * For example, if calling this function for an array of 32-bit integers, * you might say sizeof(gint32). This value isn't used except for the purpose * of a double-check that the form of the serialized data matches the caller's * expectation. * * @n_elements must be the length of the @elements array. * * Returns: (transfer none): a floating reference to a new array #GVariant instance * * Since: 2.32 **/ GVariant * g_variant_new_fixed_array (const GVariantType *element_type, gconstpointer elements, gsize n_elements, gsize element_size) { GVariantType *array_type; gsize array_element_size; GVariantTypeInfo *array_info; GVariant *value; gpointer data; g_return_val_if_fail (g_variant_type_is_definite (element_type), NULL); g_return_val_if_fail (element_size > 0, NULL); array_type = g_variant_type_new_array (element_type); array_info = g_variant_type_info_get (array_type); g_variant_type_info_query_element (array_info, NULL, &array_element_size); if G_UNLIKELY (array_element_size != element_size) { if (array_element_size) g_critical ("g_variant_new_fixed_array: array size %" G_GSIZE_FORMAT " does not match given element_size %" G_GSIZE_FORMAT ".", array_element_size, element_size); else g_critical ("g_variant_get_fixed_array: array does not have fixed size."); return NULL; } data = g_memdup2 (elements, n_elements * element_size); value = g_variant_new_from_data (array_type, data, n_elements * element_size, FALSE, g_free, data); g_variant_type_free (array_type); g_variant_type_info_unref (array_info); return value; } /* String type constructor/getters/validation {{{1 */ /** * g_variant_new_string: * @string: a normal UTF-8 nul-terminated string * * Creates a string #GVariant with the contents of @string. * * @string must be valid UTF-8, and must not be %NULL. To encode * potentially-%NULL strings, use g_variant_new() with `ms` as the * [format string][gvariant-format-strings-maybe-types]. * * Returns: (transfer none): a floating reference to a new string #GVariant instance * * Since: 2.24 **/ GVariant * g_variant_new_string (const gchar *string) { g_return_val_if_fail (string != NULL, NULL); g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL); return g_variant_new_from_trusted (G_VARIANT_TYPE_STRING, string, strlen (string) + 1); } /** * g_variant_new_take_string: (skip) * @string: a normal UTF-8 nul-terminated string * * Creates a string #GVariant with the contents of @string. * * @string must be valid UTF-8, and must not be %NULL. To encode * potentially-%NULL strings, use this with g_variant_new_maybe(). * * After this call, @string belongs to the #GVariant and may no longer be * modified by the caller. The memory of @data has to be dynamically * allocated and will eventually be freed with g_free(). * * You must not modify or access @string in any other way after passing * it to this function. It is even possible that @string is immediately * freed. * * Returns: (transfer none): a floating reference to a new string * #GVariant instance * * Since: 2.38 **/ GVariant * g_variant_new_take_string (gchar *string) { GVariant *value; GBytes *bytes; g_return_val_if_fail (string != NULL, NULL); g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL); bytes = g_bytes_new_take (string, strlen (string) + 1); value = g_variant_new_from_bytes (G_VARIANT_TYPE_STRING, bytes, TRUE); g_bytes_unref (bytes); return value; } /** * g_variant_new_printf: (skip) * @format_string: a printf-style format string * @...: arguments for @format_string * * Creates a string-type GVariant using printf formatting. * * This is similar to calling g_strdup_printf() and then * g_variant_new_string() but it saves a temporary variable and an * unnecessary copy. * * Returns: (transfer none): a floating reference to a new string * #GVariant instance * * Since: 2.38 **/ GVariant * g_variant_new_printf (const gchar *format_string, ...) { GVariant *value; GBytes *bytes; gchar *string; va_list ap; g_return_val_if_fail (format_string != NULL, NULL); va_start (ap, format_string); string = g_strdup_vprintf (format_string, ap); va_end (ap); bytes = g_bytes_new_take (string, strlen (string) + 1); value = g_variant_new_from_bytes (G_VARIANT_TYPE_STRING, bytes, TRUE); g_bytes_unref (bytes); return value; } /** * g_variant_new_object_path: * @object_path: a normal C nul-terminated string * * Creates a D-Bus object path #GVariant with the contents of @object_path. * @object_path must be a valid D-Bus object path. Use * g_variant_is_object_path() if you're not sure. * * Returns: (transfer none): a floating reference to a new object path #GVariant instance * * Since: 2.24 **/ GVariant * g_variant_new_object_path (const gchar *object_path) { g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); return g_variant_new_from_trusted (G_VARIANT_TYPE_OBJECT_PATH, object_path, strlen (object_path) + 1); } /** * g_variant_is_object_path: * @string: a normal C nul-terminated string * * Determines if a given string is a valid D-Bus object path. You * should ensure that a string is a valid D-Bus object path before * passing it to g_variant_new_object_path(). * * A valid object path starts with `/` followed by zero or more * sequences of characters separated by `/` characters. Each sequence * must contain only the characters `[A-Z][a-z][0-9]_`. No sequence * (including the one following the final `/` character) may be empty. * * Returns: %TRUE if @string is a D-Bus object path * * Since: 2.24 **/ gboolean g_variant_is_object_path (const gchar *string) { g_return_val_if_fail (string != NULL, FALSE); return g_variant_serialiser_is_object_path (string, strlen (string) + 1); } /** * g_variant_new_signature: * @signature: a normal C nul-terminated string * * Creates a D-Bus type signature #GVariant with the contents of * @string. @string must be a valid D-Bus type signature. Use * g_variant_is_signature() if you're not sure. * * Returns: (transfer none): a floating reference to a new signature #GVariant instance * * Since: 2.24 **/ GVariant * g_variant_new_signature (const gchar *signature) { g_return_val_if_fail (g_variant_is_signature (signature), NULL); return g_variant_new_from_trusted (G_VARIANT_TYPE_SIGNATURE, signature, strlen (signature) + 1); } /** * g_variant_is_signature: * @string: a normal C nul-terminated string * * Determines if a given string is a valid D-Bus type signature. You * should ensure that a string is a valid D-Bus type signature before * passing it to g_variant_new_signature(). * * D-Bus type signatures consist of zero or more definite #GVariantType * strings in sequence. * * Returns: %TRUE if @string is a D-Bus type signature * * Since: 2.24 **/ gboolean g_variant_is_signature (const gchar *string) { g_return_val_if_fail (string != NULL, FALSE); return g_variant_serialiser_is_signature (string, strlen (string) + 1); } /** * g_variant_get_string: * @value: a string #GVariant instance * @length: (optional) (default 0) (out): a pointer to a #gsize, * to store the length * * Returns the string value of a #GVariant instance with a string * type. This includes the types %G_VARIANT_TYPE_STRING, * %G_VARIANT_TYPE_OBJECT_PATH and %G_VARIANT_TYPE_SIGNATURE. * * The string will always be UTF-8 encoded, will never be %NULL, and will never * contain nul bytes. * * If @length is non-%NULL then the length of the string (in bytes) is * returned there. For trusted values, this information is already * known. Untrusted values will be validated and, if valid, a strlen() will be * performed. If invalid, a default value will be returned — for * %G_VARIANT_TYPE_OBJECT_PATH, this is `"/"`, and for other types it is the * empty string. * * It is an error to call this function with a @value of any type * other than those three. * * The return value remains valid as long as @value exists. * * Returns: (transfer none): the constant string, UTF-8 encoded * * Since: 2.24 **/ const gchar * g_variant_get_string (GVariant *value, gsize *length) { gconstpointer data; gsize size; g_return_val_if_fail (value != NULL, NULL); g_return_val_if_fail ( g_variant_is_of_type (value, G_VARIANT_TYPE_STRING) || g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH) || g_variant_is_of_type (value, G_VARIANT_TYPE_SIGNATURE), NULL); data = g_variant_get_data (value); size = g_variant_get_size (value); if (!g_variant_is_trusted (value)) { switch (g_variant_classify (value)) { case G_VARIANT_CLASS_STRING: if (g_variant_serialiser_is_string (data, size)) break; data = ""; size = 1; break; case G_VARIANT_CLASS_OBJECT_PATH: if (g_variant_serialiser_is_object_path (data, size)) break; data = "/"; size = 2; break; case G_VARIANT_CLASS_SIGNATURE: if (g_variant_serialiser_is_signature (data, size)) break; data = ""; size = 1; break; default: g_assert_not_reached (); } } if (length) *length = size - 1; return data; } /** * g_variant_dup_string: * @value: a string #GVariant instance * @length: (out): a pointer to a #gsize, to store the length * * Similar to g_variant_get_string() except that instead of returning * a constant string, the string is duplicated. * * The string will always be UTF-8 encoded. * * The return value must be freed using g_free(). * * Returns: (transfer full): a newly allocated string, UTF-8 encoded * * Since: 2.24 **/ gchar * g_variant_dup_string (GVariant *value, gsize *length) { return g_strdup (g_variant_get_string (value, length)); } /** * g_variant_new_strv: * @strv: (array length=length) (element-type utf8): an array of strings * @length: the length of @strv, or -1 * * Constructs an array of strings #GVariant from the given array of * strings. * * If @length is -1 then @strv is %NULL-terminated. * * Returns: (transfer none): a new floating #GVariant instance * * Since: 2.24 **/ GVariant * g_variant_new_strv (const gchar * const *strv, gssize length) { GVariant **strings; gsize i, length_unsigned; g_return_val_if_fail (length == 0 || strv != NULL, NULL); if (length < 0) length = g_strv_length ((gchar **) strv); length_unsigned = length; strings = g_new (GVariant *, length_unsigned); for (i = 0; i < length_unsigned; i++) strings[i] = g_variant_ref_sink (g_variant_new_string (strv[i])); return g_variant_new_from_children (G_VARIANT_TYPE_STRING_ARRAY, strings, length_unsigned, TRUE); } /** * g_variant_get_strv: * @value: an array of strings #GVariant * @length: (out) (optional): the length of the result, or %NULL * * Gets the contents of an array of strings #GVariant. This call * makes a shallow copy; the return result should be released with * g_free(), but the individual strings must not be modified. * * If @length is non-%NULL then the number of elements in the result * is stored there. In any case, the resulting array will be * %NULL-terminated. * * For an empty array, @length will be set to 0 and a pointer to a * %NULL pointer will be returned. * * Returns: (array length=length zero-terminated=1) (transfer container): an array of constant strings * * Since: 2.24 **/ const gchar ** g_variant_get_strv (GVariant *value, gsize *length) { const gchar **strv; gsize n; gsize i; TYPE_CHECK (value, G_VARIANT_TYPE_STRING_ARRAY, NULL); g_variant_get_data (value); n = g_variant_n_children (value); strv = g_new (const gchar *, n + 1); for (i = 0; i < n; i++) { GVariant *string; string = g_variant_get_child_value (value, i); strv[i] = g_variant_get_string (string, NULL); g_variant_unref (string); } strv[i] = NULL; if (length) *length = n; return strv; } /** * g_variant_dup_strv: * @value: an array of strings #GVariant * @length: (out) (optional): the length of the result, or %NULL * * Gets the contents of an array of strings #GVariant. This call * makes a deep copy; the return result should be released with * g_strfreev(). * * If @length is non-%NULL then the number of elements in the result * is stored there. In any case, the resulting array will be * %NULL-terminated. * * For an empty array, @length will be set to 0 and a pointer to a * %NULL pointer will be returned. * * Returns: (array length=length zero-terminated=1) (transfer full): an array of strings * * Since: 2.24 **/ gchar ** g_variant_dup_strv (GVariant *value, gsize *length) { gchar **strv; gsize n; gsize i; TYPE_CHECK (value, G_VARIANT_TYPE_STRING_ARRAY, NULL); n = g_variant_n_children (value); strv = g_new (gchar *, n + 1); for (i = 0; i < n; i++) { GVariant *string; string = g_variant_get_child_value (value, i); strv[i] = g_variant_dup_string (string, NULL); g_variant_unref (string); } strv[i] = NULL; if (length) *length = n; return strv; } /** * g_variant_new_objv: * @strv: (array length=length) (element-type utf8): an array of strings * @length: the length of @strv, or -1 * * Constructs an array of object paths #GVariant from the given array of * strings. * * Each string must be a valid #GVariant object path; see * g_variant_is_object_path(). * * If @length is -1 then @strv is %NULL-terminated. * * Returns: (transfer none): a new floating #GVariant instance * * Since: 2.30 **/ GVariant * g_variant_new_objv (const gchar * const *strv, gssize length) { GVariant **strings; gsize i, length_unsigned; g_return_val_if_fail (length == 0 || strv != NULL, NULL); if (length < 0) length = g_strv_length ((gchar **) strv); length_unsigned = length; strings = g_new (GVariant *, length_unsigned); for (i = 0; i < length_unsigned; i++) strings[i] = g_variant_ref_sink (g_variant_new_object_path (strv[i])); return g_variant_new_from_children (G_VARIANT_TYPE_OBJECT_PATH_ARRAY, strings, length_unsigned, TRUE); } /** * g_variant_get_objv: * @value: an array of object paths #GVariant * @length: (out) (optional): the length of the result, or %NULL * * Gets the contents of an array of object paths #GVariant. This call * makes a shallow copy; the return result should be released with * g_free(), but the individual strings must not be modified. * * If @length is non-%NULL then the number of elements in the result * is stored there. In any case, the resulting array will be * %NULL-terminated. * * For an empty array, @length will be set to 0 and a pointer to a * %NULL pointer will be returned. * * Returns: (array length=length zero-terminated=1) (transfer container): an array of constant strings * * Since: 2.30 **/ const gchar ** g_variant_get_objv (GVariant *value, gsize *length) { const gchar **strv; gsize n; gsize i; TYPE_CHECK (value, G_VARIANT_TYPE_OBJECT_PATH_ARRAY, NULL); g_variant_get_data (value); n = g_variant_n_children (value); strv = g_new (const gchar *, n + 1); for (i = 0; i < n; i++) { GVariant *string; string = g_variant_get_child_value (value, i); strv[i] = g_variant_get_string (string, NULL); g_variant_unref (string); } strv[i] = NULL; if (length) *length = n; return strv; } /** * g_variant_dup_objv: * @value: an array of object paths #GVariant * @length: (out) (optional): the length of the result, or %NULL * * Gets the contents of an array of object paths #GVariant. This call * makes a deep copy; the return result should be released with * g_strfreev(). * * If @length is non-%NULL then the number of elements in the result * is stored there. In any case, the resulting array will be * %NULL-terminated. * * For an empty array, @length will be set to 0 and a pointer to a * %NULL pointer will be returned. * * Returns: (array length=length zero-terminated=1) (transfer full): an array of strings * * Since: 2.30 **/ gchar ** g_variant_dup_objv (GVariant *value, gsize *length) { gchar **strv; gsize n; gsize i; TYPE_CHECK (value, G_VARIANT_TYPE_OBJECT_PATH_ARRAY, NULL); n = g_variant_n_children (value); strv = g_new (gchar *, n + 1); for (i = 0; i < n; i++) { GVariant *string; string = g_variant_get_child_value (value, i); strv[i] = g_variant_dup_string (string, NULL); g_variant_unref (string); } strv[i] = NULL; if (length) *length = n; return strv; } /** * g_variant_new_bytestring: * @string: (array zero-terminated=1) (element-type guint8): a normal * nul-terminated string in no particular encoding * * Creates an array-of-bytes #GVariant with the contents of @string. * This function is just like g_variant_new_string() except that the * string need not be valid UTF-8. * * The nul terminator character at the end of the string is stored in * the array. * * Returns: (transfer none): a floating reference to a new bytestring #GVariant instance * * Since: 2.26 **/ GVariant * g_variant_new_bytestring (const gchar *string) { g_return_val_if_fail (string != NULL, NULL); return g_variant_new_from_trusted (G_VARIANT_TYPE_BYTESTRING, string, strlen (string) + 1); } /** * g_variant_get_bytestring: * @value: an array-of-bytes #GVariant instance * * Returns the string value of a #GVariant instance with an * array-of-bytes type. The string has no particular encoding. * * If the array does not end with a nul terminator character, the empty * string is returned. For this reason, you can always trust that a * non-%NULL nul-terminated string will be returned by this function. * * If the array contains a nul terminator character somewhere other than * the last byte then the returned string is the string, up to the first * such nul character. * * g_variant_get_fixed_array() should be used instead if the array contains * arbitrary data that could not be nul-terminated or could contain nul bytes. * * It is an error to call this function with a @value that is not an * array of bytes. * * The return value remains valid as long as @value exists. * * Returns: (transfer none) (array zero-terminated=1) (element-type guint8): * the constant string * * Since: 2.26 **/ const gchar * g_variant_get_bytestring (GVariant *value) { const gchar *string; gsize size; TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING, NULL); /* Won't be NULL since this is an array type */ string = g_variant_get_data (value); size = g_variant_get_size (value); if (size && string[size - 1] == '\0') return string; else return ""; } /** * g_variant_dup_bytestring: * @value: an array-of-bytes #GVariant instance * @length: (out) (optional) (default NULL): a pointer to a #gsize, to store * the length (not including the nul terminator) * * Similar to g_variant_get_bytestring() except that instead of * returning a constant string, the string is duplicated. * * The return value must be freed using g_free(). * * Returns: (transfer full) (array zero-terminated=1 length=length) (element-type guint8): * a newly allocated string * * Since: 2.26 **/ gchar * g_variant_dup_bytestring (GVariant *value, gsize *length) { const gchar *original = g_variant_get_bytestring (value); gsize size; /* don't crash in case get_bytestring() had an assert failure */ if (original == NULL) return NULL; size = strlen (original); if (length) *length = size; return g_memdup2 (original, size + 1); } /** * g_variant_new_bytestring_array: * @strv: (array length=length): an array of strings * @length: the length of @strv, or -1 * * Constructs an array of bytestring #GVariant from the given array of * strings. * * If @length is -1 then @strv is %NULL-terminated. * * Returns: (transfer none): a new floating #GVariant instance * * Since: 2.26 **/ GVariant * g_variant_new_bytestring_array (const gchar * const *strv, gssize length) { GVariant **strings; gsize i, length_unsigned; g_return_val_if_fail (length == 0 || strv != NULL, NULL); if (length < 0) length = g_strv_length ((gchar **) strv); length_unsigned = length; strings = g_new (GVariant *, length_unsigned); for (i = 0; i < length_unsigned; i++) strings[i] = g_variant_ref_sink (g_variant_new_bytestring (strv[i])); return g_variant_new_from_children (G_VARIANT_TYPE_BYTESTRING_ARRAY, strings, length_unsigned, TRUE); } /** * g_variant_get_bytestring_array: * @value: an array of array of bytes #GVariant ('aay') * @length: (out) (optional): the length of the result, or %NULL * * Gets the contents of an array of array of bytes #GVariant. This call * makes a shallow copy; the return result should be released with * g_free(), but the individual strings must not be modified. * * If @length is non-%NULL then the number of elements in the result is * stored there. In any case, the resulting array will be * %NULL-terminated. * * For an empty array, @length will be set to 0 and a pointer to a * %NULL pointer will be returned. * * Returns: (array length=length) (transfer container): an array of constant strings * * Since: 2.26 **/ const gchar ** g_variant_get_bytestring_array (GVariant *value, gsize *length) { const gchar **strv; gsize n; gsize i; TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL); g_variant_get_data (value); n = g_variant_n_children (value); strv = g_new (const gchar *, n + 1); for (i = 0; i < n; i++) { GVariant *string; string = g_variant_get_child_value (value, i); strv[i] = g_variant_get_bytestring (string); g_variant_unref (string); } strv[i] = NULL; if (length) *length = n; return strv; } /** * g_variant_dup_bytestring_array: * @value: an array of array of bytes #GVariant ('aay') * @length: (out) (optional): the length of the result, or %NULL * * Gets the contents of an array of array of bytes #GVariant. This call * makes a deep copy; the return result should be released with * g_strfreev(). * * If @length is non-%NULL then the number of elements in the result is * stored there. In any case, the resulting array will be * %NULL-terminated. * * For an empty array, @length will be set to 0 and a pointer to a * %NULL pointer will be returned. * * Returns: (array length=length) (transfer full): an array of strings * * Since: 2.26 **/ gchar ** g_variant_dup_bytestring_array (GVariant *value, gsize *length) { gchar **strv; gsize n; gsize i; TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL); g_variant_get_data (value); n = g_variant_n_children (value); strv = g_new (gchar *, n + 1); for (i = 0; i < n; i++) { GVariant *string; string = g_variant_get_child_value (value, i); strv[i] = g_variant_dup_bytestring (string, NULL); g_variant_unref (string); } strv[i] = NULL; if (length) *length = n; return strv; } /* Type checking and querying {{{1 */ /** * g_variant_get_type: * @value: a #GVariant * * Determines the type of @value. * * The return value is valid for the lifetime of @value and must not * be freed. * * Returns: a #GVariantType * * Since: 2.24 **/ const GVariantType * g_variant_get_type (GVariant *value) { GVariantTypeInfo *type_info; g_return_val_if_fail (value != NULL, NULL); type_info = g_variant_get_type_info (value); return (GVariantType *) g_variant_type_info_get_type_string (type_info); } /** * g_variant_get_type_string: * @value: a #GVariant * * Returns the type string of @value. Unlike the result of calling * g_variant_type_peek_string(), this string is nul-terminated. This * string belongs to #GVariant and must not be freed. * * Returns: the type string for the type of @value * * Since: 2.24 **/ const gchar * g_variant_get_type_string (GVariant *value) { GVariantTypeInfo *type_info; g_return_val_if_fail (value != NULL, NULL); type_info = g_variant_get_type_info (value); return g_variant_type_info_get_type_string (type_info); } /** * g_variant_is_of_type: * @value: a #GVariant instance * @type: a #GVariantType * * Checks if a value has a type matching the provided type. * * Returns: %TRUE if the type of @value matches @type * * Since: 2.24 **/ gboolean g_variant_is_of_type (GVariant *value, const GVariantType *type) { return g_variant_type_is_subtype_of (g_variant_get_type (value), type); } /** * g_variant_is_container: * @value: a #GVariant instance * * Checks if @value is a container. * * Returns: %TRUE if @value is a container * * Since: 2.24 */ gboolean g_variant_is_container (GVariant *value) { return g_variant_type_is_container (g_variant_get_type (value)); } /** * g_variant_classify: * @value: a #GVariant * * Classifies @value according to its top-level type. * * Returns: the #GVariantClass of @value * * Since: 2.24 **/ /** * GVariantClass: * @G_VARIANT_CLASS_BOOLEAN: The #GVariant is a boolean. * @G_VARIANT_CLASS_BYTE: The #GVariant is a byte. * @G_VARIANT_CLASS_INT16: The #GVariant is a signed 16 bit integer. * @G_VARIANT_CLASS_UINT16: The #GVariant is an unsigned 16 bit integer. * @G_VARIANT_CLASS_INT32: The #GVariant is a signed 32 bit integer. * @G_VARIANT_CLASS_UINT32: The #GVariant is an unsigned 32 bit integer. * @G_VARIANT_CLASS_INT64: The #GVariant is a signed 64 bit integer. * @G_VARIANT_CLASS_UINT64: The #GVariant is an unsigned 64 bit integer. * @G_VARIANT_CLASS_HANDLE: The #GVariant is a file handle index. * @G_VARIANT_CLASS_DOUBLE: The #GVariant is a double precision floating * point value. * @G_VARIANT_CLASS_STRING: The #GVariant is a normal string. * @G_VARIANT_CLASS_OBJECT_PATH: The #GVariant is a D-Bus object path * string. * @G_VARIANT_CLASS_SIGNATURE: The #GVariant is a D-Bus signature string. * @G_VARIANT_CLASS_VARIANT: The #GVariant is a variant. * @G_VARIANT_CLASS_MAYBE: The #GVariant is a maybe-typed value. * @G_VARIANT_CLASS_ARRAY: The #GVariant is an array. * @G_VARIANT_CLASS_TUPLE: The #GVariant is a tuple. * @G_VARIANT_CLASS_DICT_ENTRY: The #GVariant is a dictionary entry. * * The range of possible top-level types of #GVariant instances. * * Since: 2.24 **/ GVariantClass g_variant_classify (GVariant *value) { g_return_val_if_fail (value != NULL, 0); return *g_variant_get_type_string (value); } /* Pretty printer {{{1 */ /* This function is not introspectable because if @string is NULL, @returns is (transfer full), otherwise it is (transfer none), which is not supported by GObjectIntrospection */ /** * g_variant_print_string: (skip) * @value: a #GVariant * @string: (nullable) (default NULL): a #GString, or %NULL * @type_annotate: %TRUE if type information should be included in * the output * * Behaves as g_variant_print(), but operates on a #GString. * * If @string is non-%NULL then it is appended to and returned. Else, * a new empty #GString is allocated and it is returned. * * Returns: a #GString containing the string * * Since: 2.24 **/ GString * g_variant_print_string (GVariant *value, GString *string, gboolean type_annotate) { const gchar *value_type_string = g_variant_get_type_string (value); if G_UNLIKELY (string == NULL) string = g_string_new (NULL); switch (value_type_string[0]) { case G_VARIANT_CLASS_MAYBE: if (type_annotate) g_string_append_printf (string, "@%s ", value_type_string); if (g_variant_n_children (value)) { const GVariantType *base_type; guint i, depth; GVariant *element = NULL; /* Nested maybes: * * Consider the case of the type "mmi". In this case we could * write "just just 4", but "4" alone is totally unambiguous, * so we try to drop "just" where possible. * * We have to be careful not to always drop "just", though, * since "nothing" needs to be distinguishable from "just * nothing". The case where we need to ensure we keep the * "just" is actually exactly the case where we have a nested * Nothing. * * Search for the nested Nothing, to save a lot of recursion if there * are multiple levels of maybes. */ for (depth = 0, base_type = g_variant_get_type (value); g_variant_type_is_maybe (base_type); depth++, base_type = g_variant_type_element (base_type)); element = g_variant_ref (value); for (i = 0; i < depth && element != NULL; i++) { GVariant *new_element = g_variant_n_children (element) ? g_variant_get_child_value (element, 0) : NULL; g_variant_unref (element); element = g_steal_pointer (&new_element); } if (element == NULL) { /* One of the maybes was Nothing, so print out the right number of * justs. */ for (; i > 1; i--) g_string_append (string, "just "); g_string_append (string, "nothing"); } else { /* There are no Nothings, so print out the child with no prefixes. */ g_variant_print_string (element, string, FALSE); } g_clear_pointer (&element, g_variant_unref); } else g_string_append (string, "nothing"); break; case G_VARIANT_CLASS_ARRAY: /* it's an array so the first character of the type string is 'a' * * if the first two characters are 'ay' then it's a bytestring. * under certain conditions we print those as strings. */ if (value_type_string[1] == 'y') { const gchar *str; gsize size; gsize i; /* first determine if it is a byte string. * that's when there's a single nul character: at the end. */ str = g_variant_get_data (value); size = g_variant_get_size (value); for (i = 0; i < size; i++) if (str[i] == '\0') break; /* first nul byte is the last byte -> it's a byte string. */ if (i == size - 1) { gchar *escaped = g_strescape (str, NULL); /* use double quotes only if a ' is in the string */ if (strchr (str, '\'')) g_string_append_printf (string, "b\"%s\"", escaped); else g_string_append_printf (string, "b'%s'", escaped); g_free (escaped); break; } else { /* fall through and handle normally... */ } } /* * if the first two characters are 'a{' then it's an array of * dictionary entries (ie: a dictionary) so we print that * differently. */ if (value_type_string[1] == '{') /* dictionary */ { const gchar *comma = ""; gsize n, i; if ((n = g_variant_n_children (value)) == 0) { if (type_annotate) g_string_append_printf (string, "@%s ", value_type_string); g_string_append (string, "{}"); break; } g_string_append_c (string, '{'); for (i = 0; i < n; i++) { GVariant *entry, *key, *val; g_string_append (string, comma); comma = ", "; entry = g_variant_get_child_value (value, i); key = g_variant_get_child_value (entry, 0); val = g_variant_get_child_value (entry, 1); g_variant_unref (entry); g_variant_print_string (key, string, type_annotate); g_variant_unref (key); g_string_append (string, ": "); g_variant_print_string (val, string, type_annotate); g_variant_unref (val); type_annotate = FALSE; } g_string_append_c (string, '}'); } else /* normal (non-dictionary) array */ { const gchar *comma = ""; gsize n, i; if ((n = g_variant_n_children (value)) == 0) { if (type_annotate) g_string_append_printf (string, "@%s ", value_type_string); g_string_append (string, "[]"); break; } g_string_append_c (string, '['); for (i = 0; i < n; i++) { GVariant *element; g_string_append (string, comma); comma = ", "; element = g_variant_get_child_value (value, i); g_variant_print_string (element, string, type_annotate); g_variant_unref (element); type_annotate = FALSE; } g_string_append_c (string, ']'); } break; case G_VARIANT_CLASS_TUPLE: { gsize n, i; n = g_variant_n_children (value); g_string_append_c (string, '('); for (i = 0; i < n; i++) { GVariant *element; element = g_variant_get_child_value (value, i); g_variant_print_string (element, string, type_annotate); g_string_append (string, ", "); g_variant_unref (element); } /* for >1 item: remove final ", " * for 1 item: remove final " ", but leave the "," * for 0 items: there is only "(", so remove nothing */ g_string_truncate (string, string->len - (n > 0) - (n > 1)); g_string_append_c (string, ')'); } break; case G_VARIANT_CLASS_DICT_ENTRY: { GVariant *element; g_string_append_c (string, '{'); element = g_variant_get_child_value (value, 0); g_variant_print_string (element, string, type_annotate); g_variant_unref (element); g_string_append (string, ", "); element = g_variant_get_child_value (value, 1); g_variant_print_string (element, string, type_annotate); g_variant_unref (element); g_string_append_c (string, '}'); } break; case G_VARIANT_CLASS_VARIANT: { GVariant *child = g_variant_get_variant (value); /* Always annotate types in nested variants, because they are * (by nature) of variable type. */ g_string_append_c (string, '<'); g_variant_print_string (child, string, TRUE); g_string_append_c (string, '>'); g_variant_unref (child); } break; case G_VARIANT_CLASS_BOOLEAN: if (g_variant_get_boolean (value)) g_string_append (string, "true"); else g_string_append (string, "false"); break; case G_VARIANT_CLASS_STRING: { const gchar *str = g_variant_get_string (value, NULL); gunichar quote = strchr (str, '\'') ? '"' : '\''; g_string_append_c (string, quote); while (*str) { gunichar c = g_utf8_get_char (str); if (c == quote || c == '\\') g_string_append_c (string, '\\'); if (g_unichar_isprint (c)) g_string_append_unichar (string, c); else { g_string_append_c (string, '\\'); if (c < 0x10000) switch (c) { case '\a': g_string_append_c (string, 'a'); break; case '\b': g_string_append_c (string, 'b'); break; case '\f': g_string_append_c (string, 'f'); break; case '\n': g_string_append_c (string, 'n'); break; case '\r': g_string_append_c (string, 'r'); break; case '\t': g_string_append_c (string, 't'); break; case '\v': g_string_append_c (string, 'v'); break; default: g_string_append_printf (string, "u%04x", c); break; } else g_string_append_printf (string, "U%08x", c); } str = g_utf8_next_char (str); } g_string_append_c (string, quote); } break; case G_VARIANT_CLASS_BYTE: if (type_annotate) g_string_append (string, "byte "); g_string_append_printf (string, "0x%02x", g_variant_get_byte (value)); break; case G_VARIANT_CLASS_INT16: if (type_annotate) g_string_append (string, "int16 "); g_string_append_printf (string, "%"G_GINT16_FORMAT, g_variant_get_int16 (value)); break; case G_VARIANT_CLASS_UINT16: if (type_annotate) g_string_append (string, "uint16 "); g_string_append_printf (string, "%"G_GUINT16_FORMAT, g_variant_get_uint16 (value)); break; case G_VARIANT_CLASS_INT32: /* Never annotate this type because it is the default for numbers * (and this is a *pretty* printer) */ g_string_append_printf (string, "%"G_GINT32_FORMAT, g_variant_get_int32 (value)); break; case G_VARIANT_CLASS_HANDLE: if (type_annotate) g_string_append (string, "handle "); g_string_append_printf (string, "%"G_GINT32_FORMAT, g_variant_get_handle (value)); break; case G_VARIANT_CLASS_UINT32: if (type_annotate) g_string_append (string, "uint32 "); g_string_append_printf (string, "%"G_GUINT32_FORMAT, g_variant_get_uint32 (value)); break; case G_VARIANT_CLASS_INT64: if (type_annotate) g_string_append (string, "int64 "); g_string_append_printf (string, "%"G_GINT64_FORMAT, g_variant_get_int64 (value)); break; case G_VARIANT_CLASS_UINT64: if (type_annotate) g_string_append (string, "uint64 "); g_string_append_printf (string, "%"G_GUINT64_FORMAT, g_variant_get_uint64 (value)); break; case G_VARIANT_CLASS_DOUBLE: { gchar buffer[100]; gint i; g_ascii_dtostr (buffer, sizeof buffer, g_variant_get_double (value)); for (i = 0; buffer[i]; i++) if (buffer[i] == '.' || buffer[i] == 'e' || buffer[i] == 'n' || buffer[i] == 'N') break; /* if there is no '.' or 'e' in the float then add one */ if (buffer[i] == '\0') { buffer[i++] = '.'; buffer[i++] = '0'; buffer[i++] = '\0'; } g_string_append (string, buffer); } break; case G_VARIANT_CLASS_OBJECT_PATH: if (type_annotate) g_string_append (string, "objectpath "); g_string_append_printf (string, "\'%s\'", g_variant_get_string (value, NULL)); break; case G_VARIANT_CLASS_SIGNATURE: if (type_annotate) g_string_append (string, "signature "); g_string_append_printf (string, "\'%s\'", g_variant_get_string (value, NULL)); break; default: g_assert_not_reached (); } return string; } /** * g_variant_print: * @value: a #GVariant * @type_annotate: %TRUE if type information should be included in * the output * * Pretty-prints @value in the format understood by g_variant_parse(). * * The format is described [here][gvariant-text]. * * If @type_annotate is %TRUE, then type information is included in * the output. * * Returns: (transfer full): a newly-allocated string holding the result. * * Since: 2.24 */ gchar * g_variant_print (GVariant *value, gboolean type_annotate) { return g_string_free (g_variant_print_string (value, NULL, type_annotate), FALSE); } /* Hash, Equal, Compare {{{1 */ /** * g_variant_hash: * @value: (type GVariant): a basic #GVariant value as a #gconstpointer * * Generates a hash value for a #GVariant instance. * * The output of this function is guaranteed to be the same for a given * value only per-process. It may change between different processor * architectures or even different versions of GLib. Do not use this * function as a basis for building protocols or file formats. * * The type of @value is #gconstpointer only to allow use of this * function with #GHashTable. @value must be a #GVariant. * * Returns: a hash value corresponding to @value * * Since: 2.24 **/ guint g_variant_hash (gconstpointer value_) { GVariant *value = (GVariant *) value_; switch (g_variant_classify (value)) { case G_VARIANT_CLASS_STRING: case G_VARIANT_CLASS_OBJECT_PATH: case G_VARIANT_CLASS_SIGNATURE: return g_str_hash (g_variant_get_string (value, NULL)); case G_VARIANT_CLASS_BOOLEAN: /* this is a very odd thing to hash... */ return g_variant_get_boolean (value); case G_VARIANT_CLASS_BYTE: return g_variant_get_byte (value); case G_VARIANT_CLASS_INT16: case G_VARIANT_CLASS_UINT16: { const guint16 *ptr; ptr = g_variant_get_data (value); if (ptr) return *ptr; else return 0; } case G_VARIANT_CLASS_INT32: case G_VARIANT_CLASS_UINT32: case G_VARIANT_CLASS_HANDLE: { const guint *ptr; ptr = g_variant_get_data (value); if (ptr) return *ptr; else return 0; } case G_VARIANT_CLASS_INT64: case G_VARIANT_CLASS_UINT64: case G_VARIANT_CLASS_DOUBLE: /* need a separate case for these guys because otherwise * performance could be quite bad on big endian systems */ { const guint *ptr; ptr = g_variant_get_data (value); if (ptr) return ptr[0] + ptr[1]; else return 0; } default: g_return_val_if_fail (!g_variant_is_container (value), 0); g_assert_not_reached (); } } /** * g_variant_equal: * @one: (type GVariant): a #GVariant instance * @two: (type GVariant): a #GVariant instance * * Checks if @one and @two have the same type and value. * * The types of @one and @two are #gconstpointer only to allow use of * this function with #GHashTable. They must each be a #GVariant. * * Returns: %TRUE if @one and @two are equal * * Since: 2.24 **/ gboolean g_variant_equal (gconstpointer one, gconstpointer two) { gboolean equal; g_return_val_if_fail (one != NULL && two != NULL, FALSE); if (g_variant_get_type_info ((GVariant *) one) != g_variant_get_type_info ((GVariant *) two)) return FALSE; /* if both values are trusted to be in their canonical serialized form * then a simple memcmp() of their serialized data will answer the * question. * * if not, then this might generate a false negative (since it is * possible for two different byte sequences to represent the same * value). for now we solve this by pretty-printing both values and * comparing the result. */ if (g_variant_is_trusted ((GVariant *) one) && g_variant_is_trusted ((GVariant *) two)) { gconstpointer data_one, data_two; gsize size_one, size_two; size_one = g_variant_get_size ((GVariant *) one); size_two = g_variant_get_size ((GVariant *) two); if (size_one != size_two) return FALSE; data_one = g_variant_get_data ((GVariant *) one); data_two = g_variant_get_data ((GVariant *) two); if (size_one) equal = memcmp (data_one, data_two, size_one) == 0; else equal = TRUE; } else { gchar *strone, *strtwo; strone = g_variant_print ((GVariant *) one, FALSE); strtwo = g_variant_print ((GVariant *) two, FALSE); equal = strcmp (strone, strtwo) == 0; g_free (strone); g_free (strtwo); } return equal; } /** * g_variant_compare: * @one: (type GVariant): a basic-typed #GVariant instance * @two: (type GVariant): a #GVariant instance of the same type * * Compares @one and @two. * * The types of @one and @two are #gconstpointer only to allow use of * this function with #GTree, #GPtrArray, etc. They must each be a * #GVariant. * * Comparison is only defined for basic types (ie: booleans, numbers, * strings). For booleans, %FALSE is less than %TRUE. Numbers are * ordered in the usual way. Strings are in ASCII lexographical order. * * It is a programmer error to attempt to compare container values or * two values that have types that are not exactly equal. For example, * you cannot compare a 32-bit signed integer with a 32-bit unsigned * integer. Also note that this function is not particularly * well-behaved when it comes to comparison of doubles; in particular, * the handling of incomparable values (ie: NaN) is undefined. * * If you only require an equality comparison, g_variant_equal() is more * general. * * Returns: negative value if a < b; * zero if a = b; * positive value if a > b. * * Since: 2.26 **/ gint g_variant_compare (gconstpointer one, gconstpointer two) { GVariant *a = (GVariant *) one; GVariant *b = (GVariant *) two; g_return_val_if_fail (g_variant_classify (a) == g_variant_classify (b), 0); switch (g_variant_classify (a)) { case G_VARIANT_CLASS_BOOLEAN: return g_variant_get_boolean (a) - g_variant_get_boolean (b); case G_VARIANT_CLASS_BYTE: return ((gint) g_variant_get_byte (a)) - ((gint) g_variant_get_byte (b)); case G_VARIANT_CLASS_INT16: return ((gint) g_variant_get_int16 (a)) - ((gint) g_variant_get_int16 (b)); case G_VARIANT_CLASS_UINT16: return ((gint) g_variant_get_uint16 (a)) - ((gint) g_variant_get_uint16 (b)); case G_VARIANT_CLASS_INT32: { gint32 a_val = g_variant_get_int32 (a); gint32 b_val = g_variant_get_int32 (b); return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1; } case G_VARIANT_CLASS_UINT32: { guint32 a_val = g_variant_get_uint32 (a); guint32 b_val = g_variant_get_uint32 (b); return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1; } case G_VARIANT_CLASS_INT64: { gint64 a_val = g_variant_get_int64 (a); gint64 b_val = g_variant_get_int64 (b); return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1; } case G_VARIANT_CLASS_UINT64: { guint64 a_val = g_variant_get_uint64 (a); guint64 b_val = g_variant_get_uint64 (b); return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1; } case G_VARIANT_CLASS_DOUBLE: { gdouble a_val = g_variant_get_double (a); gdouble b_val = g_variant_get_double (b); return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1; } case G_VARIANT_CLASS_STRING: case G_VARIANT_CLASS_OBJECT_PATH: case G_VARIANT_CLASS_SIGNATURE: return strcmp (g_variant_get_string (a, NULL), g_variant_get_string (b, NULL)); default: g_return_val_if_fail (!g_variant_is_container (a), 0); g_assert_not_reached (); } } /* GVariantIter {{{1 */ /** * GVariantIter: (skip) * * #GVariantIter is an opaque data structure and can only be accessed * using the following functions. **/ struct stack_iter { GVariant *value; gssize n, i; const gchar *loop_format; gsize padding[3]; gsize magic; }; G_STATIC_ASSERT (sizeof (struct stack_iter) <= sizeof (GVariantIter)); struct heap_iter { struct stack_iter iter; GVariant *value_ref; gsize magic; }; G_STATIC_ASSERT (sizeof (struct heap_iter) <= sizeof (GVariantIter)); #define GVSI(i) ((struct stack_iter *) (i)) #define GVHI(i) ((struct heap_iter *) (i)) #define GVSI_MAGIC ((gsize) 3579507750u) #define GVHI_MAGIC ((gsize) 1450270775u) #define is_valid_iter(i) (i != NULL && \ GVSI(i)->magic == GVSI_MAGIC) #define is_valid_heap_iter(i) (is_valid_iter(i) && \ GVHI(i)->magic == GVHI_MAGIC) /** * g_variant_iter_new: * @value: a container #GVariant * * Creates a heap-allocated #GVariantIter for iterating over the items * in @value. * * Use g_variant_iter_free() to free the return value when you no longer * need it. * * A reference is taken to @value and will be released only when * g_variant_iter_free() is called. * * Returns: (transfer full): a new heap-allocated #GVariantIter * * Since: 2.24 **/ GVariantIter * g_variant_iter_new (GVariant *value) { GVariantIter *iter; iter = (GVariantIter *) g_slice_new (struct heap_iter); GVHI(iter)->value_ref = g_variant_ref (value); GVHI(iter)->magic = GVHI_MAGIC; g_variant_iter_init (iter, value); return iter; } /** * g_variant_iter_init: (skip) * @iter: a pointer to a #GVariantIter * @value: a container #GVariant * * Initialises (without allocating) a #GVariantIter. @iter may be * completely uninitialised prior to this call; its old value is * ignored. * * The iterator remains valid for as long as @value exists, and need not * be freed in any way. * * Returns: the number of items in @value * * Since: 2.24 **/ gsize g_variant_iter_init (GVariantIter *iter, GVariant *value) { GVSI(iter)->magic = GVSI_MAGIC; GVSI(iter)->value = value; GVSI(iter)->n = g_variant_n_children (value); GVSI(iter)->i = -1; GVSI(iter)->loop_format = NULL; return GVSI(iter)->n; } /** * g_variant_iter_copy: * @iter: a #GVariantIter * * Creates a new heap-allocated #GVariantIter to iterate over the * container that was being iterated over by @iter. Iteration begins on * the new iterator from the current position of the old iterator but * the two copies are independent past that point. * * Use g_variant_iter_free() to free the return value when you no longer * need it. * * A reference is taken to the container that @iter is iterating over * and will be related only when g_variant_iter_free() is called. * * Returns: (transfer full): a new heap-allocated #GVariantIter * * Since: 2.24 **/ GVariantIter * g_variant_iter_copy (GVariantIter *iter) { GVariantIter *copy; g_return_val_if_fail (is_valid_iter (iter), 0); copy = g_variant_iter_new (GVSI(iter)->value); GVSI(copy)->i = GVSI(iter)->i; return copy; } /** * g_variant_iter_n_children: * @iter: a #GVariantIter * * Queries the number of child items in the container that we are * iterating over. This is the total number of items -- not the number * of items remaining. * * This function might be useful for preallocation of arrays. * * Returns: the number of children in the container * * Since: 2.24 **/ gsize g_variant_iter_n_children (GVariantIter *iter) { g_return_val_if_fail (is_valid_iter (iter), 0); return GVSI(iter)->n; } /** * g_variant_iter_free: * @iter: (transfer full): a heap-allocated #GVariantIter * * Frees a heap-allocated #GVariantIter. Only call this function on * iterators that were returned by g_variant_iter_new() or * g_variant_iter_copy(). * * Since: 2.24 **/ void g_variant_iter_free (GVariantIter *iter) { g_return_if_fail (is_valid_heap_iter (iter)); g_variant_unref (GVHI(iter)->value_ref); GVHI(iter)->magic = 0; g_slice_free (struct heap_iter, GVHI(iter)); } /** * g_variant_iter_next_value: * @iter: a #GVariantIter * * Gets the next item in the container. If no more items remain then * %NULL is returned. * * Use g_variant_unref() to drop your reference on the return value when * you no longer need it. * * Here is an example for iterating with g_variant_iter_next_value(): * |[ * // recursively iterate a container * void * iterate_container_recursive (GVariant *container) * { * GVariantIter iter; * GVariant *child; * * g_variant_iter_init (&iter, container); * while ((child = g_variant_iter_next_value (&iter))) * { * g_print ("type '%s'\n", g_variant_get_type_string (child)); * * if (g_variant_is_container (child)) * iterate_container_recursive (child); * * g_variant_unref (child); * } * } * ]| * * Returns: (nullable) (transfer full): a #GVariant, or %NULL * * Since: 2.24 **/ GVariant * g_variant_iter_next_value (GVariantIter *iter) { g_return_val_if_fail (is_valid_iter (iter), FALSE); if G_UNLIKELY (GVSI(iter)->i >= GVSI(iter)->n) { g_critical ("g_variant_iter_next_value: must not be called again " "after NULL has already been returned."); return NULL; } GVSI(iter)->i++; if (GVSI(iter)->i < GVSI(iter)->n) return g_variant_get_child_value (GVSI(iter)->value, GVSI(iter)->i); return NULL; } /* GVariantBuilder {{{1 */ /** * GVariantBuilder: * * A utility type for constructing container-type #GVariant instances. * * This is an opaque structure and may only be accessed using the * following functions. * * #GVariantBuilder is not threadsafe in any way. Do not attempt to * access it from more than one thread. **/ struct stack_builder { GVariantBuilder *parent; GVariantType *type; /* type constraint explicitly specified by 'type'. * for tuple types, this moves along as we add more items. */ const GVariantType *expected_type; /* type constraint implied by previous array item. */ const GVariantType *prev_item_type; /* constraints on the number of children. max = -1 for unlimited. */ gsize min_items; gsize max_items; /* dynamically-growing pointer array */ GVariant **children; gsize allocated_children; gsize offset; /* set to '1' if all items in the container will have the same type * (ie: maybe, array, variant) '0' if not (ie: tuple, dict entry) */ guint uniform_item_types : 1; /* set to '1' initially and changed to '0' if an untrusted value is * added */ guint trusted : 1; gsize magic; }; G_STATIC_ASSERT (sizeof (struct stack_builder) <= sizeof (GVariantBuilder)); struct heap_builder { GVariantBuilder builder; gsize magic; gint ref_count; }; #define GVSB(b) ((struct stack_builder *) (b)) #define GVHB(b) ((struct heap_builder *) (b)) #define GVSB_MAGIC ((gsize) 1033660112u) #define GVSB_MAGIC_PARTIAL ((gsize) 2942751021u) #define GVHB_MAGIC ((gsize) 3087242682u) #define is_valid_builder(b) (GVSB(b)->magic == GVSB_MAGIC) #define is_valid_heap_builder(b) (GVHB(b)->magic == GVHB_MAGIC) /* Just to make sure that by adding a union to GVariantBuilder, we * didn't accidentally change ABI. */ G_STATIC_ASSERT (sizeof (GVariantBuilder) == sizeof (guintptr[16])); static gboolean ensure_valid_builder (GVariantBuilder *builder) { if (builder == NULL) return FALSE; else if (is_valid_builder (builder)) return TRUE; if (builder->u.s.partial_magic == GVSB_MAGIC_PARTIAL) { static GVariantBuilder cleared_builder; /* Make sure that only first two fields were set and the rest is * zeroed to avoid messing up the builder that had parent * address equal to GVSB_MAGIC_PARTIAL. */ if (memcmp (cleared_builder.u.s.y, builder->u.s.y, sizeof cleared_builder.u.s.y)) return FALSE; g_variant_builder_init (builder, builder->u.s.type); } return is_valid_builder (builder); } /* return_if_invalid_builder (b) is like * g_return_if_fail (ensure_valid_builder (b)), except that * the side effects of ensure_valid_builder are evaluated * regardless of whether G_DISABLE_CHECKS is defined or not. */ #define return_if_invalid_builder(b) G_STMT_START { \ gboolean valid_builder G_GNUC_UNUSED = ensure_valid_builder (b); \ g_return_if_fail (valid_builder); \ } G_STMT_END /* return_val_if_invalid_builder (b, val) is like * g_return_val_if_fail (ensure_valid_builder (b), val), except that * the side effects of ensure_valid_builder are evaluated * regardless of whether G_DISABLE_CHECKS is defined or not. */ #define return_val_if_invalid_builder(b, val) G_STMT_START { \ gboolean valid_builder G_GNUC_UNUSED = ensure_valid_builder (b); \ g_return_val_if_fail (valid_builder, val); \ } G_STMT_END /** * g_variant_builder_new: * @type: a container type * * Allocates and initialises a new #GVariantBuilder. * * You should call g_variant_builder_unref() on the return value when it * is no longer needed. The memory will not be automatically freed by * any other call. * * In most cases it is easier to place a #GVariantBuilder directly on * the stack of the calling function and initialise it with * g_variant_builder_init(). * * Returns: (transfer full): a #GVariantBuilder * * Since: 2.24 **/ GVariantBuilder * g_variant_builder_new (const GVariantType *type) { GVariantBuilder *builder; builder = (GVariantBuilder *) g_slice_new (struct heap_builder); g_variant_builder_init (builder, type); GVHB(builder)->magic = GVHB_MAGIC; GVHB(builder)->ref_count = 1; return builder; } /** * g_variant_builder_unref: * @builder: (transfer full): a #GVariantBuilder allocated by g_variant_builder_new() * * Decreases the reference count on @builder. * * In the event that there are no more references, releases all memory * associated with the #GVariantBuilder. * * Don't call this on stack-allocated #GVariantBuilder instances or bad * things will happen. * * Since: 2.24 **/ void g_variant_builder_unref (GVariantBuilder *builder) { g_return_if_fail (is_valid_heap_builder (builder)); if (--GVHB(builder)->ref_count) return; g_variant_builder_clear (builder); GVHB(builder)->magic = 0; g_slice_free (struct heap_builder, GVHB(builder)); } /** * g_variant_builder_ref: * @builder: a #GVariantBuilder allocated by g_variant_builder_new() * * Increases the reference count on @builder. * * Don't call this on stack-allocated #GVariantBuilder instances or bad * things will happen. * * Returns: (transfer full): a new reference to @builder * * Since: 2.24 **/ GVariantBuilder * g_variant_builder_ref (GVariantBuilder *builder) { g_return_val_if_fail (is_valid_heap_builder (builder), NULL); GVHB(builder)->ref_count++; return builder; } /** * g_variant_builder_clear: (skip) * @builder: a #GVariantBuilder * * Releases all memory associated with a #GVariantBuilder without * freeing the #GVariantBuilder structure itself. * * It typically only makes sense to do this on a stack-allocated * #GVariantBuilder if you want to abort building the value part-way * through. This function need not be called if you call * g_variant_builder_end() and it also doesn't need to be called on * builders allocated with g_variant_builder_new() (see * g_variant_builder_unref() for that). * * This function leaves the #GVariantBuilder structure set to all-zeros. * It is valid to call this function on either an initialised * #GVariantBuilder or one that is set to all-zeros but it is not valid * to call this function on uninitialised memory. * * Since: 2.24 **/ void g_variant_builder_clear (GVariantBuilder *builder) { gsize i; if (GVSB(builder)->magic == 0) /* all-zeros or partial case */ return; return_if_invalid_builder (builder); g_variant_type_free (GVSB(builder)->type); for (i = 0; i < GVSB(builder)->offset; i++) g_variant_unref (GVSB(builder)->children[i]); g_free (GVSB(builder)->children); if (GVSB(builder)->parent) { g_variant_builder_clear (GVSB(builder)->parent); g_slice_free (GVariantBuilder, GVSB(builder)->parent); } memset (builder, 0, sizeof (GVariantBuilder)); } /** * g_variant_builder_init: (skip) * @builder: a #GVariantBuilder * @type: a container type * * Initialises a #GVariantBuilder structure. * * @type must be non-%NULL. It specifies the type of container to * construct. It can be an indefinite type such as * %G_VARIANT_TYPE_ARRAY or a definite type such as "as" or "(ii)". * Maybe, array, tuple, dictionary entry and variant-typed values may be * constructed. * * After the builder is initialised, values are added using * g_variant_builder_add_value() or g_variant_builder_add(). * * After all the child values are added, g_variant_builder_end() frees * the memory associated with the builder and returns the #GVariant that * was created. * * This function completely ignores the previous contents of @builder. * On one hand this means that it is valid to pass in completely * uninitialised memory. On the other hand, this means that if you are * initialising over top of an existing #GVariantBuilder you need to * first call g_variant_builder_clear() in order to avoid leaking * memory. * * You must not call g_variant_builder_ref() or * g_variant_builder_unref() on a #GVariantBuilder that was initialised * with this function. If you ever pass a reference to a * #GVariantBuilder outside of the control of your own code then you * should assume that the person receiving that reference may try to use * reference counting; you should use g_variant_builder_new() instead of * this function. * * Since: 2.24 **/ void g_variant_builder_init (GVariantBuilder *builder, const GVariantType *type) { g_return_if_fail (type != NULL); g_return_if_fail (g_variant_type_is_container (type)); memset (builder, 0, sizeof (GVariantBuilder)); GVSB(builder)->type = g_variant_type_copy (type); GVSB(builder)->magic = GVSB_MAGIC; GVSB(builder)->trusted = TRUE; switch (*(const gchar *) type) { case G_VARIANT_CLASS_VARIANT: GVSB(builder)->uniform_item_types = TRUE; GVSB(builder)->allocated_children = 1; GVSB(builder)->expected_type = NULL; GVSB(builder)->min_items = 1; GVSB(builder)->max_items = 1; break; case G_VARIANT_CLASS_ARRAY: GVSB(builder)->uniform_item_types = TRUE; GVSB(builder)->allocated_children = 8; GVSB(builder)->expected_type = g_variant_type_element (GVSB(builder)->type); GVSB(builder)->min_items = 0; GVSB(builder)->max_items = -1; break; case G_VARIANT_CLASS_MAYBE: GVSB(builder)->uniform_item_types = TRUE; GVSB(builder)->allocated_children = 1; GVSB(builder)->expected_type = g_variant_type_element (GVSB(builder)->type); GVSB(builder)->min_items = 0; GVSB(builder)->max_items = 1; break; case G_VARIANT_CLASS_DICT_ENTRY: GVSB(builder)->uniform_item_types = FALSE; GVSB(builder)->allocated_children = 2; GVSB(builder)->expected_type = g_variant_type_key (GVSB(builder)->type); GVSB(builder)->min_items = 2; GVSB(builder)->max_items = 2; break; case 'r': /* G_VARIANT_TYPE_TUPLE was given */ GVSB(builder)->uniform_item_types = FALSE; GVSB(builder)->allocated_children = 8; GVSB(builder)->expected_type = NULL; GVSB(builder)->min_items = 0; GVSB(builder)->max_items = -1; break; case G_VARIANT_CLASS_TUPLE: /* a definite tuple type was given */ GVSB(builder)->allocated_children = g_variant_type_n_items (type); GVSB(builder)->expected_type = g_variant_type_first (GVSB(builder)->type); GVSB(builder)->min_items = GVSB(builder)->allocated_children; GVSB(builder)->max_items = GVSB(builder)->allocated_children; GVSB(builder)->uniform_item_types = FALSE; break; default: g_assert_not_reached (); } #ifdef G_ANALYZER_ANALYZING /* Static analysers can’t couple the code in g_variant_builder_init() to the * code in g_variant_builder_end() by GVariantType, so end up assuming that * @offset and @children mismatch and that uninitialised memory is accessed * from @children. At runtime, this is caught by the preconditions at the top * of g_variant_builder_end(). Help the analyser by zero-initialising the * memory to avoid a false positive. */ GVSB(builder)->children = g_new0 (GVariant *, GVSB(builder)->allocated_children); #else GVSB(builder)->children = g_new (GVariant *, GVSB(builder)->allocated_children); #endif } static void g_variant_builder_make_room (struct stack_builder *builder) { if (builder->offset == builder->allocated_children) { builder->allocated_children *= 2; builder->children = g_renew (GVariant *, builder->children, builder->allocated_children); } } /** * g_variant_builder_add_value: * @builder: a #GVariantBuilder * @value: a #GVariant * * Adds @value to @builder. * * It is an error to call this function in any way that would create an * inconsistent value to be constructed. Some examples of this are * putting different types of items into an array, putting the wrong * types or number of items in a tuple, putting more than one value into * a variant, etc. * * If @value is a floating reference (see g_variant_ref_sink()), * the @builder instance takes ownership of @value. * * Since: 2.24 **/ void g_variant_builder_add_value (GVariantBuilder *builder, GVariant *value) { return_if_invalid_builder (builder); g_return_if_fail (GVSB(builder)->offset < GVSB(builder)->max_items); g_return_if_fail (!GVSB(builder)->expected_type || g_variant_is_of_type (value, GVSB(builder)->expected_type)); g_return_if_fail (!GVSB(builder)->prev_item_type || g_variant_is_of_type (value, GVSB(builder)->prev_item_type)); GVSB(builder)->trusted &= g_variant_is_trusted (value); if (!GVSB(builder)->uniform_item_types) { /* advance our expected type pointers */ if (GVSB(builder)->expected_type) GVSB(builder)->expected_type = g_variant_type_next (GVSB(builder)->expected_type); if (GVSB(builder)->prev_item_type) GVSB(builder)->prev_item_type = g_variant_type_next (GVSB(builder)->prev_item_type); } else GVSB(builder)->prev_item_type = g_variant_get_type (value); g_variant_builder_make_room (GVSB(builder)); GVSB(builder)->children[GVSB(builder)->offset++] = g_variant_ref_sink (value); } /** * g_variant_builder_open: * @builder: a #GVariantBuilder * @type: the #GVariantType of the container * * Opens a subcontainer inside the given @builder. When done adding * items to the subcontainer, g_variant_builder_close() must be called. @type * is the type of the container: so to build a tuple of several values, @type * must include the tuple itself. * * It is an error to call this function in any way that would cause an * inconsistent value to be constructed (ie: adding too many values or * a value of an incorrect type). * * Example of building a nested variant: * |[ * GVariantBuilder builder; * guint32 some_number = get_number (); * g_autoptr (GHashTable) some_dict = get_dict (); * GHashTableIter iter; * const gchar *key; * const GVariant *value; * g_autoptr (GVariant) output = NULL; * * g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ua{sv})")); * g_variant_builder_add (&builder, "u", some_number); * g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}")); * * g_hash_table_iter_init (&iter, some_dict); * while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) * { * g_variant_builder_open (&builder, G_VARIANT_TYPE ("{sv}")); * g_variant_builder_add (&builder, "s", key); * g_variant_builder_add (&builder, "v", value); * g_variant_builder_close (&builder); * } * * g_variant_builder_close (&builder); * * output = g_variant_builder_end (&builder); * ]| * * Since: 2.24 **/ void g_variant_builder_open (GVariantBuilder *builder, const GVariantType *type) { GVariantBuilder *parent; return_if_invalid_builder (builder); g_return_if_fail (GVSB(builder)->offset < GVSB(builder)->max_items); g_return_if_fail (!GVSB(builder)->expected_type || g_variant_type_is_subtype_of (type, GVSB(builder)->expected_type)); g_return_if_fail (!GVSB(builder)->prev_item_type || g_variant_type_is_subtype_of (GVSB(builder)->prev_item_type, type)); parent = g_slice_dup (GVariantBuilder, builder); g_variant_builder_init (builder, type); GVSB(builder)->parent = parent; /* push the prev_item_type down into the subcontainer */ if (GVSB(parent)->prev_item_type) { if (!GVSB(builder)->uniform_item_types) /* tuples and dict entries */ GVSB(builder)->prev_item_type = g_variant_type_first (GVSB(parent)->prev_item_type); else if (!g_variant_type_is_variant (GVSB(builder)->type)) /* maybes and arrays */ GVSB(builder)->prev_item_type = g_variant_type_element (GVSB(parent)->prev_item_type); } } /** * g_variant_builder_close: * @builder: a #GVariantBuilder * * Closes the subcontainer inside the given @builder that was opened by * the most recent call to g_variant_builder_open(). * * It is an error to call this function in any way that would create an * inconsistent value to be constructed (ie: too few values added to the * subcontainer). * * Since: 2.24 **/ void g_variant_builder_close (GVariantBuilder *builder) { GVariantBuilder *parent; return_if_invalid_builder (builder); g_return_if_fail (GVSB(builder)->parent != NULL); parent = GVSB(builder)->parent; GVSB(builder)->parent = NULL; g_variant_builder_add_value (parent, g_variant_builder_end (builder)); *builder = *parent; g_slice_free (GVariantBuilder, parent); } /*< private > * g_variant_make_maybe_type: * @element: a #GVariant * * Return the type of a maybe containing @element. */ static GVariantType * g_variant_make_maybe_type (GVariant *element) { return g_variant_type_new_maybe (g_variant_get_type (element)); } /*< private > * g_variant_make_array_type: * @element: a #GVariant * * Return the type of an array containing @element. */ static GVariantType * g_variant_make_array_type (GVariant *element) { return g_variant_type_new_array (g_variant_get_type (element)); } /** * g_variant_builder_end: * @builder: a #GVariantBuilder * * Ends the builder process and returns the constructed value. * * It is not permissible to use @builder in any way after this call * except for reference counting operations (in the case of a * heap-allocated #GVariantBuilder) or by reinitialising it with * g_variant_builder_init() (in the case of stack-allocated). This * means that for the stack-allocated builders there is no need to * call g_variant_builder_clear() after the call to * g_variant_builder_end(). * * It is an error to call this function in any way that would create an * inconsistent value to be constructed (ie: insufficient number of * items added to a container with a specific number of children * required). It is also an error to call this function if the builder * was created with an indefinite array or maybe type and no children * have been added; in this case it is impossible to infer the type of * the empty array. * * Returns: (transfer none): a new, floating, #GVariant * * Since: 2.24 **/ GVariant * g_variant_builder_end (GVariantBuilder *builder) { GVariantType *my_type; GVariant *value; return_val_if_invalid_builder (builder, NULL); g_return_val_if_fail (GVSB(builder)->offset >= GVSB(builder)->min_items, NULL); g_return_val_if_fail (!GVSB(builder)->uniform_item_types || GVSB(builder)->prev_item_type != NULL || g_variant_type_is_definite (GVSB(builder)->type), NULL); if (g_variant_type_is_definite (GVSB(builder)->type)) my_type = g_variant_type_copy (GVSB(builder)->type); else if (g_variant_type_is_maybe (GVSB(builder)->type)) my_type = g_variant_make_maybe_type (GVSB(builder)->children[0]); else if (g_variant_type_is_array (GVSB(builder)->type)) my_type = g_variant_make_array_type (GVSB(builder)->children[0]); else if (g_variant_type_is_tuple (GVSB(builder)->type)) my_type = g_variant_make_tuple_type (GVSB(builder)->children, GVSB(builder)->offset); else if (g_variant_type_is_dict_entry (GVSB(builder)->type)) my_type = g_variant_make_dict_entry_type (GVSB(builder)->children[0], GVSB(builder)->children[1]); else g_assert_not_reached (); value = g_variant_new_from_children (my_type, g_renew (GVariant *, GVSB(builder)->children, GVSB(builder)->offset), GVSB(builder)->offset, GVSB(builder)->trusted); GVSB(builder)->children = NULL; GVSB(builder)->offset = 0; g_variant_builder_clear (builder); g_variant_type_free (my_type); return value; } /* GVariantDict {{{1 */ /** * GVariantDict: * * #GVariantDict is a mutable interface to #GVariant dictionaries. * * It can be used for doing a sequence of dictionary lookups in an * efficient way on an existing #GVariant dictionary or it can be used * to construct new dictionaries with a hashtable-like interface. It * can also be used for taking existing dictionaries and modifying them * in order to create new ones. * * #GVariantDict can only be used with %G_VARIANT_TYPE_VARDICT * dictionaries. * * It is possible to use #GVariantDict allocated on the stack or on the * heap. When using a stack-allocated #GVariantDict, you begin with a * call to g_variant_dict_init() and free the resources with a call to * g_variant_dict_clear(). * * Heap-allocated #GVariantDict follows normal refcounting rules: you * allocate it with g_variant_dict_new() and use g_variant_dict_ref() * and g_variant_dict_unref(). * * g_variant_dict_end() is used to convert the #GVariantDict back into a * dictionary-type #GVariant. When used with stack-allocated instances, * this also implicitly frees all associated memory, but for * heap-allocated instances, you must still call g_variant_dict_unref() * afterwards. * * You will typically want to use a heap-allocated #GVariantDict when * you expose it as part of an API. For most other uses, the * stack-allocated form will be more convenient. * * Consider the following two examples that do the same thing in each * style: take an existing dictionary and look up the "count" uint32 * key, adding 1 to it if it is found, or returning an error if the * key is not found. Each returns the new dictionary as a floating * #GVariant. * * ## Using a stack-allocated GVariantDict * * |[ * GVariant * * add_to_count (GVariant *orig, * GError **error) * { * GVariantDict dict; * guint32 count; * * g_variant_dict_init (&dict, orig); * if (!g_variant_dict_lookup (&dict, "count", "u", &count)) * { * g_set_error (...); * g_variant_dict_clear (&dict); * return NULL; * } * * g_variant_dict_insert (&dict, "count", "u", count + 1); * * return g_variant_dict_end (&dict); * } * ]| * * ## Using heap-allocated GVariantDict * * |[ * GVariant * * add_to_count (GVariant *orig, * GError **error) * { * GVariantDict *dict; * GVariant *result; * guint32 count; * * dict = g_variant_dict_new (orig); * * if (g_variant_dict_lookup (dict, "count", "u", &count)) * { * g_variant_dict_insert (dict, "count", "u", count + 1); * result = g_variant_dict_end (dict); * } * else * { * g_set_error (...); * result = NULL; * } * * g_variant_dict_unref (dict); * * return result; * } * ]| * * Since: 2.40 **/ struct stack_dict { GHashTable *values; gsize magic; }; G_STATIC_ASSERT (sizeof (struct stack_dict) <= sizeof (GVariantDict)); struct heap_dict { struct stack_dict dict; gint ref_count; gsize magic; }; #define GVSD(d) ((struct stack_dict *) (d)) #define GVHD(d) ((struct heap_dict *) (d)) #define GVSD_MAGIC ((gsize) 2579507750u) #define GVSD_MAGIC_PARTIAL ((gsize) 3488698669u) #define GVHD_MAGIC ((gsize) 2450270775u) #define is_valid_dict(d) (GVSD(d)->magic == GVSD_MAGIC) #define is_valid_heap_dict(d) (GVHD(d)->magic == GVHD_MAGIC) /* Just to make sure that by adding a union to GVariantDict, we didn't * accidentally change ABI. */ G_STATIC_ASSERT (sizeof (GVariantDict) == sizeof (guintptr[16])); static gboolean ensure_valid_dict (GVariantDict *dict) { if (dict == NULL) return FALSE; else if (is_valid_dict (dict)) return TRUE; if (dict->u.s.partial_magic == GVSD_MAGIC_PARTIAL) { static GVariantDict cleared_dict; /* Make sure that only first two fields were set and the rest is * zeroed to avoid messing up the builder that had parent * address equal to GVSB_MAGIC_PARTIAL. */ if (memcmp (cleared_dict.u.s.y, dict->u.s.y, sizeof cleared_dict.u.s.y)) return FALSE; g_variant_dict_init (dict, dict->u.s.asv); } return is_valid_dict (dict); } /* return_if_invalid_dict (d) is like * g_return_if_fail (ensure_valid_dict (d)), except that * the side effects of ensure_valid_dict are evaluated * regardless of whether G_DISABLE_CHECKS is defined or not. */ #define return_if_invalid_dict(d) G_STMT_START { \ gboolean valid_dict G_GNUC_UNUSED = ensure_valid_dict (d); \ g_return_if_fail (valid_dict); \ } G_STMT_END /* return_val_if_invalid_dict (d, val) is like * g_return_val_if_fail (ensure_valid_dict (d), val), except that * the side effects of ensure_valid_dict are evaluated * regardless of whether G_DISABLE_CHECKS is defined or not. */ #define return_val_if_invalid_dict(d, val) G_STMT_START { \ gboolean valid_dict G_GNUC_UNUSED = ensure_valid_dict (d); \ g_return_val_if_fail (valid_dict, val); \ } G_STMT_END /** * g_variant_dict_new: * @from_asv: (nullable): the #GVariant with which to initialise the * dictionary * * Allocates and initialises a new #GVariantDict. * * You should call g_variant_dict_unref() on the return value when it * is no longer needed. The memory will not be automatically freed by * any other call. * * In some cases it may be easier to place a #GVariantDict directly on * the stack of the calling function and initialise it with * g_variant_dict_init(). This is particularly useful when you are * using #GVariantDict to construct a #GVariant. * * Returns: (transfer full): a #GVariantDict * * Since: 2.40 **/ GVariantDict * g_variant_dict_new (GVariant *from_asv) { GVariantDict *dict; dict = g_slice_alloc (sizeof (struct heap_dict)); g_variant_dict_init (dict, from_asv); GVHD(dict)->magic = GVHD_MAGIC; GVHD(dict)->ref_count = 1; return dict; } /** * g_variant_dict_init: (skip) * @dict: a #GVariantDict * @from_asv: (nullable): the initial value for @dict * * Initialises a #GVariantDict structure. * * If @from_asv is given, it is used to initialise the dictionary. * * This function completely ignores the previous contents of @dict. On * one hand this means that it is valid to pass in completely * uninitialised memory. On the other hand, this means that if you are * initialising over top of an existing #GVariantDict you need to first * call g_variant_dict_clear() in order to avoid leaking memory. * * You must not call g_variant_dict_ref() or g_variant_dict_unref() on a * #GVariantDict that was initialised with this function. If you ever * pass a reference to a #GVariantDict outside of the control of your * own code then you should assume that the person receiving that * reference may try to use reference counting; you should use * g_variant_dict_new() instead of this function. * * Since: 2.40 **/ void g_variant_dict_init (GVariantDict *dict, GVariant *from_asv) { GVariantIter iter; gchar *key; GVariant *value; GVSD(dict)->values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); GVSD(dict)->magic = GVSD_MAGIC; if (from_asv) { g_variant_iter_init (&iter, from_asv); while (g_variant_iter_next (&iter, "{sv}", &key, &value)) g_hash_table_insert (GVSD(dict)->values, key, value); } } /** * g_variant_dict_lookup: * @dict: a #GVariantDict * @key: the key to look up in the dictionary * @format_string: a GVariant format string * @...: the arguments to unpack the value into * * Looks up a value in a #GVariantDict. * * This function is a wrapper around g_variant_dict_lookup_value() and * g_variant_get(). In the case that %NULL would have been returned, * this function returns %FALSE and does not modify the values of the arguments * passed in to @.... Otherwise, it unpacks the returned * value and returns %TRUE. * * @format_string determines the C types that are used for unpacking the * values and also determines if the values are copied or borrowed, see the * section on [`GVariant` format strings](gvariant-format-strings.html#pointers). * * Returns: %TRUE if a value was unpacked * * Since: 2.40 **/ gboolean g_variant_dict_lookup (GVariantDict *dict, const gchar *key, const gchar *format_string, ...) { GVariant *value; va_list ap; return_val_if_invalid_dict (dict, FALSE); g_return_val_if_fail (key != NULL, FALSE); g_return_val_if_fail (format_string != NULL, FALSE); value = g_hash_table_lookup (GVSD(dict)->values, key); if (value == NULL || !g_variant_check_format_string (value, format_string, FALSE)) return FALSE; va_start (ap, format_string); g_variant_get_va (value, format_string, NULL, &ap); va_end (ap); return TRUE; } /** * g_variant_dict_lookup_value: * @dict: a #GVariantDict * @key: the key to look up in the dictionary * @expected_type: (nullable): a #GVariantType, or %NULL * * Looks up a value in a #GVariantDict. * * If @key is not found in @dictionary, %NULL is returned. * * The @expected_type string specifies what type of value is expected. * If the value associated with @key has a different type then %NULL is * returned. * * If the key is found and the value has the correct type, it is * returned. If @expected_type was specified then any non-%NULL return * value will have this type. * * Returns: (transfer full) (nullable): the value of the dictionary key, or %NULL * * Since: 2.40 **/ GVariant * g_variant_dict_lookup_value (GVariantDict *dict, const gchar *key, const GVariantType *expected_type) { GVariant *result; return_val_if_invalid_dict (dict, NULL); g_return_val_if_fail (key != NULL, NULL); result = g_hash_table_lookup (GVSD(dict)->values, key); if (result && (!expected_type || g_variant_is_of_type (result, expected_type))) return g_variant_ref (result); return NULL; } /** * g_variant_dict_contains: * @dict: a #GVariantDict * @key: the key to look up in the dictionary * * Checks if @key exists in @dict. * * Returns: %TRUE if @key is in @dict * * Since: 2.40 **/ gboolean g_variant_dict_contains (GVariantDict *dict, const gchar *key) { return_val_if_invalid_dict (dict, FALSE); g_return_val_if_fail (key != NULL, FALSE); return g_hash_table_contains (GVSD(dict)->values, key); } /** * g_variant_dict_insert: * @dict: a #GVariantDict * @key: the key to insert a value for * @format_string: a #GVariant varargs format string * @...: arguments, as per @format_string * * Inserts a value into a #GVariantDict. * * This call is a convenience wrapper that is exactly equivalent to * calling g_variant_new() followed by g_variant_dict_insert_value(). * * Since: 2.40 **/ void g_variant_dict_insert (GVariantDict *dict, const gchar *key, const gchar *format_string, ...) { va_list ap; return_if_invalid_dict (dict); g_return_if_fail (key != NULL); g_return_if_fail (format_string != NULL); va_start (ap, format_string); g_variant_dict_insert_value (dict, key, g_variant_new_va (format_string, NULL, &ap)); va_end (ap); } /** * g_variant_dict_insert_value: * @dict: a #GVariantDict * @key: the key to insert a value for * @value: the value to insert * * Inserts (or replaces) a key in a #GVariantDict. * * @value is consumed if it is floating. * * Since: 2.40 **/ void g_variant_dict_insert_value (GVariantDict *dict, const gchar *key, GVariant *value) { return_if_invalid_dict (dict); g_return_if_fail (key != NULL); g_return_if_fail (value != NULL); g_hash_table_insert (GVSD(dict)->values, g_strdup (key), g_variant_ref_sink (value)); } /** * g_variant_dict_remove: * @dict: a #GVariantDict * @key: the key to remove * * Removes a key and its associated value from a #GVariantDict. * * Returns: %TRUE if the key was found and removed * * Since: 2.40 **/ gboolean g_variant_dict_remove (GVariantDict *dict, const gchar *key) { return_val_if_invalid_dict (dict, FALSE); g_return_val_if_fail (key != NULL, FALSE); return g_hash_table_remove (GVSD(dict)->values, key); } /** * g_variant_dict_clear: * @dict: a #GVariantDict * * Releases all memory associated with a #GVariantDict without freeing * the #GVariantDict structure itself. * * It typically only makes sense to do this on a stack-allocated * #GVariantDict if you want to abort building the value part-way * through. This function need not be called if you call * g_variant_dict_end() and it also doesn't need to be called on dicts * allocated with g_variant_dict_new (see g_variant_dict_unref() for * that). * * It is valid to call this function on either an initialised * #GVariantDict or one that was previously cleared by an earlier call * to g_variant_dict_clear() but it is not valid to call this function * on uninitialised memory. * * Since: 2.40 **/ void g_variant_dict_clear (GVariantDict *dict) { if (GVSD(dict)->magic == 0) /* all-zeros case */ return; return_if_invalid_dict (dict); g_hash_table_unref (GVSD(dict)->values); GVSD(dict)->values = NULL; GVSD(dict)->magic = 0; } /** * g_variant_dict_end: * @dict: a #GVariantDict * * Returns the current value of @dict as a #GVariant of type * %G_VARIANT_TYPE_VARDICT, clearing it in the process. * * It is not permissible to use @dict in any way after this call except * for reference counting operations (in the case of a heap-allocated * #GVariantDict) or by reinitialising it with g_variant_dict_init() (in * the case of stack-allocated). * * Returns: (transfer none): a new, floating, #GVariant * * Since: 2.40 **/ GVariant * g_variant_dict_end (GVariantDict *dict) { GVariantBuilder builder; GHashTableIter iter; gpointer key, value; return_val_if_invalid_dict (dict, NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_hash_table_iter_init (&iter, GVSD(dict)->values); while (g_hash_table_iter_next (&iter, &key, &value)) g_variant_builder_add (&builder, "{sv}", (const gchar *) key, (GVariant *) value); g_variant_dict_clear (dict); return g_variant_builder_end (&builder); } /** * g_variant_dict_ref: * @dict: a heap-allocated #GVariantDict * * Increases the reference count on @dict. * * Don't call this on stack-allocated #GVariantDict instances or bad * things will happen. * * Returns: (transfer full): a new reference to @dict * * Since: 2.40 **/ GVariantDict * g_variant_dict_ref (GVariantDict *dict) { g_return_val_if_fail (is_valid_heap_dict (dict), NULL); GVHD(dict)->ref_count++; return dict; } /** * g_variant_dict_unref: * @dict: (transfer full): a heap-allocated #GVariantDict * * Decreases the reference count on @dict. * * In the event that there are no more references, releases all memory * associated with the #GVariantDict. * * Don't call this on stack-allocated #GVariantDict instances or bad * things will happen. * * Since: 2.40 **/ void g_variant_dict_unref (GVariantDict *dict) { g_return_if_fail (is_valid_heap_dict (dict)); if (--GVHD(dict)->ref_count == 0) { g_variant_dict_clear (dict); g_slice_free (struct heap_dict, (struct heap_dict *) dict); } } /* Format strings {{{1 */ /*< private > * g_variant_format_string_scan: * @string: a string that may be prefixed with a format string * @limit: (nullable) (default NULL): a pointer to the end of @string, * or %NULL * @endptr: (nullable) (default NULL): location to store the end pointer, * or %NULL * * Checks the string pointed to by @string for starting with a properly * formed #GVariant varargs format string. If no valid format string is * found then %FALSE is returned. * * If @string does start with a valid format string then %TRUE is * returned. If @endptr is non-%NULL then it is updated to point to the * first character after the format string. * * If @limit is non-%NULL then @limit (and any character after it) will * not be accessed and the effect is otherwise equivalent to if the * character at @limit were nul. * * See the section on [GVariant format strings][gvariant-format-strings]. * * Returns: %TRUE if there was a valid format string * * Since: 2.24 */ gboolean g_variant_format_string_scan (const gchar *string, const gchar *limit, const gchar **endptr) { #define next_char() (string == limit ? '\0' : *(string++)) #define peek_char() (string == limit ? '\0' : *string) char c; switch (next_char()) { case 'b': case 'y': case 'n': case 'q': case 'i': case 'u': case 'x': case 't': case 'h': case 'd': case 's': case 'o': case 'g': case 'v': case '*': case '?': case 'r': break; case 'm': return g_variant_format_string_scan (string, limit, endptr); case 'a': case '@': return g_variant_type_string_scan (string, limit, endptr); case '(': while (peek_char() != ')') if (!g_variant_format_string_scan (string, limit, &string)) return FALSE; next_char(); /* consume ')' */ break; case '{': c = next_char(); if (c == '&') { c = next_char (); if (c != 's' && c != 'o' && c != 'g') return FALSE; } else { if (c == '@') c = next_char (); /* ISO/IEC 9899:1999 (C99) §7.21.5.2: * The terminating null character is considered to be * part of the string. */ if (c != '\0' && strchr ("bynqiuxthdsog?", c) == NULL) return FALSE; } if (!g_variant_format_string_scan (string, limit, &string)) return FALSE; if (next_char() != '}') return FALSE; break; case '^': if ((c = next_char()) == 'a') { if ((c = next_char()) == '&') { if ((c = next_char()) == 'a') { if ((c = next_char()) == 'y') break; /* '^a&ay' */ } else if (c == 's' || c == 'o') break; /* '^a&s', '^a&o' */ } else if (c == 'a') { if ((c = next_char()) == 'y') break; /* '^aay' */ } else if (c == 's' || c == 'o') break; /* '^as', '^ao' */ else if (c == 'y') break; /* '^ay' */ } else if (c == '&') { if ((c = next_char()) == 'a') { if ((c = next_char()) == 'y') break; /* '^&ay' */ } } return FALSE; case '&': c = next_char(); if (c != 's' && c != 'o' && c != 'g') return FALSE; break; default: return FALSE; } if (endptr != NULL) *endptr = string; #undef next_char #undef peek_char return TRUE; } /** * g_variant_check_format_string: * @value: a #GVariant * @format_string: a valid #GVariant format string * @copy_only: %TRUE to ensure the format string makes deep copies * * Checks if calling g_variant_get() with @format_string on @value would * be valid from a type-compatibility standpoint. @format_string is * assumed to be a valid format string (from a syntactic standpoint). * * If @copy_only is %TRUE then this function additionally checks that it * would be safe to call g_variant_unref() on @value immediately after * the call to g_variant_get() without invalidating the result. This is * only possible if deep copies are made (ie: there are no pointers to * the data inside of the soon-to-be-freed #GVariant instance). If this * check fails then a g_critical() is printed and %FALSE is returned. * * This function is meant to be used by functions that wish to provide * varargs accessors to #GVariant values of uncertain values (eg: * g_variant_lookup() or g_menu_model_get_item_attribute()). * * Returns: %TRUE if @format_string is safe to use * * Since: 2.34 */ gboolean g_variant_check_format_string (GVariant *value, const gchar *format_string, gboolean copy_only) { const gchar *original_format = format_string; const gchar *type_string; /* Interesting factoid: assuming a format string is valid, it can be * converted to a type string by removing all '@' '&' and '^' * characters. * * Instead of doing that, we can just skip those characters when * comparing it to the type string of @value. * * For the copy-only case we can just drop the '&' from the list of * characters to skip over. A '&' will never appear in a type string * so we know that it won't be possible to return %TRUE if it is in a * format string. */ type_string = g_variant_get_type_string (value); while (*type_string || *format_string) { gchar format = *format_string++; switch (format) { case '&': if G_UNLIKELY (copy_only) { /* for the love of all that is good, please don't mark this string for translation... */ g_critical ("g_variant_check_format_string() is being called by a function with a GVariant varargs " "interface to validate the passed format string for type safety. The passed format " "(%s) contains a '&' character which would result in a pointer being returned to the " "data inside of a GVariant instance that may no longer exist by the time the function " "returns. Modify your code to use a format string without '&'.", original_format); return FALSE; } G_GNUC_FALLTHROUGH; case '^': case '@': /* ignore these 2 (or 3) */ continue; case '?': /* attempt to consume one of 'bynqiuxthdsog' */ { char s = *type_string++; if (s == '\0' || strchr ("bynqiuxthdsog", s) == NULL) return FALSE; } continue; case 'r': /* ensure it's a tuple */ if (*type_string != '(') return FALSE; G_GNUC_FALLTHROUGH; case '*': /* consume a full type string for the '*' or 'r' */ if (!g_variant_type_string_scan (type_string, NULL, &type_string)) return FALSE; continue; default: /* attempt to consume exactly one character equal to the format */ if (format != *type_string++) return FALSE; } } return TRUE; } /*< private > * g_variant_format_string_scan_type: * @string: a string that may be prefixed with a format string * @limit: (nullable) (default NULL): a pointer to the end of @string, * or %NULL * @endptr: (nullable) (default NULL): location to store the end pointer, * or %NULL * * If @string starts with a valid format string then this function will * return the type that the format string corresponds to. Otherwise * this function returns %NULL. * * Use g_variant_type_free() to free the return value when you no longer * need it. * * This function is otherwise exactly like * g_variant_format_string_scan(). * * Returns: (nullable): a #GVariantType if there was a valid format string * * Since: 2.24 */ GVariantType * g_variant_format_string_scan_type (const gchar *string, const gchar *limit, const gchar **endptr) { const gchar *my_end; gchar *dest; gchar *new; if (endptr == NULL) endptr = &my_end; if (!g_variant_format_string_scan (string, limit, endptr)) return NULL; dest = new = g_malloc (*endptr - string + 1); while (string != *endptr) { if (*string != '@' && *string != '&' && *string != '^') *dest++ = *string; string++; } *dest = '\0'; return (GVariantType *) G_VARIANT_TYPE (new); } static gboolean valid_format_string (const gchar *format_string, gboolean single, GVariant *value) { const gchar *endptr; GVariantType *type; type = g_variant_format_string_scan_type (format_string, NULL, &endptr); if G_UNLIKELY (type == NULL || (single && *endptr != '\0')) { if (single) g_critical ("'%s' is not a valid GVariant format string", format_string); else g_critical ("'%s' does not have a valid GVariant format " "string as a prefix", format_string); if (type != NULL) g_variant_type_free (type); return FALSE; } if G_UNLIKELY (value && !g_variant_is_of_type (value, type)) { gchar *fragment; gchar *typestr; fragment = g_strndup (format_string, endptr - format_string); typestr = g_variant_type_dup_string (type); g_critical ("the GVariant format string '%s' has a type of " "'%s' but the given value has a type of '%s'", fragment, typestr, g_variant_get_type_string (value)); g_variant_type_free (type); g_free (fragment); g_free (typestr); return FALSE; } g_variant_type_free (type); return TRUE; } /* Variable Arguments {{{1 */ /* We consider 2 main classes of format strings: * * - recursive format strings * these are ones that result in recursion and the collection of * possibly more than one argument. Maybe types, tuples, * dictionary entries. * * - leaf format string * these result in the collection of a single argument. * * Leaf format strings are further subdivided into two categories: * * - single non-null pointer ("nnp") * these either collect or return a single non-null pointer. * * - other * these collect or return something else (bool, number, etc). * * Based on the above, the varargs handling code is split into 4 main parts: * * - nnp handling code * - leaf handling code (which may invoke nnp code) * - generic handling code (may be recursive, may invoke leaf code) * - user-facing API (which invokes the generic code) * * Each section implements some of the following functions: * * - skip: * collect the arguments for the format string as if * g_variant_new() had been called, but do nothing with them. used * for skipping over arguments when constructing a Nothing maybe * type. * * - new: * create a GVariant * * * - get: * unpack a GVariant * * * - free (nnp only): * free a previously allocated item */ static gboolean g_variant_format_string_is_leaf (const gchar *str) { return str[0] != 'm' && str[0] != '(' && str[0] != '{'; } static gboolean g_variant_format_string_is_nnp (const gchar *str) { return str[0] == 'a' || str[0] == 's' || str[0] == 'o' || str[0] == 'g' || str[0] == '^' || str[0] == '@' || str[0] == '*' || str[0] == '?' || str[0] == 'r' || str[0] == 'v' || str[0] == '&'; } /* Single non-null pointer ("nnp") {{{2 */ static void g_variant_valist_free_nnp (const gchar *str, gpointer ptr) { switch (*str) { case 'a': g_variant_iter_free (ptr); break; case '^': if (g_str_has_suffix (str, "y")) { if (str[2] != 'a') /* '^a&ay', '^ay' */ g_free (ptr); else if (str[1] == 'a') /* '^aay' */ g_strfreev (ptr); break; /* '^&ay' */ } else if (str[2] != '&') /* '^as', '^ao' */ g_strfreev (ptr); else /* '^a&s', '^a&o' */ g_free (ptr); break; case 's': case 'o': case 'g': g_free (ptr); break; case '@': case '*': case '?': case 'v': g_variant_unref (ptr); break; case '&': break; default: g_assert_not_reached (); } } static gchar g_variant_scan_convenience (const gchar **str, gboolean *constant, guint *arrays) { *constant = FALSE; *arrays = 0; for (;;) { char c = *(*str)++; if (c == '&') *constant = TRUE; else if (c == 'a') (*arrays)++; else return c; } } static GVariant * g_variant_valist_new_nnp (const gchar **str, gpointer ptr) { if (**str == '&') (*str)++; switch (*(*str)++) { case 'a': if (ptr != NULL) { const GVariantType *type; GVariant *value; value = g_variant_builder_end (ptr); type = g_variant_get_type (value); if G_UNLIKELY (!g_variant_type_is_array (type)) g_error ("g_variant_new: expected array GVariantBuilder but " "the built value has type '%s'", g_variant_get_type_string (value)); type = g_variant_type_element (type); if G_UNLIKELY (!g_variant_type_is_subtype_of (type, (GVariantType *) *str)) { gchar *type_string = g_variant_type_dup_string ((GVariantType *) *str); g_error ("g_variant_new: expected GVariantBuilder array element " "type '%s' but the built value has element type '%s'", type_string, g_variant_get_type_string (value) + 1); g_free (type_string); } g_variant_type_string_scan (*str, NULL, str); return value; } else /* special case: NULL pointer for empty array */ { const GVariantType *type = (GVariantType *) *str; g_variant_type_string_scan (*str, NULL, str); if G_UNLIKELY (!g_variant_type_is_definite (type)) g_error ("g_variant_new: NULL pointer given with indefinite " "array type; unable to determine which type of empty " "array to construct."); return g_variant_new_array (type, NULL, 0); } case 's': { GVariant *value; value = g_variant_new_string (ptr); if (value == NULL) value = g_variant_new_string ("[Invalid UTF-8]"); return value; } case 'o': return g_variant_new_object_path (ptr); case 'g': return g_variant_new_signature (ptr); case '^': { gboolean constant; guint arrays; gchar type; type = g_variant_scan_convenience (str, &constant, &arrays); if (type == 's') return g_variant_new_strv (ptr, -1); if (type == 'o') return g_variant_new_objv (ptr, -1); if (arrays > 1) return g_variant_new_bytestring_array (ptr, -1); return g_variant_new_bytestring (ptr); } case '@': if G_UNLIKELY (!g_variant_is_of_type (ptr, (GVariantType *) *str)) { gchar *type_string = g_variant_type_dup_string ((GVariantType *) *str); g_error ("g_variant_new: expected GVariant of type '%s' but " "received value has type '%s'", type_string, g_variant_get_type_string (ptr)); g_free (type_string); } g_variant_type_string_scan (*str, NULL, str); return ptr; case '*': return ptr; case '?': if G_UNLIKELY (!g_variant_type_is_basic (g_variant_get_type (ptr))) g_error ("g_variant_new: format string '?' expects basic-typed " "GVariant, but received value has type '%s'", g_variant_get_type_string (ptr)); return ptr; case 'r': if G_UNLIKELY (!g_variant_type_is_tuple (g_variant_get_type (ptr))) g_error ("g_variant_new: format string 'r' expects tuple-typed " "GVariant, but received value has type '%s'", g_variant_get_type_string (ptr)); return ptr; case 'v': return g_variant_new_variant (ptr); default: g_assert_not_reached (); } } static gpointer g_variant_valist_get_nnp (const gchar **str, GVariant *value) { switch (*(*str)++) { case 'a': g_variant_type_string_scan (*str, NULL, str); return g_variant_iter_new (value); case '&': (*str)++; return (gchar *) g_variant_get_string (value, NULL); case 's': case 'o': case 'g': return g_variant_dup_string (value, NULL); case '^': { gboolean constant; guint arrays; gchar type; type = g_variant_scan_convenience (str, &constant, &arrays); if (type == 's') { if (constant) return g_variant_get_strv (value, NULL); else return g_variant_dup_strv (value, NULL); } else if (type == 'o') { if (constant) return g_variant_get_objv (value, NULL); else return g_variant_dup_objv (value, NULL); } else if (arrays > 1) { if (constant) return g_variant_get_bytestring_array (value, NULL); else return g_variant_dup_bytestring_array (value, NULL); } else { if (constant) return (gchar *) g_variant_get_bytestring (value); else return g_variant_dup_bytestring (value, NULL); } } case '@': g_variant_type_string_scan (*str, NULL, str); G_GNUC_FALLTHROUGH; case '*': case '?': case 'r': return g_variant_ref (value); case 'v': return g_variant_get_variant (value); default: g_assert_not_reached (); } } /* Leaves {{{2 */ static void g_variant_valist_skip_leaf (const gchar **str, va_list *app) { if (g_variant_format_string_is_nnp (*str)) { g_variant_format_string_scan (*str, NULL, str); va_arg (*app, gpointer); return; } switch (*(*str)++) { case 'b': case 'y': case 'n': case 'q': case 'i': case 'u': case 'h': va_arg (*app, int); return; case 'x': case 't': va_arg (*app, guint64); return; case 'd': va_arg (*app, gdouble); return; default: g_assert_not_reached (); } } static GVariant * g_variant_valist_new_leaf (const gchar **str, va_list *app) { if (g_variant_format_string_is_nnp (*str)) return g_variant_valist_new_nnp (str, va_arg (*app, gpointer)); switch (*(*str)++) { case 'b': return g_variant_new_boolean (va_arg (*app, gboolean)); case 'y': return g_variant_new_byte (va_arg (*app, guint)); case 'n': return g_variant_new_int16 (va_arg (*app, gint)); case 'q': return g_variant_new_uint16 (va_arg (*app, guint)); case 'i': return g_variant_new_int32 (va_arg (*app, gint)); case 'u': return g_variant_new_uint32 (va_arg (*app, guint)); case 'x': return g_variant_new_int64 (va_arg (*app, gint64)); case 't': return g_variant_new_uint64 (va_arg (*app, guint64)); case 'h': return g_variant_new_handle (va_arg (*app, gint)); case 'd': return g_variant_new_double (va_arg (*app, gdouble)); default: g_assert_not_reached (); } } /* The code below assumes this */ G_STATIC_ASSERT (sizeof (gboolean) == sizeof (guint32)); G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64)); static void g_variant_valist_get_leaf (const gchar **str, GVariant *value, gboolean free, va_list *app) { gpointer ptr = va_arg (*app, gpointer); if (ptr == NULL) { g_variant_format_string_scan (*str, NULL, str); return; } if (g_variant_format_string_is_nnp (*str)) { gpointer *nnp = (gpointer *) ptr; if (free && *nnp != NULL) g_variant_valist_free_nnp (*str, *nnp); *nnp = NULL; if (value != NULL) *nnp = g_variant_valist_get_nnp (str, value); else g_variant_format_string_scan (*str, NULL, str); return; } if (value != NULL) { switch (*(*str)++) { case 'b': *(gboolean *) ptr = g_variant_get_boolean (value); return; case 'y': *(guint8 *) ptr = g_variant_get_byte (value); return; case 'n': *(gint16 *) ptr = g_variant_get_int16 (value); return; case 'q': *(guint16 *) ptr = g_variant_get_uint16 (value); return; case 'i': *(gint32 *) ptr = g_variant_get_int32 (value); return; case 'u': *(guint32 *) ptr = g_variant_get_uint32 (value); return; case 'x': *(gint64 *) ptr = g_variant_get_int64 (value); return; case 't': *(guint64 *) ptr = g_variant_get_uint64 (value); return; case 'h': *(gint32 *) ptr = g_variant_get_handle (value); return; case 'd': *(gdouble *) ptr = g_variant_get_double (value); return; } } else { switch (*(*str)++) { case 'y': *(guint8 *) ptr = 0; return; case 'n': case 'q': *(guint16 *) ptr = 0; return; case 'i': case 'u': case 'h': case 'b': *(guint32 *) ptr = 0; return; case 'x': case 't': case 'd': *(guint64 *) ptr = 0; return; } } g_assert_not_reached (); } /* Generic (recursive) {{{2 */ static void g_variant_valist_skip (const gchar **str, va_list *app) { if (g_variant_format_string_is_leaf (*str)) g_variant_valist_skip_leaf (str, app); else if (**str == 'm') /* maybe */ { (*str)++; if (!g_variant_format_string_is_nnp (*str)) va_arg (*app, gboolean); g_variant_valist_skip (str, app); } else /* tuple, dictionary entry */ { g_assert (**str == '(' || **str == '{'); (*str)++; while (**str != ')' && **str != '}') g_variant_valist_skip (str, app); (*str)++; } } static GVariant * g_variant_valist_new (const gchar **str, va_list *app) { if (g_variant_format_string_is_leaf (*str)) return g_variant_valist_new_leaf (str, app); if (**str == 'm') /* maybe */ { GVariantType *type = NULL; GVariant *value = NULL; (*str)++; if (g_variant_format_string_is_nnp (*str)) { gpointer nnp = va_arg (*app, gpointer); if (nnp != NULL) value = g_variant_valist_new_nnp (str, nnp); else type = g_variant_format_string_scan_type (*str, NULL, str); } else { gboolean just = va_arg (*app, gboolean); if (just) value = g_variant_valist_new (str, app); else { type = g_variant_format_string_scan_type (*str, NULL, NULL); g_variant_valist_skip (str, app); } } value = g_variant_new_maybe (type, value); if (type != NULL) g_variant_type_free (type); return value; } else /* tuple, dictionary entry */ { GVariantBuilder b; if (**str == '(') g_variant_builder_init (&b, G_VARIANT_TYPE_TUPLE); else { g_assert (**str == '{'); g_variant_builder_init (&b, G_VARIANT_TYPE_DICT_ENTRY); } (*str)++; /* '(' */ while (**str != ')' && **str != '}') g_variant_builder_add_value (&b, g_variant_valist_new (str, app)); (*str)++; /* ')' */ return g_variant_builder_end (&b); } } static void g_variant_valist_get (const gchar **str, GVariant *value, gboolean free, va_list *app) { if (g_variant_format_string_is_leaf (*str)) g_variant_valist_get_leaf (str, value, free, app); else if (**str == 'm') { (*str)++; if (value != NULL) value = g_variant_get_maybe (value); if (!g_variant_format_string_is_nnp (*str)) { gboolean *ptr = va_arg (*app, gboolean *); if (ptr != NULL) *ptr = value != NULL; } g_variant_valist_get (str, value, free, app); if (value != NULL) g_variant_unref (value); } else /* tuple, dictionary entry */ { gint index = 0; g_assert (**str == '(' || **str == '{'); (*str)++; while (**str != ')' && **str != '}') { if (value != NULL) { GVariant *child = g_variant_get_child_value (value, index++); g_variant_valist_get (str, child, free, app); g_variant_unref (child); } else g_variant_valist_get (str, NULL, free, app); } (*str)++; } } /* User-facing API {{{2 */ /** * g_variant_new: (skip) * @format_string: a #GVariant format string * @...: arguments, as per @format_string * * Creates a new #GVariant instance. * * Think of this function as an analogue to g_strdup_printf(). * * The type of the created instance and the arguments that are expected * by this function are determined by @format_string. See the section on * [GVariant format strings][gvariant-format-strings]. Please note that * the syntax of the format string is very likely to be extended in the * future. * * The first character of the format string must not be '*' '?' '@' or * 'r'; in essence, a new #GVariant must always be constructed by this * function (and not merely passed through it unmodified). * * Note that the arguments must be of the correct width for their types * specified in @format_string. This can be achieved by casting them. See * the [GVariant varargs documentation][gvariant-varargs]. * * |[ * MyFlags some_flags = FLAG_ONE | FLAG_TWO; * const gchar *some_strings[] = { "a", "b", "c", NULL }; * GVariant *new_variant; * * new_variant = g_variant_new ("(t^as)", * // This cast is required. * (guint64) some_flags, * some_strings); * ]| * * Returns: a new floating #GVariant instance * * Since: 2.24 **/ GVariant * g_variant_new (const gchar *format_string, ...) { GVariant *value; va_list ap; g_return_val_if_fail (valid_format_string (format_string, TRUE, NULL) && format_string[0] != '?' && format_string[0] != '@' && format_string[0] != '*' && format_string[0] != 'r', NULL); va_start (ap, format_string); value = g_variant_new_va (format_string, NULL, &ap); va_end (ap); return value; } /** * g_variant_new_va: (skip) * @format_string: a string that is prefixed with a format string * @endptr: (nullable) (default NULL): location to store the end pointer, * or %NULL * @app: a pointer to a #va_list * * This function is intended to be used by libraries based on * #GVariant that want to provide g_variant_new()-like functionality * to their users. * * The API is more general than g_variant_new() to allow a wider range * of possible uses. * * @format_string must still point to a valid format string, but it only * needs to be nul-terminated if @endptr is %NULL. If @endptr is * non-%NULL then it is updated to point to the first character past the * end of the format string. * * @app is a pointer to a #va_list. The arguments, according to * @format_string, are collected from this #va_list and the list is left * pointing to the argument following the last. * * Note that the arguments in @app must be of the correct width for their * types specified in @format_string when collected into the #va_list. * See the [GVariant varargs documentation][gvariant-varargs]. * * These two generalisations allow mixing of multiple calls to * g_variant_new_va() and g_variant_get_va() within a single actual * varargs call by the user. * * The return value will be floating if it was a newly created GVariant * instance (for example, if the format string was "(ii)"). In the case * that the format_string was '*', '?', 'r', or a format starting with * '@' then the collected #GVariant pointer will be returned unmodified, * without adding any additional references. * * In order to behave correctly in all cases it is necessary for the * calling function to g_variant_ref_sink() the return result before * returning control to the user that originally provided the pointer. * At this point, the caller will have their own full reference to the * result. This can also be done by adding the result to a container, * or by passing it to another g_variant_new() call. * * Returns: a new, usually floating, #GVariant * * Since: 2.24 **/ GVariant * g_variant_new_va (const gchar *format_string, const gchar **endptr, va_list *app) { GVariant *value; g_return_val_if_fail (valid_format_string (format_string, !endptr, NULL), NULL); g_return_val_if_fail (app != NULL, NULL); value = g_variant_valist_new (&format_string, app); if (endptr != NULL) *endptr = format_string; return value; } /** * g_variant_get: (skip) * @value: a #GVariant instance * @format_string: a #GVariant format string * @...: arguments, as per @format_string * * Deconstructs a #GVariant instance. * * Think of this function as an analogue to scanf(). * * The arguments that are expected by this function are entirely * determined by @format_string. @format_string also restricts the * permissible types of @value. It is an error to give a value with * an incompatible type. See the section on * [GVariant format strings][gvariant-format-strings]. * Please note that the syntax of the format string is very likely to be * extended in the future. * * @format_string determines the C types that are used for unpacking * the values and also determines if the values are copied or borrowed, * see the section on * [`GVariant` format strings](gvariant-format-strings.html#pointers). * * Since: 2.24 **/ void g_variant_get (GVariant *value, const gchar *format_string, ...) { va_list ap; g_return_if_fail (value != NULL); g_return_if_fail (valid_format_string (format_string, TRUE, value)); /* if any direct-pointer-access formats are in use, flatten first */ if (strchr (format_string, '&')) g_variant_get_data (value); va_start (ap, format_string); g_variant_get_va (value, format_string, NULL, &ap); va_end (ap); } /** * g_variant_get_va: (skip) * @value: a #GVariant * @format_string: a string that is prefixed with a format string * @endptr: (nullable) (default NULL): location to store the end pointer, * or %NULL * @app: a pointer to a #va_list * * This function is intended to be used by libraries based on #GVariant * that want to provide g_variant_get()-like functionality to their * users. * * The API is more general than g_variant_get() to allow a wider range * of possible uses. * * @format_string must still point to a valid format string, but it only * need to be nul-terminated if @endptr is %NULL. If @endptr is * non-%NULL then it is updated to point to the first character past the * end of the format string. * * @app is a pointer to a #va_list. The arguments, according to * @format_string, are collected from this #va_list and the list is left * pointing to the argument following the last. * * These two generalisations allow mixing of multiple calls to * g_variant_new_va() and g_variant_get_va() within a single actual * varargs call by the user. * * @format_string determines the C types that are used for unpacking * the values and also determines if the values are copied or borrowed, * see the section on * [`GVariant` format strings](gvariant-format-strings.html#pointers). * * Since: 2.24 **/ void g_variant_get_va (GVariant *value, const gchar *format_string, const gchar **endptr, va_list *app) { g_return_if_fail (valid_format_string (format_string, !endptr, value)); g_return_if_fail (value != NULL); g_return_if_fail (app != NULL); /* if any direct-pointer-access formats are in use, flatten first */ if (strchr (format_string, '&')) g_variant_get_data (value); g_variant_valist_get (&format_string, value, FALSE, app); if (endptr != NULL) *endptr = format_string; } /* Varargs-enabled Utility Functions {{{1 */ /** * g_variant_builder_add: (skip) * @builder: a #GVariantBuilder * @format_string: a #GVariant varargs format string * @...: arguments, as per @format_string * * Adds to a #GVariantBuilder. * * This call is a convenience wrapper that is exactly equivalent to * calling g_variant_new() followed by g_variant_builder_add_value(). * * Note that the arguments must be of the correct width for their types * specified in @format_string. This can be achieved by casting them. See * the [GVariant varargs documentation][gvariant-varargs]. * * This function might be used as follows: * * |[ * GVariant * * make_pointless_dictionary (void) * { * GVariantBuilder builder; * int i; * * g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); * for (i = 0; i < 16; i++) * { * gchar buf[3]; * * sprintf (buf, "%d", i); * g_variant_builder_add (&builder, "{is}", i, buf); * } * * return g_variant_builder_end (&builder); * } * ]| * * Since: 2.24 */ void g_variant_builder_add (GVariantBuilder *builder, const gchar *format_string, ...) { GVariant *variant; va_list ap; va_start (ap, format_string); variant = g_variant_new_va (format_string, NULL, &ap); va_end (ap); g_variant_builder_add_value (builder, variant); } /** * g_variant_get_child: (skip) * @value: a container #GVariant * @index_: the index of the child to deconstruct * @format_string: a #GVariant format string * @...: arguments, as per @format_string * * Reads a child item out of a container #GVariant instance and * deconstructs it according to @format_string. This call is * essentially a combination of g_variant_get_child_value() and * g_variant_get(). * * @format_string determines the C types that are used for unpacking * the values and also determines if the values are copied or borrowed, * see the section on * [`GVariant` format strings](gvariant-format-strings.html#pointers). * * Since: 2.24 **/ void g_variant_get_child (GVariant *value, gsize index_, const gchar *format_string, ...) { GVariant *child; va_list ap; /* if any direct-pointer-access formats are in use, flatten first */ if (strchr (format_string, '&')) g_variant_get_data (value); child = g_variant_get_child_value (value, index_); g_return_if_fail (valid_format_string (format_string, TRUE, child)); va_start (ap, format_string); g_variant_get_va (child, format_string, NULL, &ap); va_end (ap); g_variant_unref (child); } /** * g_variant_iter_next: (skip) * @iter: a #GVariantIter * @format_string: a GVariant format string * @...: the arguments to unpack the value into * * Gets the next item in the container and unpacks it into the variable * argument list according to @format_string, returning %TRUE. * * If no more items remain then %FALSE is returned. * * All of the pointers given on the variable arguments list of this * function are assumed to point at uninitialised memory. It is the * responsibility of the caller to free all of the values returned by * the unpacking process. * * Here is an example for memory management with g_variant_iter_next(): * |[ * // Iterates a dictionary of type 'a{sv}' * void * iterate_dictionary (GVariant *dictionary) * { * GVariantIter iter; * GVariant *value; * gchar *key; * * g_variant_iter_init (&iter, dictionary); * while (g_variant_iter_next (&iter, "{sv}", &key, &value)) * { * g_print ("Item '%s' has type '%s'\n", key, * g_variant_get_type_string (value)); * * // must free data for ourselves * g_variant_unref (value); * g_free (key); * } * } * ]| * * For a solution that is likely to be more convenient to C programmers * when dealing with loops, see g_variant_iter_loop(). * * @format_string determines the C types that are used for unpacking * the values and also determines if the values are copied or borrowed. * * See the section on * [`GVariant` format strings](gvariant-format-strings.html#pointers). * * Returns: %TRUE if a value was unpacked, or %FALSE if there as no value * * Since: 2.24 **/ gboolean g_variant_iter_next (GVariantIter *iter, const gchar *format_string, ...) { GVariant *value; value = g_variant_iter_next_value (iter); g_return_val_if_fail (valid_format_string (format_string, TRUE, value), FALSE); if (value != NULL) { va_list ap; va_start (ap, format_string); g_variant_valist_get (&format_string, value, FALSE, &ap); va_end (ap); g_variant_unref (value); } return value != NULL; } /** * g_variant_iter_loop: (skip) * @iter: a #GVariantIter * @format_string: a GVariant format string * @...: the arguments to unpack the value into * * Gets the next item in the container and unpacks it into the variable * argument list according to @format_string, returning %TRUE. * * If no more items remain then %FALSE is returned. * * On the first call to this function, the pointers appearing on the * variable argument list are assumed to point at uninitialised memory. * On the second and later calls, it is assumed that the same pointers * will be given and that they will point to the memory as set by the * previous call to this function. This allows the previous values to * be freed, as appropriate. * * This function is intended to be used with a while loop as * demonstrated in the following example. This function can only be * used when iterating over an array. It is only valid to call this * function with a string constant for the format string and the same * string constant must be used each time. Mixing calls to this * function and g_variant_iter_next() or g_variant_iter_next_value() on * the same iterator causes undefined behavior. * * If you break out of a such a while loop using g_variant_iter_loop() then * you must free or unreference all the unpacked values as you would with * g_variant_get(). Failure to do so will cause a memory leak. * * Here is an example for memory management with g_variant_iter_loop(): * |[ * // Iterates a dictionary of type 'a{sv}' * void * iterate_dictionary (GVariant *dictionary) * { * GVariantIter iter; * GVariant *value; * gchar *key; * * g_variant_iter_init (&iter, dictionary); * while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) * { * g_print ("Item '%s' has type '%s'\n", key, * g_variant_get_type_string (value)); * * // no need to free 'key' and 'value' here * // unless breaking out of this loop * } * } * ]| * * For most cases you should use g_variant_iter_next(). * * This function is really only useful when unpacking into #GVariant or * #GVariantIter in order to allow you to skip the call to * g_variant_unref() or g_variant_iter_free(). * * For example, if you are only looping over simple integer and string * types, g_variant_iter_next() is definitely preferred. For string * types, use the '&' prefix to avoid allocating any memory at all (and * thereby avoiding the need to free anything as well). * * @format_string determines the C types that are used for unpacking * the values and also determines if the values are copied or borrowed. * * See the section on * [`GVariant` format strings](gvariant-format-strings.html#pointers). * * Returns: %TRUE if a value was unpacked, or %FALSE if there was no * value * * Since: 2.24 **/ gboolean g_variant_iter_loop (GVariantIter *iter, const gchar *format_string, ...) { gboolean first_time = GVSI(iter)->loop_format == NULL; GVariant *value; va_list ap; g_return_val_if_fail (first_time || format_string == GVSI(iter)->loop_format, FALSE); if (first_time) { TYPE_CHECK (GVSI(iter)->value, G_VARIANT_TYPE_ARRAY, FALSE); GVSI(iter)->loop_format = format_string; if (strchr (format_string, '&')) g_variant_get_data (GVSI(iter)->value); } value = g_variant_iter_next_value (iter); g_return_val_if_fail (!first_time || valid_format_string (format_string, TRUE, value), FALSE); va_start (ap, format_string); g_variant_valist_get (&format_string, value, !first_time, &ap); va_end (ap); if (value != NULL) g_variant_unref (value); return value != NULL; } /* Serialized data {{{1 */ static GVariant * g_variant_deep_copy (GVariant *value, gboolean byteswap) { switch (g_variant_classify (value)) { case G_VARIANT_CLASS_MAYBE: case G_VARIANT_CLASS_TUPLE: case G_VARIANT_CLASS_DICT_ENTRY: case G_VARIANT_CLASS_VARIANT: { GVariantBuilder builder; gsize i, n_children; g_variant_builder_init (&builder, g_variant_get_type (value)); for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++) { GVariant *child = g_variant_get_child_value (value, i); g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap)); g_variant_unref (child); } return g_variant_builder_end (&builder); } case G_VARIANT_CLASS_ARRAY: { GVariantBuilder builder; gsize i, n_children; GVariant *first_invalid_child_deep_copy = NULL; /* Arrays are in theory treated the same as maybes, tuples, dict entries * and variants, and could be another case in the above block of code. * * However, they have the property that when dealing with non-normal * data (which is the only time g_variant_deep_copy() is currently * called) in a variable-sized array, the code above can easily end up * creating many default child values in order to return an array which * is of the right length and type, but without containing non-normal * data. This can happen if the offset table for the array is malformed. * * In this case, the code above would end up allocating the same default * value for each one of the child indexes beyond the first malformed * entry in the offset table. This can end up being a lot of identical * allocations of default values, particularly if the non-normal array * is crafted maliciously. * * Avoid that problem by returning a new reference to the same default * value for every child after the first invalid one. This results in * returning an equivalent array, in normal form and trusted — but with * significantly fewer memory allocations. * * See https://gitlab.gnome.org/GNOME/glib/-/issues/2540 */ g_variant_builder_init (&builder, g_variant_get_type (value)); for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++) { /* Try maybe_get_child_value() first; if it returns NULL, this child * is non-normal. get_child_value() would have constructed and * returned a default value in that case. */ GVariant *child = g_variant_maybe_get_child_value (value, i); if (child != NULL) { /* Non-normal children may not always be contiguous, as they may * be non-normal for reasons other than invalid offset table * entries. As they are all the same type, they will all have * the same default value though, so keep that around. */ g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap)); } else if (child == NULL && first_invalid_child_deep_copy != NULL) { g_variant_builder_add_value (&builder, first_invalid_child_deep_copy); } else if (child == NULL) { child = g_variant_get_child_value (value, i); first_invalid_child_deep_copy = g_variant_ref_sink (g_variant_deep_copy (child, byteswap)); g_variant_builder_add_value (&builder, first_invalid_child_deep_copy); } g_clear_pointer (&child, g_variant_unref); } g_clear_pointer (&first_invalid_child_deep_copy, g_variant_unref); return g_variant_builder_end (&builder); } case G_VARIANT_CLASS_BOOLEAN: return g_variant_new_boolean (g_variant_get_boolean (value)); case G_VARIANT_CLASS_BYTE: return g_variant_new_byte (g_variant_get_byte (value)); case G_VARIANT_CLASS_INT16: if (byteswap) return g_variant_new_int16 (GUINT16_SWAP_LE_BE (g_variant_get_int16 (value))); else return g_variant_new_int16 (g_variant_get_int16 (value)); case G_VARIANT_CLASS_UINT16: if (byteswap) return g_variant_new_uint16 (GUINT16_SWAP_LE_BE (g_variant_get_uint16 (value))); else return g_variant_new_uint16 (g_variant_get_uint16 (value)); case G_VARIANT_CLASS_INT32: if (byteswap) return g_variant_new_int32 (GUINT32_SWAP_LE_BE (g_variant_get_int32 (value))); else return g_variant_new_int32 (g_variant_get_int32 (value)); case G_VARIANT_CLASS_UINT32: if (byteswap) return g_variant_new_uint32 (GUINT32_SWAP_LE_BE (g_variant_get_uint32 (value))); else return g_variant_new_uint32 (g_variant_get_uint32 (value)); case G_VARIANT_CLASS_INT64: if (byteswap) return g_variant_new_int64 (GUINT64_SWAP_LE_BE (g_variant_get_int64 (value))); else return g_variant_new_int64 (g_variant_get_int64 (value)); case G_VARIANT_CLASS_UINT64: if (byteswap) return g_variant_new_uint64 (GUINT64_SWAP_LE_BE (g_variant_get_uint64 (value))); else return g_variant_new_uint64 (g_variant_get_uint64 (value)); case G_VARIANT_CLASS_HANDLE: if (byteswap) return g_variant_new_handle (GUINT32_SWAP_LE_BE (g_variant_get_handle (value))); else return g_variant_new_handle (g_variant_get_handle (value)); case G_VARIANT_CLASS_DOUBLE: if (byteswap) { /* We have to convert the double to a uint64 here using a union, * because a cast will round it numerically. */ union { guint64 u64; gdouble dbl; } u1, u2; u1.dbl = g_variant_get_double (value); u2.u64 = GUINT64_SWAP_LE_BE (u1.u64); return g_variant_new_double (u2.dbl); } else return g_variant_new_double (g_variant_get_double (value)); case G_VARIANT_CLASS_STRING: return g_variant_new_string (g_variant_get_string (value, NULL)); case G_VARIANT_CLASS_OBJECT_PATH: return g_variant_new_object_path (g_variant_get_string (value, NULL)); case G_VARIANT_CLASS_SIGNATURE: return g_variant_new_signature (g_variant_get_string (value, NULL)); } g_assert_not_reached (); } /** * g_variant_get_normal_form: * @value: a #GVariant * * Gets a #GVariant instance that has the same value as @value and is * trusted to be in normal form. * * If @value is already trusted to be in normal form then a new * reference to @value is returned. * * If @value is not already trusted, then it is scanned to check if it * is in normal form. If it is found to be in normal form then it is * marked as trusted and a new reference to it is returned. * * If @value is found not to be in normal form then a new trusted * #GVariant is created with the same value as @value. The non-normal parts of * @value will be replaced with default values which are guaranteed to be in * normal form. * * It makes sense to call this function if you've received #GVariant * data from untrusted sources and you want to ensure your serialized * output is definitely in normal form. * * If @value is already in normal form, a new reference will be returned * (which will be floating if @value is floating). If it is not in normal form, * the newly created #GVariant will be returned with a single non-floating * reference. Typically, g_variant_take_ref() should be called on the return * value from this function to guarantee ownership of a single non-floating * reference to it. * * Returns: (transfer full): a trusted #GVariant * * Since: 2.24 **/ GVariant * g_variant_get_normal_form (GVariant *value) { GVariant *trusted; if (g_variant_is_normal_form (value)) return g_variant_ref (value); trusted = g_variant_deep_copy (value, FALSE); g_assert (g_variant_is_trusted (trusted)); return g_variant_ref_sink (trusted); } /** * g_variant_byteswap: * @value: a #GVariant * * Performs a byteswapping operation on the contents of @value. The * result is that all multi-byte numeric data contained in @value is * byteswapped. That includes 16, 32, and 64bit signed and unsigned * integers as well as file handles and double precision floating point * values. * * This function is an identity mapping on any value that does not * contain multi-byte numeric data. That include strings, booleans, * bytes and containers containing only these things (recursively). * * While this function can safely handle untrusted, non-normal data, it is * recommended to check whether the input is in normal form beforehand, using * g_variant_is_normal_form(), and to reject non-normal inputs if your * application can be strict about what inputs it rejects. * * The returned value is always in normal form and is marked as trusted. * A full, not floating, reference is returned. * * Returns: (transfer full): the byteswapped form of @value * * Since: 2.24 **/ GVariant * g_variant_byteswap (GVariant *value) { GVariantTypeInfo *type_info; guint alignment; GVariant *new; type_info = g_variant_get_type_info (value); g_variant_type_info_query (type_info, &alignment, NULL); if (alignment && g_variant_is_normal_form (value)) { /* (potentially) contains multi-byte numeric data, but is also already in * normal form so we can use a faster byteswapping codepath on the * serialised data */ GVariantSerialised serialised = { 0, }; GBytes *bytes; serialised.type_info = g_variant_get_type_info (value); serialised.size = g_variant_get_size (value); serialised.data = g_malloc (serialised.size); serialised.depth = g_variant_get_depth (value); serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */ serialised.checked_offsets_up_to = G_MAXSIZE; g_variant_store (value, serialised.data); g_variant_serialised_byteswap (serialised); bytes = g_bytes_new_take (serialised.data, serialised.size); new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE)); g_bytes_unref (bytes); } else if (alignment) /* (potentially) contains multi-byte numeric data */ new = g_variant_ref_sink (g_variant_deep_copy (value, TRUE)); else /* contains no multi-byte data */ new = g_variant_get_normal_form (value); g_assert (g_variant_is_trusted (new)); return g_steal_pointer (&new); } /** * g_variant_new_from_data: * @type: a definite #GVariantType * @data: (array length=size) (element-type guint8): the serialized data * @size: the size of @data * @trusted: %TRUE if @data is definitely in normal form * @notify: (scope async): function to call when @data is no longer needed * @user_data: data for @notify * * Creates a new #GVariant instance from serialized data. * * @type is the type of #GVariant instance that will be constructed. * The interpretation of @data depends on knowing the type. * * @data is not modified by this function and must remain valid with an * unchanging value until such a time as @notify is called with * @user_data. If the contents of @data change before that time then * the result is undefined. * * If @data is trusted to be serialized data in normal form then * @trusted should be %TRUE. This applies to serialized data created * within this process or read from a trusted location on the disk (such * as a file installed in /usr/lib alongside your application). You * should set trusted to %FALSE if @data is read from the network, a * file in the user's home directory, etc. * * If @data was not stored in this machine's native endianness, any multi-byte * numeric values in the returned variant will also be in non-native * endianness. g_variant_byteswap() can be used to recover the original values. * * @notify will be called with @user_data when @data is no longer * needed. The exact time of this call is unspecified and might even be * before this function returns. * * Note: @data must be backed by memory that is aligned appropriately for the * @type being loaded. Otherwise this function will internally create a copy of * the memory (since GLib 2.60) or (in older versions) fail and exit the * process. * * Returns: (transfer none): a new floating #GVariant of type @type * * Since: 2.24 **/ GVariant * g_variant_new_from_data (const GVariantType *type, gconstpointer data, gsize size, gboolean trusted, GDestroyNotify notify, gpointer user_data) { GVariant *value; GBytes *bytes; g_return_val_if_fail (g_variant_type_is_definite (type), NULL); g_return_val_if_fail (data != NULL || size == 0, NULL); if (notify) bytes = g_bytes_new_with_free_func (data, size, notify, user_data); else bytes = g_bytes_new_static (data, size); value = g_variant_new_from_bytes (type, bytes, trusted); g_bytes_unref (bytes); return value; } /* Epilogue {{{1 */ /* vim:set foldmethod=marker: */