/* * variant-util.c - Source for GVariant utilities * * Copyright (C) 2012 Collabora Ltd. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * SECTION:variant-util * @title: GVariant utilities * @short_description: some GVariant utility functions * * GVariant utility functions used in telepathy-glib. */ /** * SECTION:vardict * @title: Manipulating a{sv} mappings * @short_description: Functions to manipulate mappings from string to * variant, as represented in GVariant by a %G_VARIANT_TYPE_VARDICT * * These functions provide convenient access to the values in such * a mapping. * * Since: 0.19.10 */ #include "config.h" #include #include #include #include #define DEBUG_FLAG TP_DEBUG_MISC #include "debug-internal.h" /* * _tp_asv_to_vardict: * * Returns: (transfer full): a #GVariant of type %G_VARIANT_TYPE_VARDICT */ GVariant * _tp_asv_to_vardict (const GHashTable *asv) { return _tp_boxed_to_variant (TP_HASH_TYPE_STRING_VARIANT_MAP, "a{sv}", (gpointer) asv); } GVariant * _tp_boxed_to_variant (GType gtype, const gchar *variant_type, gpointer boxed) { GValue v = G_VALUE_INIT; GVariant *ret; g_return_val_if_fail (boxed != NULL, NULL); g_value_init (&v, gtype); g_value_set_boxed (&v, boxed); ret = dbus_g_value_build_g_variant (&v); g_return_val_if_fail (!tp_strdiff (g_variant_get_type_string (ret), variant_type), NULL); g_value_unset (&v); return g_variant_ref_sink (ret); } /* * _tp_asv_from_vardict: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * * Returns: (transfer full): a newly created #GHashTable of * type #TP_HASH_TYPE_STRING_VARIANT_MAP */ GHashTable * _tp_asv_from_vardict (GVariant *variant) { GValue v = G_VALUE_INIT; GHashTable *result; g_return_val_if_fail (variant != NULL, NULL); g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL); dbus_g_value_parse_g_variant (variant, &v); g_assert (G_VALUE_HOLDS (&v, TP_HASH_TYPE_STRING_VARIANT_MAP)); result = g_value_dup_boxed (&v); g_value_unset (&v); return result; } /** * tp_variant_type_classify: * @type: a #GVariantType * * Classifies @type according to its top-level type. * * Returns: the #GVariantClass of @type * Since: 0.19.10 **/ GVariantClass tp_variant_type_classify (const GVariantType *type) { /* Same as g_variant_classify() but for a GVariantType. This returns the first * letter of the dbus type and cast it to an enum where elements have the * ascii value of the type letters. */ return g_variant_type_peek_string (type)[0]; } static gdouble _tp_variant_convert_double (GVariant *variant, gboolean *valid) { *valid = TRUE; switch (g_variant_classify (variant)) { case G_VARIANT_CLASS_DOUBLE: return g_variant_get_double (variant); case G_VARIANT_CLASS_BYTE: return g_variant_get_byte (variant); case G_VARIANT_CLASS_UINT32: return g_variant_get_uint32 (variant); case G_VARIANT_CLASS_INT32: return g_variant_get_int32 (variant); case G_VARIANT_CLASS_INT64: return g_variant_get_int64 (variant); case G_VARIANT_CLASS_UINT64: return g_variant_get_uint64 (variant); default: break; } *valid = FALSE; return 0.0; } static gint32 _tp_variant_convert_int32 (GVariant *variant, gboolean *valid) { gint64 i; guint64 u; *valid = TRUE; switch (g_variant_classify (variant)) { case G_VARIANT_CLASS_BYTE: return g_variant_get_byte (variant); case G_VARIANT_CLASS_UINT32: u = g_variant_get_uint32 (variant); if (G_LIKELY (u <= G_MAXINT32)) return u; break; case G_VARIANT_CLASS_INT32: return g_variant_get_int32 (variant); case G_VARIANT_CLASS_INT64: i = g_variant_get_int64 (variant); if (G_LIKELY (i >= G_MININT32 && i <= G_MAXINT32)) return i; break; case G_VARIANT_CLASS_UINT64: u = g_variant_get_uint64 (variant); if (G_LIKELY (u <= G_MAXINT32)) return u; break; default: break; } *valid = FALSE; return 0; } static gint64 _tp_variant_convert_int64 (GVariant *variant, gboolean *valid) { guint64 u; *valid = TRUE; switch (g_variant_classify (variant)) { case G_VARIANT_CLASS_BYTE: return g_variant_get_byte (variant); case G_VARIANT_CLASS_UINT32: return g_variant_get_uint32 (variant); case G_VARIANT_CLASS_INT32: return g_variant_get_int32 (variant); case G_VARIANT_CLASS_INT64: return g_variant_get_int64 (variant); case G_VARIANT_CLASS_UINT64: u = g_variant_get_uint64 (variant); if (G_LIKELY (u <= G_MAXINT64)) return u; break; default: break; } *valid = FALSE; return 0; } static guint32 _tp_variant_convert_uint32 (GVariant *variant, gboolean *valid) { gint64 i; guint64 u; *valid = TRUE; switch (g_variant_classify (variant)) { case G_VARIANT_CLASS_BYTE: return g_variant_get_byte (variant); case G_VARIANT_CLASS_UINT32: return g_variant_get_uint32 (variant); case G_VARIANT_CLASS_INT32: i = g_variant_get_int32 (variant); if (G_LIKELY (i >= 0)) return i; break; case G_VARIANT_CLASS_INT64: i = g_variant_get_int64 (variant); if (G_LIKELY (i >= 0 && i <= G_MAXUINT32)) return i; break; case G_VARIANT_CLASS_UINT64: u = g_variant_get_uint64 (variant); if (G_LIKELY (u <= G_MAXUINT32)) return u; break; default: break; } *valid = FALSE; return 0; } static guint64 _tp_variant_convert_uint64 (GVariant *variant, gboolean *valid) { gint64 tmp; *valid = TRUE; switch (g_variant_classify (variant)) { case G_VARIANT_CLASS_BYTE: return g_variant_get_byte (variant); case G_VARIANT_CLASS_UINT32: return g_variant_get_uint32 (variant); case G_VARIANT_CLASS_INT32: tmp = g_variant_get_int32 (variant); if (G_LIKELY (tmp >= 0)) return tmp; break; case G_VARIANT_CLASS_INT64: tmp = g_variant_get_int64 (variant); if (G_LIKELY (tmp >= 0)) return tmp; break; case G_VARIANT_CLASS_UINT64: return g_variant_get_uint64 (variant); default: break; } *valid = FALSE; return 0; } /** * tp_variant_convert: * @variant: (transfer full): a #GVariant to convert * @type: a #GVariantType @variant must be converted to * * Convert the type of @variant to @type if possible. This takes ownership of * @variant. If no conversion is needed, simply return @variant. If conversion * is not possible, %NULL is returned. * * Returns: (transfer full): a new #GVariant owned by the caller. * Since: 0.19.10 **/ GVariant * tp_variant_convert (GVariant *variant, const GVariantType *type) { GVariant *ret = NULL; gboolean valid; if (variant == NULL) return NULL; g_variant_ref_sink (variant); if (g_variant_is_of_type (variant, type)) return variant; switch (tp_variant_type_classify (type)) { #define CASE(type) \ { \ g##type tmp = _tp_variant_convert_##type (variant, &valid); \ if (valid) \ ret = g_variant_new_##type (tmp); \ } case G_VARIANT_CLASS_DOUBLE: CASE (double); break; case G_VARIANT_CLASS_INT32: CASE (int32); break; case G_VARIANT_CLASS_INT64: CASE (int64); break; case G_VARIANT_CLASS_UINT32: CASE (uint32); break; case G_VARIANT_CLASS_UINT64: CASE (uint64); break; default: break; #undef CASE } g_variant_unref (variant); return (ret != NULL) ? g_variant_ref_sink (ret) : NULL; } /** * tp_vardict_get_string: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * * If a value for @key in @variant is present and is a string, return it. * * Otherwise return %NULL. * * The returned value is not copied, and is only valid as long as @variant is * kept. Copy it with g_strdup() if you need to keep it for longer. * * Returns: (transfer none) (allow-none): the string value of @key, or %NULL * Since: 0.19.10 */ const gchar * tp_vardict_get_string (GVariant *variant, const gchar *key) { const gchar *ret; g_return_val_if_fail (variant != NULL, NULL); g_return_val_if_fail (key != NULL, NULL); g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL); if (!g_variant_lookup (variant, key, "&s", &ret)) return NULL; return ret; } /** * tp_vardict_get_object_path: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * * If a value for @key in @variant is present and is an object path, return it. * * Otherwise return %NULL. * * The returned value is not copied, and is only valid as long as @variant is * kept. Copy it with g_strdup() if you need to keep it for longer. * * Returns: (transfer none) (allow-none): the object path value of @key, or * %NULL * Since: 0.19.10 */ const gchar * tp_vardict_get_object_path (GVariant *variant, const gchar *key) { const gchar *ret; g_return_val_if_fail (variant != NULL, NULL); g_return_val_if_fail (key != NULL, NULL); g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL); if (!g_variant_lookup (variant, key, "&o", &ret)) return NULL; return ret; } /** * tp_vardict_get_boolean: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * @valid: (out): Either %NULL, or a location to store %TRUE if the key actually * exists and has a boolean value * * If a value for @key in @variant is present and boolean, return it, * and set *@valid to %TRUE if @valid is not %NULL. * * Otherwise return %FALSE, and set *@valid to %FALSE if @valid is not %NULL. * * Returns: a boolean value for @key * Since: 0.19.10 */ gboolean tp_vardict_get_boolean (GVariant *variant, const gchar *key, gboolean *valid) { gboolean ret; g_return_val_if_fail (variant != NULL, FALSE); g_return_val_if_fail (key != NULL, FALSE); g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), FALSE); if (!g_variant_lookup (variant, key, "b", &ret)) { if (valid != NULL) *valid = FALSE; return FALSE; } if (valid != NULL) *valid = TRUE; return ret; } #define IMPLEMENT(type) \ g##type \ tp_vardict_get_##type (GVariant *variant, \ const gchar *key, \ gboolean *valid) \ { \ g##type ret = 0; \ gboolean ret_valid = FALSE; \ GVariant *value; \ \ g_return_val_if_fail (variant != NULL, 0); \ g_return_val_if_fail (key != NULL, 0); \ g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), 0); \ \ value = g_variant_lookup_value (variant, key, NULL); \ if (value != NULL) \ { \ ret = _tp_variant_convert_##type (value, &ret_valid); \ g_variant_unref (value); \ } \ \ if (valid != NULL) \ *valid = ret_valid; \ \ return ret; \ } /** * tp_vardict_get_double: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * @valid: (out): Either %NULL, or a location in which to store %TRUE on success * or %FALSE on failure * * If a value for @key in @variant is present and has any numeric type used by * GVariant (gint32, guint32, gint64, guint64 or gdouble), * return it as a double, and if @valid is not %NULL, set *@valid to %TRUE. * * Otherwise, return 0.0, and if @valid is not %NULL, set *@valid to %FALSE. * * Returns: the double precision floating-point value of @key, or 0.0 * Since: 0.19.10 */ IMPLEMENT (double) /** * tp_vardict_get_int32: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * @valid: (out): Either %NULL, or a location in which to store %TRUE on success * or %FALSE on failure * * If a value for @key in @variant is present, has an integer type used by * GVariant (gint32, guint32, gint64 or guint64) and fits in the * range of a gint32, return it, and if @valid is not %NULL, set *@valid to * %TRUE. * * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE. * * Returns: the 32-bit signed integer value of @key, or 0 * Since: 0.19.10 */ IMPLEMENT (int32) /** * tp_vardict_get_int64: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * @valid: (out): Either %NULL, or a location in which to store %TRUE on success * or %FALSE on failure * * If a value for @key in @variant is present, has an integer type used by * GVariant (gint32, guint32, gint64 or guint64) and fits in the * range of a gint64, return it, and if @valid is not %NULL, set *@valid to * %TRUE. * * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE. * * Returns: the 64-bit signed integer value of @key, or 0 * Since: 0.19.10 */ IMPLEMENT (int64) /** * tp_vardict_get_uint32: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * @valid: (out): Either %NULL, or a location in which to store %TRUE on success * or %FALSE on failure * * If a value for @key in @variant is present, has an integer type used by * GVariant (gint32, guint32, gint64 or guint64) and fits in the * range of a guint32, return it, and if @valid is not %NULL, set *@valid to * %TRUE. * * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE. * * Returns: the 32-bit unsigned integer value of @key, or 0 * Since: 0.19.10 */ IMPLEMENT (uint32) /** * tp_vardict_get_uint64: * @variant: a #GVariant of type %G_VARIANT_TYPE_VARDICT * @key: The key to look up * @valid: (out): Either %NULL, or a location in which to store %TRUE on success * or %FALSE on failure * * If a value for @key in @variant is present, has an integer type used by * GVariant (gint32, guint32, gint64 or guint64) and is non-negative, * return it, and if @valid is not %NULL, set *@valid to %TRUE. * * Otherwise, return 0, and if @valid is not %NULL, set *@valid to %FALSE. * * Returns: the 64-bit unsigned integer value of @key, or 0 * Since: 0.19.10 */ IMPLEMENT (uint64) #undef IMPLEMENT