Mon, 12 Sep 2022 22:40:17 -0500
Remove PurpleTrie, apparently we stopped using this awhile ago.
Testing Done:
Compiled
Reviewed at https://reviews.imfreedom.org/r/1758/
| libpurple/meson.build | file | annotate | diff | comparison | revisions | |
| libpurple/tests/meson.build | file | annotate | diff | comparison | revisions | |
| libpurple/tests/test_trie.c | file | annotate | diff | comparison | revisions | |
| libpurple/trie.c | file | annotate | diff | comparison | revisions | |
| libpurple/trie.h | file | annotate | diff | comparison | revisions | |
| po/POTFILES.in | file | annotate | diff | comparison | revisions |
--- a/libpurple/meson.build Mon Sep 12 22:24:33 2022 -0500 +++ b/libpurple/meson.build Mon Sep 12 22:40:17 2022 -0500 @@ -96,7 +96,6 @@ 'signals.c', 'status.c', 'stun.c', - 'trie.c', 'upnp.c', 'util.c', 'version.c', @@ -201,7 +200,6 @@ 'status.h', 'stun.h', 'tests.h', - 'trie.h', 'upnp.h', 'util.h', 'xfer.h',
--- a/libpurple/tests/meson.build Mon Sep 12 22:24:33 2022 -0500 +++ b/libpurple/tests/meson.build Mon Sep 12 22:40:17 2022 -0500 @@ -17,7 +17,6 @@ 'protocol_xfer', 'purplepath', 'queued_output_stream', - 'trie', 'util', 'whiteboard_manager', 'xmlnode',
--- a/libpurple/tests/test_trie.c Mon Sep 12 22:24:33 2022 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,372 +0,0 @@ -/* - * Purple - * - * Purple is the legal property of its developers, whose names are too - * numerous to list here. Please refer to the COPYRIGHT file distributed - * with this source distribution - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include <glib.h> - -#include <purple.h> - -static gint find_sum; - -static gboolean -test_trie_replace_cb(GString *out, const gchar *word, gpointer word_data, - gpointer user_data) -{ - /* the "test" word for the test_trie_replace test */ - if ((gintptr)word_data == 0x1001) - return FALSE; - - g_string_append_printf(out, "[%d:%x]", - (int)(gintptr)user_data, (int)(gintptr)word_data); - - return TRUE; -} - -static gboolean -test_trie_find_cb(const gchar *word, gpointer word_data, - gpointer user_data) -{ - if ((gintptr)word_data == 0x7004) - return FALSE; - - find_sum += (gintptr)word_data; - find_sum -= (gintptr)user_data * 0x1000; - - return TRUE; -} - -static void -test_trie_replace_normal(void) { - PurpleTrie *trie; - const gchar *in; - gchar *out; - - trie = purple_trie_new(); - purple_trie_set_reset_on_match(trie, FALSE); - - purple_trie_add(trie, "test", (gpointer)0x1001); - purple_trie_add(trie, "testing", (gpointer)0x1002); - purple_trie_add(trie, "overtested", (gpointer)0x1003); - purple_trie_add(trie, "trie", (gpointer)0x1004); - purple_trie_add(trie, "tree", (gpointer)0x1005); - purple_trie_add(trie, "implement", (gpointer)0x1006); - purple_trie_add(trie, "implementation", (gpointer)0x1007); - - in = "Alice is testing her trie implementation, " - "but she's far away from making test tree overtested"; - - out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)1); - - g_assert_cmpstr( - "Alice is [1:1002] her [1:1004] [1:1006]ation," - " but she's far away from making test [1:1005] [1:1003]", - ==, - out - ); - - g_object_unref(trie); - g_free(out); -} - -static void -test_trie_replace_whole(void) { - PurpleTrie *trie; - const gchar *in; - gchar *out; - - trie = purple_trie_new(); - - purple_trie_add(trie, "test", (gpointer)0x2002); - - in = "test"; - - out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)2); - - g_assert_cmpstr("[2:2002]", ==, out); - - g_object_unref(trie); - g_free(out); -} - -static void -test_trie_replace_inner(void) { - PurpleTrie *trie; - const gchar *in; - gchar *out; - - trie = purple_trie_new(); - - purple_trie_add(trie, "est", (gpointer)0x3001); - purple_trie_add(trie, "tester", (gpointer)0x3002); - - in = "the test!"; - - out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)3); - - g_assert_cmpstr("the t[3:3001]!", ==, out); - - g_object_unref(trie); - g_free(out); -} - - -static void -test_trie_replace_empty(void) { - PurpleTrie *trie; - const gchar *in; - gchar *out; - - trie = purple_trie_new(); - - purple_trie_add(trie, "test", (gpointer)0x4001); - - in = ""; - - out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)4); - - g_assert_cmpstr("", ==, out); - - g_object_unref(trie); - g_free(out); -} - -static void -test_trie_multi_replace(void) { - PurpleTrie *trie1, *trie2, *trie3; - GSList *tries = NULL; - const gchar *in; - gchar *out; - - trie1 = purple_trie_new(); - trie2 = purple_trie_new(); - trie3 = purple_trie_new(); - - /* appending is not efficient, but we have only 3 elements */ - tries = g_slist_append(tries, trie1); - tries = g_slist_append(tries, trie2); - tries = g_slist_append(tries, trie3); - - purple_trie_add(trie1, "test", (gpointer)0x5011); - purple_trie_add(trie1, "trie1", (gpointer)0x5012); - purple_trie_add(trie1, "Alice", (gpointer)0x5013); - - purple_trie_add(trie2, "test", (gpointer)0x5021); - purple_trie_add(trie2, "trie2", (gpointer)0x5022); - purple_trie_add(trie2, "example", (gpointer)0x5023); - purple_trie_add(trie2, "Ali", (gpointer)0x5024); - - /* "tester" without last (accepting) letter of "test" */ - purple_trie_add(trie3, "teser", (gpointer)0x5031); - purple_trie_add(trie3, "trie3", (gpointer)0x5032); - purple_trie_add(trie3, "tester", (gpointer)0x5033); - purple_trie_add(trie3, "example", (gpointer)0x5034); - purple_trie_add(trie3, "Al", (gpointer)0x5035); - - in = "test tester trie trie1 trie2 trie3 example Alice"; - - out = purple_trie_multi_replace(tries, in, - test_trie_replace_cb, (gpointer)5); - - g_assert_cmpstr( - "[5:5011] [5:5011]er trie [5:5012] [5:5022] " - "[5:5032] [5:5023] [5:5035]ice", - ==, - out - ); - - g_slist_free_full(tries, g_object_unref); - g_free(out); -} - -static void -test_trie_remove(void) { - PurpleTrie *trie; - const gchar *in; - gchar *out; - - trie = purple_trie_new(); - - purple_trie_add(trie, "alice", (gpointer)0x6001); - purple_trie_add(trie, "bob", (gpointer)0x6002); - purple_trie_add(trie, "cherry", (gpointer)0x6003); - - purple_trie_remove(trie, "bob"); - - in = "alice bob cherry"; - - out = purple_trie_replace(trie, in, test_trie_replace_cb, (gpointer)6); - - g_assert_cmpstr("[6:6001] bob [6:6003]", ==, out); - - g_object_unref(trie); - g_free(out); -} - -static void -test_trie_find_normal(void) { - PurpleTrie *trie; - const gchar *in; - gint out; - - trie = purple_trie_new(); - - purple_trie_add(trie, "alice", (gpointer)0x7001); - purple_trie_add(trie, "bob", (gpointer)0x7002); - purple_trie_add(trie, "cherry", (gpointer)0x7003); - purple_trie_add(trie, "al", (gpointer)0x7004); /* not accepted */ - - in = "test alice bob test cherry alice"; - - find_sum = 0; - out = purple_trie_find(trie, in, test_trie_find_cb, (gpointer)7); - - g_assert_cmpint(4, ==, out); - g_assert_cmpint(2 * 1 + 2 + 3, ==, find_sum); - - g_object_unref(trie); -} - -static void -test_trie_find_reset(void) { - PurpleTrie *trie; - const gchar *in; - gint out; - - trie = purple_trie_new(); - purple_trie_set_reset_on_match(trie, TRUE); - - purple_trie_add(trie, "alice", (gpointer)0x8001); - purple_trie_add(trie, "ali", (gpointer)0x8002); - purple_trie_add(trie, "al", (gpointer)0x8003); - - in = "al ali alice"; - - find_sum = 0; - out = purple_trie_find(trie, in, test_trie_find_cb, (gpointer)8); - - g_assert_cmpint(3, ==, out); - g_assert_cmpint(3 * 3, ==, find_sum); - - g_object_unref(trie); -} - -static void -test_trie_find_noreset(void) { - PurpleTrie *trie; - const gchar *in; - gint out; - - trie = purple_trie_new(); - purple_trie_set_reset_on_match(trie, FALSE); - - purple_trie_add(trie, "alice", (gpointer)0x9001); - purple_trie_add(trie, "ali", (gpointer)0x9002); - purple_trie_add(trie, "al", (gpointer)0x9003); - - in = "al ali alice"; - - find_sum = 0; - out = purple_trie_find(trie, in, test_trie_find_cb, (gpointer)9); - - g_assert_cmpint(6, ==, out); - g_assert_cmpint(3 * 3 + 2 * 2 + 1, ==, find_sum); - - g_object_unref(trie); -} - -static void -test_trie_multi_find(void) { - PurpleTrie *trie1, *trie2, *trie3; - GSList *tries = NULL; - const gchar *in; - int out; - - trie1 = purple_trie_new(); - trie2 = purple_trie_new(); - trie3 = purple_trie_new(); - purple_trie_set_reset_on_match(trie1, FALSE); - purple_trie_set_reset_on_match(trie2, TRUE); - purple_trie_set_reset_on_match(trie3, FALSE); - - /* appending is not efficient, but we have only 3 elements */ - tries = g_slist_append(tries, trie1); - tries = g_slist_append(tries, trie2); - tries = g_slist_append(tries, trie3); - - purple_trie_add(trie1, "test", (gpointer)0x10011); - purple_trie_add(trie1, "trie1", (gpointer)0x10012); - purple_trie_add(trie1, "Alice", (gpointer)0x10013); - - purple_trie_add(trie2, "test", (gpointer)0x10021); - purple_trie_add(trie2, "trie2", (gpointer)0x10022); - purple_trie_add(trie2, "example", (gpointer)0x10023); - purple_trie_add(trie2, "Ali", (gpointer)0x10024); - - /* "tester" without last (accepting) letter of "test" */ - purple_trie_add(trie3, "teser", (gpointer)0x10031); - purple_trie_add(trie3, "trie3", (gpointer)0x10032); - purple_trie_add(trie3, "tester", (gpointer)0x10033); - purple_trie_add(trie3, "example", (gpointer)0x10034); - purple_trie_add(trie3, "Al", (gpointer)0x10035); - - in = "test tester trie trie1 trie2 trie3 example Alice"; - - out = purple_trie_multi_find(tries, in, - test_trie_find_cb, (gpointer)0x10); - - g_assert_cmpint(9, ==, out); - g_assert_cmpint(2 * 0x11 + 0x33 + 0x12 + 0x22 + - 0x32 + 0x23 + 0x35 + 0x13, ==, find_sum); - - g_slist_free_full(tries, g_object_unref); -} - -gint -main(gint argc, gchar **argv) { - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/trie/replace/normal", - test_trie_replace_normal); - g_test_add_func("/trie/replace/whole", - test_trie_replace_whole); - g_test_add_func("/trie/replace/inner", - test_trie_replace_inner); - g_test_add_func("/trie/replace/empty", - test_trie_replace_empty); - - g_test_add_func("/trie/multi_replace", - test_trie_multi_replace); - - g_test_add_func("/trie/remove", - test_trie_remove); - - g_test_add_func("/trie/find/normal", - test_trie_find_normal); - g_test_add_func("/trie/find/reset", - test_trie_find_reset); - g_test_add_func("/trie/find/noreset", - test_trie_find_noreset); - - g_test_add_func("/trie/multi_find", - test_trie_multi_find); - - return g_test_run(); -}
--- a/libpurple/trie.c Mon Sep 12 22:24:33 2022 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,853 +0,0 @@ -/* - * Purple - * - * Purple is the legal property of its developers, whose names are too - * numerous to list here. Please refer to the COPYRIGHT file distributed - * with this source distribution - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "trie.h" - -#include <string.h> - -#include "debug.h" -#include "memorypool.h" - -/* A single internal (that don't have any children) consists - * of 256 + 4 pointers. That's 1040 bytes on 32-bit machine or 2080 bytes - * on 64-bit. - * - * Thus, in 10500-byte pool block we can hold about 5-10 internal states. - * Threshold of 100 states means, we'd need 10-20 "small" blocks before - * switching to ~1-2 large blocks. - */ -#define PURPLE_TRIE_LARGE_THRESHOLD 100 -#define PURPLE_TRIE_STATES_SMALL_POOL_BLOCK_SIZE 10880 -#define PURPLE_TRIE_STATES_LARGE_POOL_BLOCK_SIZE 102400 - -typedef struct _PurpleTrieRecord PurpleTrieRecord; -typedef struct _PurpleTrieState PurpleTrieState; -typedef struct _PurpleTrieRecordList PurpleTrieRecordList; - -struct _PurpleTrie -{ - GObject parent; -}; - -typedef struct -{ - gboolean reset_on_match; - - PurpleMemoryPool *records_str_mempool; - PurpleMemoryPool *records_obj_mempool; - PurpleTrieRecordList *records; - GHashTable *records_map; - gsize records_total_size; - - PurpleMemoryPool *states_mempool; - PurpleTrieState *root_state; -} PurpleTriePrivate; - -struct _PurpleTrieRecord -{ - gchar *word; - guint word_len; - gpointer data; -}; - -struct _PurpleTrieRecordList -{ - PurpleTrieRecord *rec; - PurpleTrieRecordList *next; - PurpleTrieRecordList *prev; - - gpointer extra_data; -}; - -struct _PurpleTrieState -{ - PurpleTrieState *parent; - PurpleTrieState **children; - - PurpleTrieState *longest_suffix; - - PurpleTrieRecord *found_word; -}; - -typedef struct -{ - PurpleTrieState *state; - - PurpleTrieState *root_state; - gboolean reset_on_match; - - PurpleTrieReplaceCb replace_cb; - PurpleTrieFindCb find_cb; - gpointer user_data; -} PurpleTrieMachine; - -/* TODO: an option to make it eager or lazy (now, it's eager) */ -enum -{ - PROP_ZERO, - PROP_RESET_ON_MATCH, - PROP_LAST -}; - -static GParamSpec *properties[PROP_LAST]; - -G_DEFINE_TYPE_WITH_PRIVATE(PurpleTrie, purple_trie, G_TYPE_OBJECT); - -/******************************************************************************* - * Records list - ******************************************************************************/ - -static PurpleTrieRecordList * -purple_record_list_new(PurpleMemoryPool *mpool, PurpleTrieRecord *rec) -{ - PurpleTrieRecordList *node; - - node = purple_memory_pool_alloc0(mpool, - sizeof(PurpleTrieRecordList), sizeof(gpointer)); - g_return_val_if_fail(node != NULL, NULL); - - node->rec = rec; - - return node; -} - -static PurpleTrieRecordList * -purple_record_list_prepend(PurpleMemoryPool *mpool, - PurpleTrieRecordList *old_head, PurpleTrieRecord *rec) -{ - PurpleTrieRecordList *new_head; - - new_head = purple_record_list_new(mpool, rec); - g_return_val_if_fail(new_head != NULL, NULL); - - new_head->next = old_head; - if (old_head) - old_head->prev = new_head; - - return new_head; -} - -static PurpleTrieRecordList * -purple_record_list_copy(PurpleMemoryPool *mpool, - const PurpleTrieRecordList *head) -{ - PurpleTrieRecordList *new_head = NULL, *new_tail = NULL; - - while (head) { - PurpleTrieRecordList *node; - - node = purple_record_list_new(mpool, head->rec); - g_return_val_if_fail(node != NULL, NULL); /* there is no leak */ - - node->prev = new_tail; - if (new_tail) - new_tail->next = node; - new_tail = node; - if (!new_head) - new_head = node; - - head = head->next; - } - - return new_head; -} - -static PurpleTrieRecordList * -purple_record_list_remove(PurpleTrieRecordList *head, - PurpleTrieRecordList *node) -{ - g_return_val_if_fail(head != NULL, NULL); - g_return_val_if_fail(node != NULL, head); - g_return_val_if_fail(head->prev == NULL, NULL); - - if (head == node) { - if (head->next != NULL) - head->next->prev = NULL; - return head->next; - } else { - g_return_val_if_fail(node->prev != NULL, NULL); - node->prev->next = node->next; - if (node->next != NULL) - node->next->prev = node->prev; - return head; - } -} - - -/******************************************************************************* - * States management - ******************************************************************************/ - -static void -purple_trie_states_cleanup(PurpleTriePrivate *priv) -{ - if (priv->root_state != NULL) { - purple_memory_pool_cleanup(priv->states_mempool); - priv->root_state = NULL; - } -} - -/* Allocates a state and binds it to the parent. */ -static PurpleTrieState * -purple_trie_state_new(PurpleTriePrivate *priv, PurpleTrieState *parent, - guchar character) -{ - PurpleTrieState *state; - - state = purple_memory_pool_alloc0(priv->states_mempool, - sizeof(PurpleTrieState), sizeof(gpointer)); - g_return_val_if_fail(state != NULL, NULL); - - if (parent == NULL) - return state; - - state->parent = parent; - if (parent->children == NULL) { - parent->children = purple_memory_pool_alloc0( - priv->states_mempool, - /* PurpleTrieState *children[G_MAXUCHAR + 1] */ - 256 * sizeof(gpointer), - sizeof(gpointer)); - } - - if (parent->children == NULL) { - purple_memory_pool_free(priv->states_mempool, state); - g_warn_if_reached(); - return NULL; - } - - parent->children[character] = state; - - return state; -} - -static gboolean -purple_trie_states_build(PurpleTriePrivate *priv) -{ - PurpleTrieState *root; - PurpleMemoryPool *reclist_mpool; - PurpleTrieRecordList *reclist, *it; - gulong cur_len; - - if (priv->root_state != NULL) - return TRUE; - - if (priv->records_total_size < PURPLE_TRIE_LARGE_THRESHOLD) { - purple_memory_pool_set_block_size(priv->states_mempool, - PURPLE_TRIE_STATES_SMALL_POOL_BLOCK_SIZE); - } else { - purple_memory_pool_set_block_size(priv->states_mempool, - PURPLE_TRIE_STATES_LARGE_POOL_BLOCK_SIZE); - } - - priv->root_state = root = purple_trie_state_new(priv, NULL, '\0'); - g_return_val_if_fail(root != NULL, FALSE); - g_assert(root->longest_suffix == NULL); - - /* reclist is a list of words not yet added to the trie. Shorter words - * are removed from the list, when they are fully added to the trie. */ - reclist_mpool = purple_memory_pool_new(); - reclist = purple_record_list_copy(reclist_mpool, priv->records); - - /* extra_data on every element of reclist will be a pointer to a trie - * node -- the prefix of the word with len of cur_len */ - for (it = reclist; it != NULL; it = it->next) { - it->extra_data = root; - } - - /* Iterate over indexes of words -- every loop iteration checks certain - * index of all remaining words. Loop finishes when there are no words - * longer than cur_len. */ - for (cur_len = 0; reclist != NULL; cur_len++) { - for (it = reclist; it; it = it->next) { - PurpleTrieRecord *rec = it->rec; - guchar character = rec->word[cur_len]; - PurpleTrieState *prefix = it->extra_data; - PurpleTrieState *lon_suf_parent; - - g_assert(character != '\0'); - - if (prefix->children && prefix->children[character]) { - /* Word's prefix is already in the trie, added - * by the other word. */ - prefix = prefix->children[character]; - } else { - /* We need to create a new branch of trie. */ - prefix = purple_trie_state_new(priv, prefix, - character); - if (!prefix) { - g_warn_if_reached(); - g_object_unref(reclist_mpool); - return FALSE; - } - } - it->extra_data = prefix; - /* prefix is now of length increased by one character. */ - - /* The whole word is now added to the trie. */ - if (rec->word[cur_len + 1] == '\0') { - if (prefix->found_word == NULL) - prefix->found_word = rec; - else { - purple_debug_warning("trie", "found " - "a collision of \"%s\" words", - rec->word); - } - - /* "it" is not modified here, so it->next is - * still valid */ - reclist = purple_record_list_remove(reclist, it); - } - - /* We need to fill the longest_suffix field -- a longest - * complete suffix of the prefix we created. We look for - * that suffix in any path starting in root and ending - * in the (cur_len - 1) level of trie. */ - if (prefix->longest_suffix != NULL) - continue; - lon_suf_parent = prefix->parent->longest_suffix; - while (lon_suf_parent) { - if (lon_suf_parent->children && - lon_suf_parent->children[character]) - { - prefix->longest_suffix = lon_suf_parent-> - children[character]; - break; - } - lon_suf_parent = lon_suf_parent->longest_suffix; - } - if (prefix->longest_suffix == NULL) - prefix->longest_suffix = root; - if (prefix->found_word == NULL) { - prefix->found_word = - prefix->longest_suffix->found_word; - } - } - } - - g_object_unref(reclist_mpool); - - return TRUE; -} - -/******************************************************************************* - * Searching - ******************************************************************************/ - -static void -purple_trie_advance(PurpleTrieMachine *m, const guchar character) -{ - /* change state after processing a character */ - while (TRUE) { - /* Perfect fit - next character is the same, as the child of the - * prefix we reached so far. */ - if (m->state->children && m->state->children[character]) { - m->state = m->state->children[character]; - break; - } - - /* We reached root, that's a pity. */ - if (m->state == m->root_state) - break; - - /* Let's try a bit shorter suffix. */ - m->state = m->state->longest_suffix; - } -} - -static gboolean -purple_trie_replace_do_replacement(PurpleTrieMachine *m, GString *out) -{ - gboolean was_replaced = FALSE; - gsize str_old_len; - - /* if we reached a "found" state, let's process it */ - if (!m->state->found_word) - return FALSE; - - /* let's get back to the beginning of the word */ - g_assert(out->len >= m->state->found_word->word_len - 1); - str_old_len = out->len; - out->len -= m->state->found_word->word_len - 1; - - was_replaced = m->replace_cb(out, m->state->found_word->word, - m->state->found_word->data, m->user_data); - - /* output was untouched, revert to the previous position */ - if (!was_replaced) - out->len = str_old_len; - - /* XXX */ - if (was_replaced || m->reset_on_match) - m->state = m->root_state; - - return was_replaced; -} - -static gboolean -purple_trie_find_do_discovery(PurpleTrieMachine *m) -{ - gboolean was_accepted; - - /* if we reached a "found" state, let's process it */ - if (!m->state->found_word) - return FALSE; - - if (m->find_cb) { - was_accepted = m->find_cb(m->state->found_word->word, - m->state->found_word->data, m->user_data); - } else { - was_accepted = TRUE; - } - - if (was_accepted && m->reset_on_match) - m->state = m->root_state; - - return was_accepted; -} - -gchar * -purple_trie_replace(PurpleTrie *trie, const gchar *src, - PurpleTrieReplaceCb replace_cb, gpointer user_data) -{ - PurpleTriePrivate *priv = NULL; - PurpleTrieMachine machine; - GString *out; - gsize i; - - if (src == NULL) - return NULL; - - g_return_val_if_fail(replace_cb != NULL, g_strdup(src)); - g_return_val_if_fail(PURPLE_IS_TRIE(trie), NULL); - - priv = purple_trie_get_instance_private(trie); - - purple_trie_states_build(priv); - - machine.state = priv->root_state; - machine.root_state = priv->root_state; - machine.reset_on_match = priv->reset_on_match; - machine.replace_cb = replace_cb; - machine.user_data = user_data; - - out = g_string_new(NULL); - i = 0; - while (src[i] != '\0') { - guchar character = src[i++]; - gboolean was_replaced; - - purple_trie_advance(&machine, character); - was_replaced = purple_trie_replace_do_replacement(&machine, out); - - /* We skipped a character without finding any records, - * let's just copy it to the output. */ - if (!was_replaced) - g_string_append_c(out, character); - } - - return g_string_free(out, FALSE); -} - -gchar * -purple_trie_multi_replace(const GSList *tries, const gchar *src, - PurpleTrieReplaceCb replace_cb, gpointer user_data) -{ - guint tries_count, m_idx; - PurpleTrieMachine *machines; - GString *out; - gsize i; - - if (src == NULL) - return NULL; - - g_return_val_if_fail(replace_cb != NULL, g_strdup(src)); - - tries_count = g_slist_length((GSList*)tries); - if (tries_count == 0) - return g_strdup(src); - - /* Initialize all machines. */ - machines = g_new(PurpleTrieMachine, tries_count); - for (i = 0; i < tries_count; i++, tries = tries->next) { - PurpleTrie *trie = tries->data; - PurpleTriePrivate *priv = NULL; - - if (!PURPLE_TRIE(trie)) { - g_warn_if_reached(); - g_free(machines); - return NULL; - } - - priv = purple_trie_get_instance_private(trie); - - purple_trie_states_build(priv); - - machines[i].state = priv->root_state; - machines[i].root_state = priv->root_state; - machines[i].reset_on_match = priv->reset_on_match; - machines[i].replace_cb = replace_cb; - machines[i].user_data = user_data; - } - - out = g_string_new(NULL); - i = 0; - while (src[i] != '\0') { - guchar character = src[i++]; - gboolean was_replaced = FALSE; - - /* Advance every machine and possibly perform a replacement. */ - for (m_idx = 0; m_idx < tries_count; m_idx++) { - purple_trie_advance(&machines[m_idx], character); - if (was_replaced) - continue; - was_replaced = purple_trie_replace_do_replacement( - &machines[m_idx], out); - } - - /* We skipped a character without finding any records, - * let's just copy it to the output. */ - if (!was_replaced) - g_string_append_c(out, character); - - /* If we replaced a word, reset _all_ machines */ - if (was_replaced) { - for (m_idx = 0; m_idx < tries_count; m_idx++) { - machines[m_idx].state = - machines[m_idx].root_state; - } - } - } - - g_free(machines); - return g_string_free(out, FALSE); -} - -gulong -purple_trie_find(PurpleTrie *trie, const gchar *src, - PurpleTrieFindCb find_cb, gpointer user_data) -{ - PurpleTriePrivate *priv = NULL; - PurpleTrieMachine machine; - gulong found_count = 0; - gsize i; - - if (src == NULL) - return 0; - - g_return_val_if_fail(PURPLE_IS_TRIE(trie), 0); - - priv = purple_trie_get_instance_private(trie); - - purple_trie_states_build(priv); - - machine.state = priv->root_state; - machine.root_state = priv->root_state; - machine.reset_on_match = priv->reset_on_match; - machine.find_cb = find_cb; - machine.user_data = user_data; - - i = 0; - while (src[i] != '\0') { - guchar character = src[i++]; - gboolean was_found; - - purple_trie_advance(&machine, character); - - was_found = purple_trie_find_do_discovery(&machine); - - if (was_found) - found_count++; - } - - return found_count; -} - -gulong -purple_trie_multi_find(const GSList *tries, const gchar *src, - PurpleTrieFindCb find_cb, gpointer user_data) -{ - guint tries_count, m_idx; - PurpleTrieMachine *machines; - gulong found_count = 0; - gsize i; - - if (src == NULL) - return 0; - - tries_count = g_slist_length((GSList*)tries); - if (tries_count == 0) - return 0; - - /* Initialize all machines. */ - machines = g_new(PurpleTrieMachine, tries_count); - for (i = 0; i < tries_count; i++, tries = tries->next) { - PurpleTrie *trie = tries->data; - PurpleTriePrivate *priv = NULL; - - if (!PURPLE_IS_TRIE(trie)) { - g_warn_if_reached(); - g_free(machines); - return 0; - } - - priv = purple_trie_get_instance_private(trie); - - purple_trie_states_build(priv); - - machines[i].state = priv->root_state; - machines[i].root_state = priv->root_state; - machines[i].reset_on_match = priv->reset_on_match; - machines[i].find_cb = find_cb; - machines[i].user_data = user_data; - } - - i = 0; - while (src[i] != '\0') { - guchar character = src[i++]; - gboolean was_found = FALSE; - - /* Advance every machine and possibly perform a replacement. */ - for (m_idx = 0; m_idx < tries_count; m_idx++) { - purple_trie_advance(&machines[m_idx], character); - if (was_found) - continue; - was_found = - purple_trie_find_do_discovery(&machines[m_idx]); - if (was_found) - found_count++; - } - - /* If we replaced a word, reset _all_ machines */ - if (was_found) { - for (m_idx = 0; m_idx < tries_count; m_idx++) { - if (!machines[m_idx].reset_on_match) - continue; - machines[m_idx].state = - machines[m_idx].root_state; - } - } - } - - g_free(machines); - return found_count; -} - - -/******************************************************************************* - * Records - ******************************************************************************/ - -gboolean -purple_trie_add(PurpleTrie *trie, const gchar *word, gpointer data) -{ - PurpleTriePrivate *priv = NULL; - PurpleTrieRecord *rec; - - g_return_val_if_fail(PURPLE_IS_TRIE(trie), FALSE); - g_return_val_if_fail(word != NULL, FALSE); - g_return_val_if_fail(word[0] != '\0', FALSE); - - priv = purple_trie_get_instance_private(trie); - - if (g_hash_table_lookup(priv->records_map, word) != NULL) { - purple_debug_warning("trie", "record exists: %s", word); - return FALSE; - } - - /* Every change in a trie invalidates longest_suffix map. - * These prefixes could be updated instead of cleaning the whole graph. - */ - purple_trie_states_cleanup(priv); - - rec = purple_memory_pool_alloc(priv->records_obj_mempool, - sizeof(PurpleTrieRecord), sizeof(gpointer)); - rec->word = purple_memory_pool_strdup(priv->records_str_mempool, word); - rec->word_len = strlen(word); - g_assert(rec->word_len > 0); - rec->data = data; - - priv->records_total_size += rec->word_len; - priv->records = purple_record_list_prepend(priv->records_obj_mempool, - priv->records, rec); - g_hash_table_insert(priv->records_map, rec->word, priv->records); - - return TRUE; -} - -void -purple_trie_remove(PurpleTrie *trie, const gchar *word) -{ - PurpleTriePrivate *priv = NULL; - PurpleTrieRecordList *it; - - g_return_if_fail(PURPLE_IS_TRIE(trie)); - g_return_if_fail(word != NULL); - g_return_if_fail(word[0] != '\0'); - - priv = purple_trie_get_instance_private(trie); - - it = g_hash_table_lookup(priv->records_map, word); - if (it == NULL) - return; - - /* see purple_trie_add */ - purple_trie_states_cleanup(priv); - - priv->records_total_size -= it->rec->word_len; - priv->records = purple_record_list_remove(priv->records, it); - g_hash_table_remove(priv->records_map, it->rec->word); - - purple_memory_pool_free(priv->records_str_mempool, it->rec->word); - purple_memory_pool_free(priv->records_obj_mempool, it->rec); - purple_memory_pool_free(priv->records_obj_mempool, it); -} - -guint -purple_trie_get_size(PurpleTrie *trie) -{ - PurpleTriePrivate *priv = NULL; - - g_return_val_if_fail(PURPLE_IS_TRIE(trie), 0); - - priv = purple_trie_get_instance_private(trie); - return g_hash_table_size(priv->records_map); -} - - -/******************************************************************************* - * API implementation - ******************************************************************************/ - -gboolean -purple_trie_get_reset_on_match(PurpleTrie *trie) -{ - PurpleTriePrivate *priv = NULL; - - g_return_val_if_fail(PURPLE_IS_TRIE(trie), FALSE); - - priv = purple_trie_get_instance_private(trie); - return priv->reset_on_match; -} - -void -purple_trie_set_reset_on_match(PurpleTrie *trie, gboolean reset) -{ - PurpleTriePrivate *priv = NULL; - - g_return_if_fail(PURPLE_IS_TRIE(trie)); - - priv = purple_trie_get_instance_private(trie); - priv->reset_on_match = reset; - g_object_notify_by_pspec(G_OBJECT(trie), properties[PROP_RESET_ON_MATCH]); -} - -/******************************************************************************* - * Object stuff - ******************************************************************************/ - -PurpleTrie * -purple_trie_new(void) -{ - return g_object_new(PURPLE_TYPE_TRIE, NULL); -} - -static void -purple_trie_init(PurpleTrie *trie) -{ - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie); - - priv->records_obj_mempool = purple_memory_pool_new(); - priv->records_str_mempool = purple_memory_pool_new(); - priv->states_mempool = purple_memory_pool_new(); - purple_memory_pool_set_block_size(priv->states_mempool, - PURPLE_TRIE_STATES_SMALL_POOL_BLOCK_SIZE); - - priv->records_map = g_hash_table_new(g_str_hash, g_str_equal); -} - -static void -purple_trie_finalize(GObject *obj) -{ - PurpleTriePrivate *priv = - purple_trie_get_instance_private(PURPLE_TRIE(obj)); - - g_hash_table_destroy(priv->records_map); - g_object_unref(priv->records_obj_mempool); - g_object_unref(priv->records_str_mempool); - g_object_unref(priv->states_mempool); - - G_OBJECT_CLASS(purple_trie_parent_class)->finalize(obj); -} - -static void -purple_trie_get_property(GObject *obj, guint param_id, GValue *value, - GParamSpec *pspec) -{ - PurpleTrie *trie = PURPLE_TRIE(obj); - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie); - - switch (param_id) { - case PROP_RESET_ON_MATCH: - g_value_set_boolean(value, priv->reset_on_match); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); - } -} - -static void -purple_trie_set_property(GObject *obj, guint param_id, - const GValue *value, GParamSpec *pspec) -{ - PurpleTrie *trie = PURPLE_TRIE(obj); - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie); - - switch (param_id) { - case PROP_RESET_ON_MATCH: - priv->reset_on_match = g_value_get_boolean(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); - } -} - -static void -purple_trie_class_init(PurpleTrieClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - - obj_class->finalize = purple_trie_finalize; - obj_class->get_property = purple_trie_get_property; - obj_class->set_property = purple_trie_set_property; - - properties[PROP_RESET_ON_MATCH] = g_param_spec_boolean("reset-on-match", - "Reset on match", "Determines, if the search state machine " - "should be reset to the initial state on every match. This " - "ensures, that every match is distinct from each other. " - "Please note, that it's not well-defined for a replace " - "operation, so it's better to leave this value default, unless " - "you perform only find operations.", TRUE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(obj_class, PROP_LAST, properties); -}
--- a/libpurple/trie.h Mon Sep 12 22:24:33 2022 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,271 +0,0 @@ -/* - * Purple - * - * Purple is the legal property of its developers, whose names are too - * numerous to list here. Please refer to the COPYRIGHT file distributed - * with this source distribution - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION) -# error "only <purple.h> may be included directly" -#endif - -#ifndef PURPLE_TRIE_H -#define PURPLE_TRIE_H - -#include <glib-object.h> - -/** - * PURPLE_TYPE_TRIE: - * - * The standard _get_type macro for #PurpleTrie. - */ -#define PURPLE_TYPE_TRIE purple_trie_get_type() - -/** - * PurpleTrieReplaceCb: - * @out: currently built output string, append replacement to it. - * @word: found word. - * @word_data: the user data bound with this word, when added with - * #purple_trie_add. - * @user_data: the user supplied data passed when calling #purple_trie_replace. - * - * A function called on every matching substring to be replaced. - * - * If you decide to replace the word, append your text to @out and return %TRUE. - * Otherwise, you must not touch @out. In both cases, you must not do any - * operations on @out other than appending text to it. - * - * Returns: %TRUE if the word was replaced, %FALSE otherwise. - */ -typedef gboolean (*PurpleTrieReplaceCb)(GString *out, const gchar *word, - gpointer word_data, gpointer user_data); - -/** - * PurpleTrieFindCb: - * @word: found word. - * @word_data: the user data bound with this word, when added with - * #purple_trie_add. - * @user_data: the user data passed when calling #purple_trie_find. - * - * A function called on every matching substring. - * - * You can decide to count the match or not (for the total number of found - * words, that is returned by #purple_trie_find). In both cases you can - * obviously do some processing outside the #PurpleTrie object. - * - * If you decide to count the word and #PurpleTrie:reset-on-match property - * is set, no overlapping words will be found - the processing will skip after - * the end of this word. - * - * Returns: %TRUE if the word should be counter, %FALSE otherwise. - */ -typedef gboolean (*PurpleTrieFindCb)(const gchar *word, gpointer word_data, - gpointer user_data); - -G_BEGIN_DECLS - -/** - * PurpleTrie: - * - * A #PurpleTrie is a structure for quick searching of multiple phrases within - * a text. It's intended for repeated searches of the same set of patterns - * within multiple source texts (or a single, big one). - * - * It's preparation time is <literal>O(p)</literal>, where <literal>p</literal> - * is the total length of searched phrases. In current implementation, the - * internal structure is invalidated after every modification of the - * #PurpleTrie's contents, so it's not efficient to do alternating modifications - * and searches. Search time does not depend on patterns being stored within - * a trie and is always <literal>O(n)</literal>, where <literal>n</literal> is - * the size of a text. - * - * Its main drawback is a significant memory usage - every internal trie node - * needs about 1kB of memory on 32-bit machine and 2kB on 64-bit. Fortunately, - * the trie grows slower when more words (with common prefixes) are added. - * We could avoid invalidating the whole tree when altering it, but it would - * require figuring out, how to update <literal>longest_suffix</literal> fields - * in satisfying time. - */ - -/** - * purple_trie_get_type: - * - * Returns: the #GType for a #PurpleTrie. - */ -G_DECLARE_FINAL_TYPE(PurpleTrie, purple_trie, PURPLE, TRIE, GObject) - -/** - * purple_trie_new: - * - * Creates a new trie. - * - * Returns: the new #PurpleTrie. - */ -PurpleTrie * -purple_trie_new(void); - -/** - * purple_trie_get_reset_on_match: - * @trie: the trie. - * - * Checks, if the trie will reset its internal state after every match. - * - * Returns: %TRUE, if trie will reset, %FALSE otherwise. - */ -gboolean -purple_trie_get_reset_on_match(PurpleTrie *trie); - -/** - * purple_trie_set_reset_on_match: - * @trie: the trie. - * @reset: %TRUE, if trie should reset, %FALSE otherwise. - * - * Enables or disables a feature of resetting trie's state after every match. - * When enabled, it will not search for overlapping matches. - * - * It's well defined for #purple_trie_find, but not for replace operations. - * Thus, for the latter, it's better to stay with this option enabled, because - * its behavior may be changed in future. - */ -void -purple_trie_set_reset_on_match(PurpleTrie *trie, gboolean reset); - -/** - * purple_trie_add: - * @trie: the trie. - * @word: the word. - * @data: the word-related data (may be %NULL). - * - * Adds a word to the trie. Current implementation doesn't allow for duplicates, - * so please avoid adding those. - * - * Please note, that altering a trie invalidates its internal structure, so by - * the occasion of next search, it will be rebuilt. It's done in - * <literal>O(n)</literal>, where n is the total length of strings - * in #PurpleTrie. - * - * Returns: %TRUE if succeeded, %FALSE otherwise. - */ -gboolean -purple_trie_add(PurpleTrie *trie, const gchar *word, gpointer data); - -/** - * purple_trie_remove: - * @trie: the trie. - * @word: the word. - * - * Removes a word from the trie. Depending on used memory pool, this may not - * free allocated memory (that will be freed when destroying the whole - * collection), so use it wisely. See #purple_memory_pool_free. - * - * Please note, that altering a trie invalidates its internal structure. - * See #purple_trie_add. - */ -void -purple_trie_remove(PurpleTrie *trie, const gchar *word); - -/** - * purple_trie_get_size: - * @trie: the trie. - * - * Returns the number of elements contained in the #PurpleTrie. - * - * Returns: the number of stored words in @trie. - */ -guint -purple_trie_get_size(PurpleTrie *trie); - -/** - * purple_trie_replace: - * @trie: the trie. - * @src: the source string. - * @replace_cb: (scope call): the replacement function. - * @user_data: custom data to be passed to @replace_cb. - * - * Processes @src string and replaces all occuriences of words added to @trie. - * It's <literal>O(strlen(src))</literal>, if @replace_cb runs in - * <literal>O(strlen(word))</literal> and #PurpleTrie:reset-on-match is set. - * - * Returns: resulting string. Must be #g_free'd when you are done using it. - */ -gchar * -purple_trie_replace(PurpleTrie *trie, const gchar *src, - PurpleTrieReplaceCb replace_cb, gpointer user_data); - -/** - * purple_trie_multi_replace: - * @tries: (element-type PurpleTrie): the list of tries. - * @src: the source string. - * @replace_cb: (scope call): the replacement function. - * @user_data: custom data to be passed to @replace_cb. - * - * Processes @src and replaces all occuriences of words added to tries in list - * @tries. Entries added to tries on the beginning of the list have higher - * priority, than ones added further. - * - * Different #GSList's can be combined to possess common parts, so you can create - * a "tree of tries". - * - * Returns: resulting string. Must be #g_free'd when you are done using it. - */ -gchar * -purple_trie_multi_replace(const GSList *tries, const gchar *src, - PurpleTrieReplaceCb replace_cb, gpointer user_data); - -/** - * purple_trie_find: - * @trie: the trie. - * @src: the source string. - * @find_cb: (nullable) (scope call): the callback for the found entries (may be %NULL). - * @user_data: custom data to be passed to @find_cb. - * - * Processes @src string and finds all occuriences of words added to @trie. - * It's <literal>O(strlen(src))</literal>, if find_cb runs - * in <literal>O(1)</literal>. - * - * The word is counted as found if it's found and the callback returns %TRUE. - * - * Returns: the number of found words. - */ -gulong -purple_trie_find(PurpleTrie *trie, const gchar *src, - PurpleTrieFindCb find_cb, gpointer user_data); - -/** - * purple_trie_multi_find: - * @tries: (element-type PurpleTrie): the list of tries. - * @src: the source string. - * @find_cb: (nullable) (scope call): the callback for the found entries (may be %NULL). - * @user_data: custom data to be passed to @find_cb. - * - * Processes @src and replaces all occuriences of words added to tries in - * list @tries. Entries added to tries on the beginning of the list have higher - * priority, than ones added further. - * - * Different #GSList's can be combined to possess common parts, so you can create - * a "tree of tries". - * - * Returns: the number of found words. - */ -gulong -purple_trie_multi_find(const GSList *tries, const gchar *src, - PurpleTrieFindCb find_cb, gpointer user_data); - -G_END_DECLS - -#endif /* PURPLE_MEMORY_POOL_H */
--- a/po/POTFILES.in Mon Sep 12 22:24:33 2022 -0500 +++ b/po/POTFILES.in Mon Sep 12 22:40:17 2022 -0500 @@ -301,12 +301,10 @@ libpurple/tests/test_protocol_xfer.c libpurple/tests/test_purplepath.c libpurple/tests/test_queued_output_stream.c -libpurple/tests/test_trie.c libpurple/tests/test_ui.c libpurple/tests/test_util.c libpurple/tests/test_whiteboard_manager.c libpurple/tests/test_xmlnode.c -libpurple/trie.c libpurple/upnp.c libpurple/util.c libpurple/version.c