summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-11-06 15:44:41 +0100
committerThomas Haller <thaller@redhat.com>2016-04-07 11:45:32 +0200
commitcaee4e4617a54b472b1d3455a4612d90b6a4ba1c (patch)
treecd8cfc45651884cd2447795ee0cad0df9ca2c2dd
parent1b43c880ba43260fe551b0ac923826f16737484f (diff)
downloadNetworkManager-th/util/nm-ref-string.tar.gz
NMRefString is a simple, refcounted, immutable string. Increasing/decreasing the refcount does not affect const-ness. It can be used just like a regular 'const char *' pointer. The only difference is that you need special alloc/free functions.
-rw-r--r--src/nm-core-utils.c160
-rw-r--r--src/nm-core-utils.h11
-rw-r--r--src/tests/test-general-with-expect.c52
3 files changed, 223 insertions, 0 deletions
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index 6cdd00e0d7..3529839f35 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -3018,3 +3018,163 @@ nm_utils_lifetime_get (guint32 timestamp,
return TRUE;
}
+/*****************************************************************************
+ * NMRefString
+ *****************************************************************************/
+
+#ifdef NM_MORE_ASSERTS
+#define NM_STRING_CANARY(s) (GPOINTER_TO_UINT (s) ^ ((guint) 30112031329))
+#endif
+
+typedef struct _NMString {
+#ifdef NM_STRING_CANARY
+ guint _canary;
+#endif
+ int ref_count;
+ char str[1];
+} _NMString;
+
+static inline _NMString *
+_nm_ref_string_up_cast (NMRefString nmstr)
+{
+ _NMString *s;
+
+ s = (_NMString *) (((char *) nmstr) - G_STRUCT_OFFSET (_NMString, str));
+#ifdef NM_STRING_CANARY
+ g_return_val_if_fail (s->_canary == NM_STRING_CANARY (s), NULL);
+#endif
+ g_return_val_if_fail (s->ref_count > 0, NULL);
+
+ nm_assert (s->str == nmstr);
+
+ return s;
+}
+
+NMRefString
+nm_ref_string_new (const char *str)
+{
+ _NMString *s;
+ gsize len;
+
+ if (!str)
+ return NULL;
+
+ len = strlen (str) + 1;
+
+ s = g_malloc (G_STRUCT_OFFSET (_NMString, str) + len);
+ s->ref_count = 1;
+#ifdef NM_STRING_CANARY
+ s->_canary = NM_STRING_CANARY (s);
+#endif
+ memcpy (s->str, str, len);
+ return s->str;
+}
+
+NMRefString
+nm_ref_string_ref (NMRefString nmstr)
+{
+ _NMString *s;
+
+ if (!nmstr)
+ return NULL;
+
+ s = _nm_ref_string_up_cast (nmstr);
+ g_return_val_if_fail (s, NULL);
+
+ s->ref_count++;
+ return s->str;
+}
+
+void
+nm_ref_string_unref (NMRefString nmstr)
+{
+ _NMString *s;
+
+ if (!nmstr)
+ return;
+
+ s = _nm_ref_string_up_cast (nmstr);
+ g_return_if_fail (s);
+
+ if (--s->ref_count <= 0) {
+#ifdef NM_STRING_CANARY
+ s->_canary = 0;
+#endif
+ g_free (s);
+ }
+}
+
+NMRefString
+nm_ref_string_dedup (NMRefString nmstr, const char *str)
+{
+ /* The difference between replace and dedup is, that
+ * replace in any case decrements the refcount on nmstr. Thus,
+ * with replace you hand ownership of nmstr over.
+ *
+ * with dedup, the caller keeps ownership, but if possible,
+ * the reference will be reused/shared.
+ **/
+
+ if (!nmstr)
+ return nm_ref_string_new (str);
+ if (!str)
+ return NULL;
+
+ if (strcmp (nmstr, str) == 0)
+ return nm_ref_string_ref (nmstr);
+ return nm_ref_string_new (str);
+}
+
+NMRefString
+nm_ref_string_replace (NMRefString nmstr, const char *str)
+{
+ _NMString *s, *s2;
+ gsize len;
+
+ if (!nmstr)
+ return nm_ref_string_new (str);
+ if (!str) {
+ nm_ref_string_unref (nmstr);
+ return NULL;
+ }
+
+ s = _nm_ref_string_up_cast (nmstr);
+ g_return_val_if_fail (s, NULL);
+
+ if (strcmp (s->str, str) == 0)
+ return nmstr;
+
+ if (s->ref_count == 1) {
+ len = strlen (str) + 1;
+
+ s2 = g_realloc (s, G_STRUCT_OFFSET (_NMString, str) + len);
+
+#ifdef NM_STRING_CANARY
+ s2->_canary = NM_STRING_CANARY (s2);
+#endif
+ memcpy (s2->str, str, len);
+ return s2->str;
+ } else {
+ s->ref_count--;
+ return nm_ref_string_new (str);
+ }
+}
+
+int
+nm_ref_string_cmp (NMRefString nmstr1, NMRefString nmstr2)
+{
+ if (nmstr1 == nmstr2)
+ return 0;
+ if (!nmstr1)
+ return -1;
+ if (!nmstr2)
+ return 1;
+ return strcmp (nmstr1, nmstr2);
+}
+
+gboolean
+nm_ref_string_equal (NMRefString nmstr1, NMRefString nmstr2)
+{
+ return nm_ref_string_cmp (nmstr1, nmstr2) == 0;
+}
+
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index f77b43e2a3..28235f0560 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -391,4 +391,15 @@ gboolean nm_utils_lifetime_get (guint32 timestamp,
gboolean nm_utils_ip4_address_is_link_local (in_addr_t addr);
+/*****************************************************************************/
+
+typedef const char *NMRefString;
+NMRefString nm_ref_string_new (const char *str);
+NMRefString nm_ref_string_ref (NMRefString nmstr);
+void nm_ref_string_unref (NMRefString nmstr);
+NMRefString nm_ref_string_dedup (NMRefString nmstr, const char *str);
+NMRefString nm_ref_string_replace (NMRefString nmstr, const char *str);
+int nm_ref_string_cmp (NMRefString nmstr1, NMRefString nmstr2);
+gboolean nm_ref_string_equal (NMRefString nmstr1, NMRefString nmstr2);
+
#endif /* __NM_CORE_UTILS_H__ */
diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c
index 64b47bd38c..900272510a 100644
--- a/src/tests/test-general-with-expect.c
+++ b/src/tests/test-general-with-expect.c
@@ -39,6 +39,57 @@
/*******************************************/
static void
+test_nm_ref_string (void)
+{
+ const char *nm_str, *s1, *s2;
+
+ nm_str = nm_ref_string_new ("hallo");
+
+ g_assert_cmpstr (nm_str, ==, "hallo");
+
+ nm_ref_string_ref (nm_str);
+ g_assert_cmpstr (nm_str, ==, "hallo");
+
+ nm_ref_string_unref (nm_str);
+ g_assert_cmpstr (nm_str, ==, "hallo");
+
+ nm_str = nm_ref_string_replace (nm_str, "hallo");
+ g_assert_cmpstr (nm_str, ==, "hallo");
+
+ nm_str = nm_ref_string_replace (nm_str, "hallo2");
+ g_assert_cmpstr (nm_str, ==, "hallo2");
+
+ nm_ref_string_unref (nm_str);
+
+ /* replace() reallocs old memory if ref-count is 1. */
+ s1 = nm_ref_string_new ("abcdef");
+ g_assert_cmpstr (s1, ==, "abcdef");
+ s2 = nm_ref_string_replace (s1, "ABC");
+ g_assert_cmpstr (s2, ==, "ABC");
+ nm_ref_string_unref (s2);
+
+ /* replace() reallocs old memory if ref-count is 1. */
+ s1 = nm_ref_string_new ("ABC");
+ g_assert_cmpstr (s1, ==, "ABC");
+ s2 = nm_ref_string_replace (s1, "abcdef");
+ g_assert_cmpstr (s2, ==, "abcdef");
+ nm_ref_string_unref (s2);
+
+ /* replace allocates new memory if ref-count larger 1. */
+ s1 = nm_ref_string_new ("ABC");
+ g_assert_cmpstr (s1, ==, "ABC");
+ nm_ref_string_ref (s1);
+ s2 = nm_ref_string_replace (s1, "abcdef");
+ g_assert_cmpstr (s2, ==, "abcdef");
+ g_assert_cmpstr (s1, ==, "ABC");
+ g_assert (s1 != s2);
+ nm_ref_string_unref (s2);
+ nm_ref_string_unref (s1);
+}
+
+/*******************************************/
+
+static void
test_nm_utils_monotonic_timestamp_as_boottime (void)
{
gint64 timestamp_ns_per_tick, now, now_boottime, now_boottime_2, now_boottime_3;
@@ -904,6 +955,7 @@ main (int argc, char **argv)
{
nmtst_init_assert_logging (&argc, &argv, "DEBUG", "DEFAULT");
+ g_test_add_func ("/general/nm_ref_string", test_nm_ref_string);
g_test_add_func ("/general/nm_utils_monotonic_timestamp_as_boottime", test_nm_utils_monotonic_timestamp_as_boottime);
g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child);
g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes);