diff options
author | Thomas Haller <thaller@redhat.com> | 2019-03-21 10:22:55 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-03-24 09:21:13 +0100 |
commit | acf1cf61cf406729ef2704e6ace22b0367c90e77 (patch) | |
tree | c15ccb7fd2c64246fcdc0b5a04908223a7466857 | |
parent | 0528c1e9787f58f1528d15bc46695b41eacd5526 (diff) | |
download | NetworkManager-acf1cf61cf406729ef2704e6ace22b0367c90e77.tar.gz |
shared: add _nm_utils_strv_cmp_n() and _nm_utils_strv_equal()
-rw-r--r-- | libnm-core/nm-core-internal.h | 1 | ||||
-rw-r--r-- | libnm-core/nm-utils.c | 24 | ||||
-rw-r--r-- | shared/nm-utils/nm-shared-utils.c | 51 | ||||
-rw-r--r-- | shared/nm-utils/nm-shared-utils.h | 12 | ||||
-rw-r--r-- | shared/nm-utils/tests/test-shared-general.c | 145 |
5 files changed, 208 insertions, 25 deletions
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index f2332f766b..c60b42c6dc 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -284,7 +284,6 @@ char ** _nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy); GPtrArray * _nm_utils_strv_to_ptrarray (char **strv); char ** _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray); -gboolean _nm_utils_strv_equal (char **strv1, char **strv2); gboolean _nm_utils_check_file (const char *filename, gint64 check_owner, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index d276cfe62e..30ed43424c 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -1034,30 +1034,6 @@ _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray) return strv; } -/** - * _nm_utils_strv_equal: - * @strv1: a string array - * @strv2: a string array - * - * Compare NULL-terminated string arrays for equality. - * - * Returns: %TRUE if the arrays are equal, %FALSE otherwise. - **/ -gboolean -_nm_utils_strv_equal (char **strv1, char **strv2) -{ - if (strv1 == strv2) - return TRUE; - - if (!strv1 || !strv2) - return FALSE; - - for ( ; *strv1 && *strv2 && !strcmp (*strv1, *strv2); strv1++, strv2++) - ; - - return !*strv1 && !*strv2; -} - static gboolean device_supports_ap_ciphers (guint32 dev_caps, guint32 ap_flags, diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index 6a43c67063..93e0b97e20 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -2318,6 +2318,57 @@ _nm_utils_strv_sort (const char **strv, gssize len) NULL); } +/** + * _nm_utils_strv_cmp_n: + * @strv1: a string array + * @len1: the length of @strv1, or -1 for NULL terminated array. + * @strv2: a string array + * @len2: the length of @strv2, or -1 for NULL terminated array. + * + * Note that + * - len == -1 && strv == NULL + * is treated like a %NULL argument and compares differently from + * other arrays. + * + * Note that an empty array can be represented as + * - len == -1 && strv && !strv[0] + * - len == 0 && !strv + * - len == 0 && strv + * These 3 forms all compare equal. + * It also means, if length is 0, then it is permissible for strv to be %NULL. + * + * The strv arrays may contain %NULL strings (if len is positive). + * + * Returns: 0 if the arrays are equal (using strcmp). + **/ +int +_nm_utils_strv_cmp_n (const char *const*strv1, + gssize len1, + const char *const*strv2, + gssize len2) +{ + gsize n, n2; + + if (len1 < 0) { + if (!strv1) + return (len2 < 0 && !strv2) ? 0 : -1; + n = NM_PTRARRAY_LEN (strv1); + } else + n = len1; + + if (len2 < 0) { + if (!strv2) + return 1; + n2 = NM_PTRARRAY_LEN (strv2); + } else + n2 = len2; + + NM_CMP_DIRECT (n, n2); + for (; n > 0; n--, strv1++, strv2++) + NM_CMP_DIRECT_STRCMP0 (*strv1, *strv2); + return 0; +} + /*****************************************************************************/ gpointer diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index 65e3495983..51e57ee03f 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -941,6 +941,18 @@ gboolean nm_utils_hash_table_equal (const GHashTable *a, void _nm_utils_strv_sort (const char **strv, gssize len); #define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort (NM_CAST_STRV_MC (strv), len) +int _nm_utils_strv_cmp_n (const char *const*strv1, + gssize len1, + const char *const*strv2, + gssize len2); + +static inline gboolean +_nm_utils_strv_equal (char **strv1, char **strv2) +{ + return _nm_utils_strv_cmp_n ((const char *const*) strv1, -1, + (const char *const*) strv2, -1) == 0; +} + /*****************************************************************************/ #define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000) diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c index d53b21d972..5b220c10e0 100644 --- a/shared/nm-utils/tests/test-shared-general.c +++ b/shared/nm-utils/tests/test-shared-general.c @@ -248,6 +248,150 @@ test_unaligned (void) /*****************************************************************************/ +static void +_strv_cmp_fuzz_input (const char *const*in, + gssize l, + const char ***out_strv_free_shallow, + char ***out_strv_free_deep, + const char *const* *out_s1, + const char *const* *out_s2) +{ + const char **strv; + gsize i; + + /* Fuzz the input argument. It will return two output arrays that are semantically + * equal the input. */ + + if (nmtst_get_rand_bool ()) { + char **ss; + + if (l < 0) + ss = g_strdupv ((char **) in); + else if (l == 0) { + ss = nmtst_get_rand_bool () + ? NULL + : g_new0 (char *, 1); + } else { + ss = nm_memdup (in, sizeof (const char *) * l); + for (i = 0; i < (gsize) l; i++) + ss[i] = g_strdup (ss[i]); + } + strv = (const char **) ss; + *out_strv_free_deep = ss; + } else { + if (l < 0) { + strv = in + ? nm_memdup (in, sizeof (const char *) * (NM_PTRARRAY_LEN (in) + 1)) + : NULL; + } else if (l == 0) { + strv = nmtst_get_rand_bool () + ? NULL + : g_new0 (const char *, 1); + } else + strv = nm_memdup (in, sizeof (const char *) * l); + *out_strv_free_shallow = strv; + } + + *out_s1 = in; + *out_s2 = strv; + + if (nmtst_get_rand_bool ()) { + /* randomly swap the original and the clone. That means, out_s1 is either + * the input argument (as-is) or the sementically equal clone. */ + NMTST_SWAP (*out_s1, *out_s2); + } + if (nmtst_get_rand_bool ()) { + /* randomly make s1 and s2 the same. This is for testing that + * comparing two identical pointers yields the same result. */ + *out_s2 = *out_s1; + } +} + +static void +_strv_cmp_free_deep (char **strv, + gssize len) +{ + gssize i; + + if (strv) { + if (len < 0) + g_strfreev (strv); + else { + for (i = 0; i < len; i++) + g_free (strv[i]); + g_free (strv); + } + } +} + +static void +test_strv_cmp (void) +{ + const char *const strv0[1] = { }; + const char *const strv1[2] = { "", }; + +#define _STRV_CMP(a1, l1, a2, l2, equal) \ + G_STMT_START { \ + gssize _l1 = (l1); \ + gssize _l2 = (l2); \ + const char *const*_a1; \ + const char *const*_a2; \ + const char *const*_a1x; \ + const char *const*_a2x; \ + char **_a1_free_deep = NULL; \ + char **_a2_free_deep = NULL; \ + gs_free const char **_a1_free_shallow = NULL; \ + gs_free const char **_a2_free_shallow = NULL; \ + int _c1, _c2; \ + \ + _strv_cmp_fuzz_input ((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x); \ + _strv_cmp_fuzz_input ((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x); \ + \ + _c1 = _nm_utils_strv_cmp_n (_a1, _l1, _a2, _l2); \ + _c2 = _nm_utils_strv_cmp_n (_a2, _l2, _a1, _l1); \ + if (equal) { \ + g_assert_cmpint (_c1, ==, 0); \ + g_assert_cmpint (_c2, ==, 0); \ + } else { \ + g_assert_cmpint (_c1, ==, -1); \ + g_assert_cmpint (_c2, ==, 1); \ + } \ + \ + /* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x). + * Either way, the arrays must compare equal to their semantically equal alternative. */ \ + g_assert_cmpint (_nm_utils_strv_cmp_n (_a1, _l1, _a1x, _l1), ==, 0); \ + g_assert_cmpint (_nm_utils_strv_cmp_n (_a2, _l2, _a2x, _l2), ==, 0); \ + \ + _strv_cmp_free_deep (_a1_free_deep, _l1); \ + _strv_cmp_free_deep (_a2_free_deep, _l2); \ + } G_STMT_END + + _STRV_CMP (NULL, -1, NULL, -1, TRUE); + + _STRV_CMP (NULL, -1, NULL, 0, FALSE); + _STRV_CMP (NULL, -1, strv0, 0, FALSE); + _STRV_CMP (NULL, -1, strv0, -1, FALSE); + + _STRV_CMP (NULL, 0, NULL, 0, TRUE); + _STRV_CMP (NULL, 0, strv0, 0, TRUE); + _STRV_CMP (NULL, 0, strv0, -1, TRUE); + _STRV_CMP (strv0, 0, strv0, 0, TRUE); + _STRV_CMP (strv0, 0, strv0, -1, TRUE); + _STRV_CMP (strv0, -1, strv0, -1, TRUE); + + _STRV_CMP (NULL, 0, strv1, -1, FALSE); + _STRV_CMP (NULL, 0, strv1, 1, FALSE); + _STRV_CMP (strv0, 0, strv1, -1, FALSE); + _STRV_CMP (strv0, 0, strv1, 1, FALSE); + _STRV_CMP (strv0, -1, strv1, -1, FALSE); + _STRV_CMP (strv0, -1, strv1, 1, FALSE); + + _STRV_CMP (strv1, -1, strv1, 1, TRUE); + _STRV_CMP (strv1, 1, strv1, 1, TRUE); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -261,6 +405,7 @@ int main (int argc, char **argv) g_test_add_func ("/general/test_nm_strndup_a", test_nm_strndup_a); g_test_add_func ("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost); g_test_add_func ("/general/test_unaligned", test_unaligned); + g_test_add_func ("/general/test_strv_cmp", test_strv_cmp); return g_test_run (); } |