diff options
-rw-r--r-- | libappstream-glib/Makefile.am | 2 | ||||
-rw-r--r-- | libappstream-glib/as-ref-string.c | 263 | ||||
-rw-r--r-- | libappstream-glib/as-ref-string.h | 52 | ||||
-rw-r--r-- | libappstream-glib/as-self-test.c | 28 |
4 files changed, 345 insertions, 0 deletions
diff --git a/libappstream-glib/Makefile.am b/libappstream-glib/Makefile.am index 826d935..26161e7 100644 --- a/libappstream-glib/Makefile.am +++ b/libappstream-glib/Makefile.am @@ -124,6 +124,8 @@ libappstream_glib_la_SOURCES = \ as-profile.h \ as-provide.c \ as-provide-private.h \ + as-ref-string.c \ + as-ref-string.h \ as-release.c \ as-release-private.h \ as-review.c \ diff --git a/libappstream-glib/as-ref-string.c b/libappstream-glib/as-ref-string.c new file mode 100644 index 0000000..27e6621 --- /dev/null +++ b/libappstream-glib/as-ref-string.c @@ -0,0 +1,263 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2016 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * SECTION:as-ref-string + * @short_description: Reference counted strings + * @include: appstream-glib.h + * @stability: Unstable + * + * These functions are used to implement refcounted C strings. + */ + +#include "config.h" + +#include <string.h> + +#include "as-ref-string.h" + +typedef struct { + volatile gint refcnt; +} AsRefStringHeader; + +#define AS_REFPTR_TO_HEADER(o) ((AsRefStringHeader *) ((void *) ((guint8 *) o - sizeof (AsRefStringHeader)))) +#define AS_REFPTR_FROM_HEADER(o) ((gpointer) (((guint8 *) o) + sizeof (AsRefStringHeader))) + +static void +as_ref_string_unref_from_str (gchar *str) +{ + AsRefStringHeader *hdr = AS_REFPTR_TO_HEADER (str); + g_free (hdr); +} + +static GHashTable * +as_ref_string_get_hash (void) +{ + static GHashTable *hash = NULL; + if (hash == NULL) { + /* gpointer to AsRefStringHeader */ + hash = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + (GDestroyNotify) as_ref_string_unref_from_str, + NULL); + } + return hash; +} + +/** + * as_ref_string_new_copy_with_length: + * @str: a string + * @len: length of @str, not including the NUL byte + * + * Returns a deep copied refcounted string. The returned string can be modified + * without affecting other refcounted versions. + * + * Returns: a %AsRefString + * + * Since: 0.6.6 + */ +AsRefString * +as_ref_string_new_copy_with_length (const gchar *str, gsize len) +{ + AsRefStringHeader *hdr; + AsRefString *rstr_new; + GHashTable *hash = as_ref_string_get_hash (); + + /* create object */ + hdr = g_malloc (len + sizeof (AsRefStringHeader) + 1); + hdr->refcnt = 1; + rstr_new = AS_REFPTR_FROM_HEADER (hdr); + memcpy (rstr_new, str, len); + rstr_new[len] = '\0'; + + /* add */ + g_hash_table_add (hash, rstr_new); + + /* return to data, not the header */ + return rstr_new; +} + +/** + * as_ref_string_new_copy: + * @str: a string + * + * Returns a deep copied refcounted string. The returned string can be modified + * without affecting other refcounted versions. + * + * Returns: a %AsRefString + * + * Since: 0.6.6 + */ +AsRefString * +as_ref_string_new_copy (const gchar *str) +{ + g_return_val_if_fail (str != NULL, NULL); + return as_ref_string_new_copy_with_length (str, strlen (str)); +} + +/** + * as_ref_string_new_with_length: + * @str: a string + * @len: length of @str, not including the NUL byte + * + * Returns a immutable refcounted string. The returned string cannot be modified + * without affecting other refcounted versions. + * + * Returns: a %AsRefString + * + * Since: 0.6.6 + */ +AsRefString * +as_ref_string_new_with_length (const gchar *str, gsize len) +{ + AsRefStringHeader *hdr; + GHashTable *hash = as_ref_string_get_hash (); + + g_return_val_if_fail (str != NULL, NULL); + + /* already in hash */ + if (g_hash_table_contains (hash, str)) { + hdr = AS_REFPTR_TO_HEADER (str); + g_atomic_int_inc (&hdr->refcnt); + return str; + } + return as_ref_string_new_copy_with_length (str, len); +} + +/** + * as_ref_string_new: + * @str: a string + * + * Returns a immutable refcounted string. The returned string cannot be modified + * without affecting other refcounted versions. + * + * Returns: a %AsRefString + * + * Since: 0.6.6 + */ +AsRefString * +as_ref_string_new (const gchar *str) +{ + g_return_val_if_fail (str != NULL, NULL); + return as_ref_string_new_with_length (str, strlen (str)); +} + +/** + * as_ref_string_ref: + * @rstr: a #AsRefString + * + * Adds a reference to the string. + * + * Returns: the same %AsRefString + * + * Since: 0.6.6 + */ +AsRefString * +as_ref_string_ref (AsRefString *rstr) +{ + AsRefStringHeader *hdr; + g_return_val_if_fail (rstr != NULL, NULL); + hdr = AS_REFPTR_TO_HEADER (rstr); + g_atomic_int_inc (&hdr->refcnt); + return rstr; +} + +/** + * as_ref_string_unref: + * @rstr: a #AsRefString + * + * Removes a reference to the string. + * + * Returns: the same %AsRefString, or %NULL if the refcount dropped to zero + * + * Since: 0.6.6 + */ +AsRefString * +as_ref_string_unref (AsRefString *rstr) +{ + AsRefStringHeader *hdr; + + g_return_val_if_fail (rstr != NULL, NULL); + + hdr = AS_REFPTR_TO_HEADER (rstr); + if (g_atomic_int_dec_and_test (&hdr->refcnt)) { + GHashTable *hash = as_ref_string_get_hash (); + g_hash_table_remove (hash, rstr); + return NULL; + } + return rstr; +} + +/** + * as_ref_string_assign: + * @rstr_ptr: (out): a #AsRefString + * @rstr: a #AsRefString + * + * This function unrefs and clears @rstr_ptr if set, then sets @rstr if + * non-NULL. If @rstr and @rstr_ptr are the same string the action is ignored. + * + * This function can ONLY be used when @str is guaranteed to be a + * refcounted string and is suitable for use when getting strings from + * methods without a fixed API. + * + * This function is slightly faster than as_ref_string_assign_safe() as no + * hash table lookup is done on the @rstr pointer. + * + * Since: 0.6.6 + */ +void +as_ref_string_assign (AsRefString **rstr_ptr, AsRefString *rstr) +{ + g_return_if_fail (rstr_ptr != NULL); + if (*rstr_ptr == rstr) + return; + if (*rstr_ptr != NULL) { + as_ref_string_unref (*rstr_ptr); + *rstr_ptr = NULL; + } + if (rstr != NULL) + *rstr_ptr = as_ref_string_ref (rstr); +} + +/** + * as_ref_string_assign_safe: + * @rstr_ptr: (out): a #AsRefString + * @str: a string, or a #AsRefString + * + * This function unrefs and clears @rstr_ptr if set, then sets @rstr if + * non-NULL. If @rstr and @rstr_ptr are the same string the action is ignored. + * + * This function should be used when @str cannot be guaranteed to be a + * refcounted string and is suitable for use in existing object setters. + * + * Since: 0.6.6 + */ +void +as_ref_string_assign_safe (AsRefString **rstr_ptr, const gchar *str) +{ + g_return_if_fail (rstr_ptr != NULL); + if (*rstr_ptr != NULL) { + as_ref_string_unref (*rstr_ptr); + *rstr_ptr = NULL; + } + if (str != NULL) + *rstr_ptr = as_ref_string_new (str); +} diff --git a/libappstream-glib/as-ref-string.h b/libappstream-glib/as-ref-string.h new file mode 100644 index 0000000..2da3b08 --- /dev/null +++ b/libappstream-glib/as-ref-string.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2016 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined (__APPSTREAM_GLIB_H) && !defined (AS_COMPILATION) +#error "Only <appstream-glib.h> can be included directly." +#endif + +#ifndef __AS_REF_STRING_H +#define __AS_REF_STRING_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef gchar AsRefString; + +AsRefString *as_ref_string_new (const gchar *str); +AsRefString *as_ref_string_new_with_length (const gchar *str, + gsize len); +AsRefString *as_ref_string_new_copy (const gchar *str); +AsRefString *as_ref_string_new_copy_with_length (const gchar *str, + gsize len); +AsRefString *as_ref_string_ref (AsRefString *rstr); +AsRefString *as_ref_string_unref (AsRefString *rstr); +void as_ref_string_assign (AsRefString **rstr_ptr, + AsRefString *rstr); +void as_ref_string_assign_safe (AsRefString **rstr_ptr, + const gchar *str); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(AsRefString, as_ref_string_unref) + +G_END_DECLS + +#endif /* __AS_REF_STRING_H */ diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c index b8bbf16..457a4c5 100644 --- a/libappstream-glib/as-self-test.c +++ b/libappstream-glib/as-self-test.c @@ -41,6 +41,7 @@ #include "as-node-private.h" #include "as-problem.h" #include "as-provide-private.h" +#include "as-ref-string.h" #include "as-release-private.h" #include "as-suggest-private.h" #include "as-screenshot-private.h" @@ -5045,6 +5046,32 @@ as_test_utils_unique_id_hash_safe_func (void) g_assert (found == NULL); } +static void +as_test_ref_string_func (void) +{ + const gchar *tmp; + AsRefString *rstr; + AsRefString *rstr2; + + /* basic refcounting */ + rstr = as_ref_string_new ("test"); + g_assert (rstr != NULL); + g_assert_cmpstr (rstr, ==, "test"); + g_assert (as_ref_string_ref (rstr) != NULL); + g_assert (as_ref_string_unref (rstr) != NULL); + g_assert (as_ref_string_unref (rstr) == NULL); + + /* adopting const string */ + tmp = "test"; + rstr = as_ref_string_new (tmp); + g_assert_cmpstr (rstr, ==, tmp); + rstr2 = as_ref_string_new (rstr); + g_assert_cmpstr (rstr2, ==, tmp); + g_assert (rstr == rstr2); + g_assert (as_ref_string_unref (rstr) != NULL); + g_assert (as_ref_string_unref (rstr2) == NULL); +} + int main (int argc, char **argv) { @@ -5054,6 +5081,7 @@ main (int argc, char **argv) g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); /* tests go here */ + g_test_add_func ("/AppStream/ref-string", as_test_ref_string_func); g_test_add_func ("/AppStream/utils{unique_id-hash}", as_test_utils_unique_id_hash_func); g_test_add_func ("/AppStream/utils{unique_id-hash2}", as_test_utils_unique_id_hash_safe_func); g_test_add_func ("/AppStream/utils{unique_id}", as_test_utils_unique_id_func); |