summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2016-11-16 12:33:35 +0000
committerRichard Hughes <richard@hughsie.com>2016-11-21 15:19:55 +0000
commit977f0cc2623dc89e68b4c0d41b0164c59ac9a1ca (patch)
tree0f784c85ae1f2dc25be4a0db1d48fd4b9eacd457
parent9f52dca956a60d7921c01bff37730e5a8790f987 (diff)
downloadappstream-glib-977f0cc2623dc89e68b4c0d41b0164c59ac9a1ca.tar.gz
Add a refcounted overloaded C string object
-rw-r--r--libappstream-glib/Makefile.am2
-rw-r--r--libappstream-glib/as-ref-string.c263
-rw-r--r--libappstream-glib/as-ref-string.h52
-rw-r--r--libappstream-glib/as-self-test.c28
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);