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.
This commit is contained in:
Michael Vetter
2026-02-25 11:41:30 +01:00
parent ca8cc8f651
commit ea7a59808a
2 changed files with 27 additions and 10 deletions

View File

@@ -578,36 +578,48 @@ is_notify_enabled(void)
GSList* GSList*
prof_occurrences(const char* const needle, const char* const haystack, int offset, gboolean whole_word, GSList** result) 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; return *result;
} }
do { size_t needle_len = strlen(needle);
gchar* haystack_curr = g_utf8_offset_to_pointer(haystack, offset); gchar* p = g_utf8_offset_to_pointer(haystack, offset);
if (g_str_has_prefix(haystack_curr, needle)) { GSList* matches = NULL;
while (p) {
if (g_str_has_prefix(p, needle)) {
if (whole_word) { if (whole_word) {
gunichar before = 0; 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) { if (haystack_before_ch) {
before = g_utf8_get_char(haystack_before_ch); before = g_utf8_get_char(haystack_before_ch);
} }
gunichar after = 0; gunichar after = 0;
gchar* haystack_after_ch = haystack_curr + strlen(needle); gchar* haystack_after_ch = p + needle_len;
if (haystack_after_ch[0] != '\0') { if (*haystack_after_ch != '\0') {
after = g_utf8_get_char(haystack_after_ch); after = g_utf8_get_char(haystack_after_ch);
} }
if (!g_unichar_isalnum(before) && !g_unichar_isalnum(after)) { 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 { } 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++; 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; return *result;
} }

View File

@@ -923,6 +923,11 @@ get_mentions_tests(void** state)
assert_true(_lists_equal(actual, expected)); // expected is NULL assert_true(_lists_equal(actual, expected)); // expected is NULL
g_slist_free(actual); actual = 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 // UTF-8 characters
expected = g_slist_append(expected, GINT_TO_POINTER(0)); expected = g_slist_append(expected, GINT_TO_POINTER(0));
actual = get_mentions(TRUE, TRUE, "我能 hello", "我能"); actual = get_mentions(TRUE, TRUE, "我能 hello", "我能");