glib-aux: extend nm_uuid_generate_from_strings() to honor NULL values

When you call

  nm_uuid_generate_from_strings_strv(uuid_type, type_arg, v1, v2);

you'd probably expect that both values are honored in some way.
However, if v1 happened to be NULL, then previously v2 would be ignored.

Extend nm_uuid_generate_from_strings() to accept also NULL values and
pass on the length. Currently, there are no users of nm_uuid_generate_from_strings(),
so nobody is affected by this change.

Also extend nm_uuid_generate_from_strings_strv() to take a length
argument. It still accepts "-1" to take the input strv as a NULL
terminated array.

If a positive length is provided to nm_uuid_generate_from_strings_strv(),
it hashes the same UUID as the respective NULL terminated array. But of
course only, if there is no NULL inside the array. If there are any
NULLs, a distinct UUID gets generated.
This commit is contained in:
Thomas Haller 2022-10-07 15:13:03 +02:00
parent b5e7e48bc1
commit 2d8651ba91
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
3 changed files with 119 additions and 35 deletions

View file

@ -7946,16 +7946,50 @@ test_nm_utils_uuid_generate_from_string(void)
/*****************************************************************************/ /*****************************************************************************/
static void static void
_check_uuid(NMUuidType uuid_type, _check_uuid(NMUuidType uuid_type,
const NMUuid *type_arg, const NMUuid *type_arg,
const char *expected_uuid, const char *expected_uuid,
const char *str, const char *str,
gssize slen, gssize slen,
char *uuid_test) const char *const *strv,
gssize strv_len)
{ {
gs_free char *uuid_test = NULL;
gs_free char *uuid_test2 = NULL;
gboolean uuid_test2_valid = TRUE;
g_assert(str);
g_assert(strv_len < 0 || strv);
uuid_test = nm_uuid_generate_from_strings_strv(uuid_type, type_arg, strv, strv_len);
g_assert(uuid_test); g_assert(uuid_test);
g_assert(nm_uuid_is_normalized(uuid_test)); g_assert(nm_uuid_is_normalized(uuid_test));
g_assert(str);
if (strv_len < 0 && strv) {
uuid_test2 =
nm_uuid_generate_from_strings_strv(uuid_type, type_arg, strv, NM_PTRARRAY_LEN(strv));
} else if (strv_len >= 0) {
gssize l = nm_strv_find_first(strv, strv_len, NULL);
gs_free const char **strv2 = nm_strv_dup_packed(strv, l < 0 ? strv_len : l);
uuid_test2 = nm_uuid_generate_from_strings_strv(uuid_type,
type_arg,
strv2 ?: NM_STRV_EMPTY_CC(),
-1);
if (l >= 0) {
/* there are NULL strings. The result won't be match. */
uuid_test2_valid = FALSE;
}
}
if (uuid_test2) {
if (uuid_test2_valid)
g_assert_cmpstr(uuid_test, ==, uuid_test2);
else {
g_assert(nm_uuid_is_normalized(uuid_test));
g_assert_cmpstr(uuid_test, !=, uuid_test2);
}
}
if (!nm_streq(uuid_test, expected_uuid)) { if (!nm_streq(uuid_test, expected_uuid)) {
g_error("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s", g_error("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s",
@ -7978,24 +8012,19 @@ _check_uuid(NMUuidType uuid_type,
expected_uuid, expected_uuid,
uuid_test); uuid_test);
} }
g_free(uuid_test);
} }
#define check_uuid(uuid_type, type_arg, expected_uuid, str, ...) \ #define check_uuid(uuid_type, type_arg, expected_uuid, str, ...) \
({ \ ({ \
const NMUuidType _uuid_type = (uuid_type); \ const NMUuidType _uuid_type = (uuid_type); \
const NMUuid *_type_arg = type_arg; \ const NMUuid *_type_arg = type_arg; \
const char *_expected_uuid = (expected_uuid); \ const char *_expected_uuid = (expected_uuid); \
const char *_str = (str); \ const char *_str = (str); \
const gsize _strlen = NM_STRLEN(str); \ const gsize _strlen = NM_STRLEN(str); \
\ const char *const *_strv = NM_MAKE_STRV(__VA_ARGS__); \
_check_uuid( \ const gssize _strv_len = NM_NARG(__VA_ARGS__); \
_uuid_type, \ \
_type_arg, \ _check_uuid(_uuid_type, _type_arg, _expected_uuid, _str, _strlen, _strv, _strv_len); \
_expected_uuid, \
_str, \
_strlen, \
nm_uuid_generate_from_strings_strv(_uuid_type, _type_arg, NM_MAKE_STRV(__VA_ARGS__))); \
}) })
static void static void
@ -8023,17 +8052,19 @@ test_nm_utils_uuid_generate_from_strings(void)
"457229f4-fe49-32f5-8b09-c531d81f44d9", "457229f4-fe49-32f5-8b09-c531d81f44d9",
"x", "x",
1, 1,
nm_uuid_generate_from_strings_strv(NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1, NULL)); NULL,
check_uuid(NM_UUID_TYPE_VERSION3, -1);
&nm_uuid_ns_1, check_uuid(NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1, "b07c334a-399b-32de-8d50-58e4e08f98e3", "");
"b07c334a-399b-32de-8d50-58e4e08f98e3",
"",
NULL);
check_uuid(NM_UUID_TYPE_VERSION3, check_uuid(NM_UUID_TYPE_VERSION3,
&nm_uuid_ns_1, &nm_uuid_ns_1,
"b8a426cb-bcb5-30a3-bd8f-6786fea72df9", "b8a426cb-bcb5-30a3-bd8f-6786fea72df9",
"\0", "\0",
""); "");
check_uuid(NM_UUID_TYPE_VERSION3,
&nm_uuid_ns_1,
"9232afda-85fc-3b8f-8736-4f99c8d5db9c",
"_n",
NULL);
check_uuid(NM_UUID_TYPE_VERSION3, check_uuid(NM_UUID_TYPE_VERSION3,
&nm_uuid_ns_1, &nm_uuid_ns_1,
"12a4a982-7aae-39e1-951e-41aeb1250959", "12a4a982-7aae-39e1-951e-41aeb1250959",
@ -8074,6 +8105,13 @@ test_nm_utils_uuid_generate_from_strings(void)
"a\0a\0", "a\0a\0",
"a", "a",
"a"); "a");
check_uuid(NM_UUID_TYPE_VERSION3,
&nm_uuid_ns_1,
"f36cec99-1db8-3baa-8c3f-13e13d980318",
"a\0a\0001_1n",
"a",
NULL,
"a");
check_uuid(NM_UUID_TYPE_VERSION3, check_uuid(NM_UUID_TYPE_VERSION3,
&nm_uuid_ns_1, &nm_uuid_ns_1,
"fd698d86-1b60-3ebe-855f-7aada9950a8d", "fd698d86-1b60-3ebe-855f-7aada9950a8d",
@ -8117,6 +8155,13 @@ test_nm_utils_uuid_generate_from_strings(void)
"\0b\0", "\0b\0",
"", "",
"b"); "b");
check_uuid(NM_UUID_TYPE_VERSION5,
_uuid(NM_UUID_NS_URL),
"db3dfd17-c785-509d-a0ca-740fdd68dc68",
"\0b\00011_n",
"",
"b",
NULL);
check_uuid(NM_UUID_TYPE_VERSION3, check_uuid(NM_UUID_TYPE_VERSION3,
_uuid(NM_UUID_NS_URL), _uuid(NM_UUID_NS_URL),
"916dcdd8-5042-3b9b-9763-4312a31e5735", "916dcdd8-5042-3b9b-9763-4312a31e5735",

View file

@ -413,6 +413,10 @@ nm_uuid_generate_from_string_str(const char *s,
* @type_args: the namespace UUID. * @type_args: the namespace UUID.
* @strv: (allow-none): the strv list to hash. Can be NULL, in which * @strv: (allow-none): the strv list to hash. Can be NULL, in which
* case the result is different from an empty array. * case the result is different from an empty array.
* @len: if negative, @strv is a NULL terminated array. Otherwise,
* it is the length of the strv array. In the latter case it may
* also contain NULL strings. The result hashes differently depending
* on whether we have a NULL terminated strv array or given length.
* *
* Returns a @uuid_type UUID based on the concatenated C strings. * Returns a @uuid_type UUID based on the concatenated C strings.
* It does not simply concatenate them, but also includes the * It does not simply concatenate them, but also includes the
@ -425,13 +429,43 @@ nm_uuid_generate_from_string_str(const char *s,
char * char *
nm_uuid_generate_from_strings_strv(NMUuidType uuid_type, nm_uuid_generate_from_strings_strv(NMUuidType uuid_type,
const NMUuid *type_args, const NMUuid *type_args,
const char *const *strv) const char *const *strv,
gssize len)
{ {
nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_232, TRUE); nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_232, TRUE);
gsize slen; gsize slen;
const char *s; const char *s;
if (!strv) { if (len >= 0) {
gboolean has_nulls = FALSE;
gssize i;
nm_assert(len == 0 || strv);
for (i = 0; i < len; i++) {
if (strv[i])
nm_str_buf_append_len(&str, strv[i], strlen(strv[i]) + 1u);
else
has_nulls = TRUE;
}
if (has_nulls) {
/* We either support a NULL terminated strv array, or a ptr array of fixed
* length (@len argument).
*
* If there are no NULLs within the first @len strings, then the result
* is the same. If there are any NULL strings, we need to encode that
* in a unique way. We do that by appending a bitmap of the elements
* whether they were set, plus one 'n' character (without NUL termination).
* None of the other branches below hashes to that, so this will uniquely
* encoded the NULL strings.
*/
for (i = 0; i < len; i++)
nm_str_buf_append_c(&str, strv[i] ? '1' : '_');
nm_str_buf_append_c(&str, 'n');
}
slen = str.len;
s = nm_str_buf_get_str_unsafe(&str);
} else if (!strv) {
/* NULL is treated differently from an empty strv. We achieve that /* NULL is treated differently from an empty strv. We achieve that
* by using a non-empty, non-NUL terminated string (which cannot happen * by using a non-empty, non-NUL terminated string (which cannot happen
* in the other cases). */ * in the other cases). */

View file

@ -125,16 +125,21 @@ char *nm_uuid_generate_from_string_str(const char *s,
char *nm_uuid_generate_from_strings_strv(NMUuidType uuid_type, char *nm_uuid_generate_from_strings_strv(NMUuidType uuid_type,
const NMUuid *type_args, const NMUuid *type_args,
const char *const *strv); const char *const *strv,
gssize len);
#define nm_uuid_generate_from_strings(uuid_type, type_args, ...) \ #define nm_uuid_generate_from_strings(uuid_type, type_args, ...) \
nm_uuid_generate_from_strings_strv((uuid_type), (type_args), NM_MAKE_STRV(__VA_ARGS__)) nm_uuid_generate_from_strings_strv((uuid_type), \
(type_args), \
NM_MAKE_STRV(__VA_ARGS__), \
NM_NARG(__VA_ARGS__))
/* Legacy function. Don't use for new code. */ /* Legacy function. Don't use for new code. */
#define nm_uuid_generate_from_strings_old(uuid_type, type_args, ...) \ #define nm_uuid_generate_from_strings_old(uuid_type, type_args, ...) \
nm_uuid_generate_from_strings_strv(NM_UUID_TYPE_VERSION3, \ nm_uuid_generate_from_strings_strv(NM_UUID_TYPE_VERSION3, \
&nm_uuid_ns_1, \ &nm_uuid_ns_1, \
NM_MAKE_STRV(__VA_ARGS__)) NM_MAKE_STRV(__VA_ARGS__), \
-1)
/*****************************************************************************/ /*****************************************************************************/