High: trie: allow modifying the trie map during the notify callback

This commit is contained in:
David Vossel 2014-07-28 14:13:59 -05:00
parent 073f927801
commit 5b103cd6e4

View File

@ -43,7 +43,7 @@ struct trie_node {
uint32_t num_children;
uint32_t refcount;
struct trie_node *parent;
struct qb_list_head notifier_head;
struct qb_list_head *notifier_head;
};
struct trie {
@ -58,6 +58,7 @@ struct trie {
static void trie_notify(struct trie_node *n, uint32_t event, const char *key,
void *old_value, void *value);
static struct trie_node *trie_new_node(struct trie *t, struct trie_node *parent);
static void trie_destroy_node(struct trie_node *node);
/*
* characters are stored in reverse to make accessing the
@ -173,6 +174,7 @@ trie_node_split(struct trie *t, struct trie_node *cur_node, int seg_cnt)
struct trie_node *split_node;
struct trie_node ** children = cur_node->children;
uint32_t num_children = cur_node->num_children;
struct qb_list_head *tmp;
int i;
int s;
@ -196,14 +198,16 @@ trie_node_split(struct trie *t, struct trie_node *cur_node, int seg_cnt)
cur_node->key = NULL;
cur_node->refcount = 0;
/* move notifier list to split */
qb_list_replace(&cur_node->notifier_head, &split_node->notifier_head);
qb_list_init(&cur_node->notifier_head);
tmp = split_node->notifier_head;
split_node->notifier_head = cur_node->notifier_head;
cur_node->notifier_head = tmp;
qb_list_init(cur_node->notifier_head);
if (seg_cnt < cur_node->num_segments) {
split_node->num_segments = cur_node->num_segments - seg_cnt - 1;
split_node->segment = malloc(split_node->num_segments * sizeof(char));
if (split_node->segment == NULL) {
free(split_node);
trie_destroy_node(split_node);
return NULL;
}
for (i = (seg_cnt + 1); i < cur_node->num_segments; i++) {
@ -253,7 +257,7 @@ trie_insert(struct trie *t, const char *key)
return NULL;
}
} else if (cur_node->value == NULL &&
qb_list_empty(&cur_node->notifier_head) &&
qb_list_empty(cur_node->notifier_head) &&
cur_node->num_children == 0 &&
seg_cnt == cur_node->num_segments) {
/* we are on a leaf (with no value) so just add it as a segment */
@ -348,7 +352,7 @@ trie_node_release(struct trie *t, struct trie_node *node)
if (node->key == NULL &&
node->parent != NULL &&
qb_list_empty(&node->notifier_head)) {
qb_list_empty(node->notifier_head)) {
struct trie_node *p = node->parent;
if (node->num_children == 0) {
@ -370,9 +374,7 @@ trie_node_release(struct trie *t, struct trie_node *node)
* unlink the node from the parent
*/
p->children[node->idx] = NULL;
free(node->segment);
free(node->children);
free(node);
trie_destroy_node(node);
t->num_nodes--;
t->mem_used -= sizeof(struct trie_node);
@ -455,6 +457,15 @@ trie_destroy(struct qb_map *map)
free(t);
}
static void
trie_destroy_node(struct trie_node *node)
{
free(node->segment);
free(node->children);
free(node->notifier_head);
free(node);
}
static struct trie_node *
trie_new_node(struct trie *t, struct trie_node *parent)
{
@ -464,6 +475,12 @@ trie_new_node(struct trie *t, struct trie_node *parent)
return NULL;
}
new_node->notifier_head = calloc(1, sizeof(struct qb_list_head));
if (new_node->notifier_head == NULL) {
free(new_node);
return NULL;
}
new_node->parent = parent;
new_node->num_children = 0;
new_node->children = NULL;
@ -471,7 +488,7 @@ trie_new_node(struct trie *t, struct trie_node *parent)
new_node->segment = NULL;
t->num_nodes++;
t->mem_used += sizeof(struct trie_node);
qb_list_init(&new_node->notifier_head);
qb_list_init(new_node->notifier_head);
return new_node;
}
@ -570,10 +587,12 @@ trie_notify(struct trie_node *n,
struct trie_node *c = n;
struct qb_list_head *list;
struct qb_list_head *next;
struct qb_list_head *head;
struct qb_map_notifier *tn;
do {
qb_list_for_each_safe(list, next, &c->notifier_head) {
head = c->notifier_head;
qb_list_for_each_safe(list, next, head) {
tn = qb_list_entry(list, struct qb_map_notifier, list);
trie_notify_ref(tn);
@ -589,6 +608,7 @@ trie_notify(struct trie_node *n,
tn->callback(QB_MAP_NOTIFY_FREE, (char *)key,
old_value, value, tn->user_data);
}
trie_notify_deref(tn);
}
c = c->parent;
@ -614,7 +634,7 @@ trie_notify_add(qb_map_t * m, const char *key,
n = t->header;
}
if (n) {
qb_list_for_each(list, &n->notifier_head) {
qb_list_for_each(list, n->notifier_head) {
f = qb_list_entry(list, struct qb_map_notifier, list);
if (events & QB_MAP_NOTIFY_FREE &&
@ -648,9 +668,9 @@ trie_notify_add(qb_map_t * m, const char *key,
}
}
if (add_to_tail) {
qb_list_add_tail(&f->list, &n->notifier_head);
qb_list_add_tail(&f->list, n->notifier_head);
} else {
qb_list_add(&f->list, &n->notifier_head);
qb_list_add(&f->list, n->notifier_head);
}
return 0;
}
@ -676,7 +696,7 @@ trie_notify_del(qb_map_t * m, const char *key,
if (n == NULL) {
return -ENOENT;
}
qb_list_for_each_safe(list, next, &n->notifier_head) {
qb_list_for_each_safe(list, next, n->notifier_head) {
struct qb_map_notifier *f = qb_list_entry(list, struct qb_map_notifier, list);
if (f->events == events && f->callback == fn) {