diff options
author | Thomas Haller <thaller@redhat.com> | 2015-11-06 15:44:41 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2016-04-07 11:45:32 +0200 |
commit | caee4e4617a54b472b1d3455a4612d90b6a4ba1c (patch) | |
tree | cd8cfc45651884cd2447795ee0cad0df9ca2c2dd | |
parent | 1b43c880ba43260fe551b0ac923826f16737484f (diff) | |
download | NetworkManager-th/util/nm-ref-string.tar.gz |
utils: add NMRefStringth/wip/util/nm-ref-stringth/util/nm-ref-string
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.c | 160 | ||||
-rw-r--r-- | src/nm-core-utils.h | 11 | ||||
-rw-r--r-- | src/tests/test-general-with-expect.c | 52 |
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); |