pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Remove stock from dynamic menus in gtkblist
2021-08-12, Gary Kramlich
e42ad74de8ac
Remove stock from dynamic menus in gtkblist
Testing Done:
Ran and verified the menus.
Reviewed at https://reviews.imfreedom.org/r/894/
/*
* 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
);
}