From ea7a59808a46bc54a85c43f8e2a59b9def428036 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 25 Feb 2026 11:41:30 +0100 Subject: [PATCH] refactor: optimize prof_occurrences() Optimize prof_occurrences by using pointer arithmetic and g_utf8_next_char instead of calls to g_utf8_offset_to_pointer. Improve list construction efficiency in prof_occurrences by using g_slist_prepend and g_slist_reverse instead of appends. Also fix a slight oversight that could lead to trouble: When empty search strings (e.g. empty nicks) are passed it caused matches at every position. Update the test case to test for empty nicks. --- src/common.c | 32 ++++++++++++++++++++++---------- tests/unittests/test_common.c | 5 +++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/common.c b/src/common.c index d5acb211..96f5cac6 100644 --- a/src/common.c +++ b/src/common.c @@ -578,36 +578,48 @@ is_notify_enabled(void) GSList* prof_occurrences(const char* const needle, const char* const haystack, int offset, gboolean whole_word, GSList** result) { - if (needle == NULL || haystack == NULL) { + if (needle == NULL || haystack == NULL || *needle == '\0') { return *result; } - do { - gchar* haystack_curr = g_utf8_offset_to_pointer(haystack, offset); - if (g_str_has_prefix(haystack_curr, needle)) { + size_t needle_len = strlen(needle); + gchar* p = g_utf8_offset_to_pointer(haystack, offset); + GSList* matches = NULL; + + while (p) { + if (g_str_has_prefix(p, needle)) { if (whole_word) { gunichar before = 0; - gchar* haystack_before_ch = g_utf8_find_prev_char(haystack, haystack_curr); + gchar* haystack_before_ch = g_utf8_find_prev_char(haystack, p); if (haystack_before_ch) { before = g_utf8_get_char(haystack_before_ch); } gunichar after = 0; - gchar* haystack_after_ch = haystack_curr + strlen(needle); - if (haystack_after_ch[0] != '\0') { + gchar* haystack_after_ch = p + needle_len; + if (*haystack_after_ch != '\0') { after = g_utf8_get_char(haystack_after_ch); } if (!g_unichar_isalnum(before) && !g_unichar_isalnum(after)) { - *result = g_slist_append(*result, GINT_TO_POINTER(offset)); + matches = g_slist_prepend(matches, GINT_TO_POINTER(offset)); } } else { - *result = g_slist_append(*result, GINT_TO_POINTER(offset)); + matches = g_slist_prepend(matches, GINT_TO_POINTER(offset)); } } + if (*p == '\0') { + break; + } + + p = g_utf8_next_char(p); offset++; - } while (g_strcmp0(g_utf8_offset_to_pointer(haystack, offset), "\0") != 0); + } + + if (matches) { + *result = g_slist_concat(*result, g_slist_reverse(matches)); + } return *result; } diff --git a/tests/unittests/test_common.c b/tests/unittests/test_common.c index 6e96ef18..40912679 100644 --- a/tests/unittests/test_common.c +++ b/tests/unittests/test_common.c @@ -923,6 +923,11 @@ get_mentions_tests(void** state) assert_true(_lists_equal(actual, expected)); // expected is NULL g_slist_free(actual); actual = NULL; + // Empty nick + actual = get_mentions(FALSE, TRUE, "hello", ""); + assert_true(_lists_equal(actual, expected)); // expected is NULL + g_slist_free(actual); actual = NULL; + // UTF-8 characters expected = g_slist_append(expected, GINT_TO_POINTER(0)); actual = get_mentions(TRUE, TRUE, "我能 hello", "我能");