diff options
author | Thomas Haller <thaller@redhat.com> | 2015-11-06 15:44:41 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2015-11-18 15:10:09 +0100 |
commit | 7acd5f6fca25141189179a4d8f23690e61e3cab2 (patch) | |
tree | c5e65687a16a16358a619a5620048fe46b03f4f9 | |
parent | e9f9e4465997fa46f9b5bc76b4ebce794ce378c1 (diff) | |
download | NetworkManager-7acd5f6fca25141189179a4d8f23690e61e3cab2.tar.gz |
core: add NMRefString
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/NetworkManagerUtils.c | 142 | ||||
-rw-r--r-- | src/NetworkManagerUtils.h | 8 | ||||
-rw-r--r-- | src/tests/test-general-with-expect.c | 52 |
3 files changed, 202 insertions, 0 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index d84bf1bc56..5c04f7fa81 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -3481,3 +3481,145 @@ nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings) g_value_take_boxed (value, strv); } + +/****************************************************************** + * 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_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/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index fddeb57d8d..c58d293ae0 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -323,4 +323,12 @@ void nm_utils_g_value_set_object_path (GValue *value, gpointer object); void nm_utils_g_value_set_object_path_array (GValue *value, GSList *objects); void nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings); +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_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 /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index c6abdccf86..7b0b432e72 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -40,6 +40,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; @@ -862,6 +913,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); |