pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Make user interfaces setup the default history adapter.
2021-10-18, Gary Kramlich
755e1554051c
Make user interfaces setup the default history adapter.
This helps avoid some issues with the unit tests as well as gives us more
flexibility in the future.
Testing Done:
Ran the unit tests without issue. Ran Pidgin 3 with no existing config directory and verified that `history.db` was created properly.
Reviewed at https://reviews.imfreedom.org/r/1033/
/*
* 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
;
/**
* PurpleTrie:
*
* The trie object instance.
*/
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
);
}