summaryrefslogtreecommitdiff
path: root/tables/apr_skiplist.c
diff options
context:
space:
mode:
Diffstat (limited to 'tables/apr_skiplist.c')
-rw-r--r--tables/apr_skiplist.c211
1 files changed, 171 insertions, 40 deletions
diff --git a/tables/apr_skiplist.c b/tables/apr_skiplist.c
index b4696bd41a5c0..5ea8643dbf2fc 100644
--- a/tables/apr_skiplist.c
+++ b/tables/apr_skiplist.c
@@ -200,7 +200,7 @@ static apr_skiplistnode *skiplist_new_node(apr_skiplist *sl)
return m;
}
-static apr_status_t skiplist_free_node(apr_skiplist *sl, apr_skiplistnode *m)
+static apr_status_t skiplist_put_node(apr_skiplist *sl, apr_skiplistnode *m)
{
return skiplist_qpush(&sl->nodes_q, m);
}
@@ -298,39 +298,45 @@ APR_DECLARE(void) apr_skiplist_add_index(apr_skiplist *sl,
}
static int skiplisti_find_compare(apr_skiplist *sl, void *data,
- apr_skiplistnode **ret,
- apr_skiplist_compare comp)
+ apr_skiplistnode **ret,
+ apr_skiplist_compare comp,
+ int last)
{
int count = 0;
- apr_skiplistnode *m;
- m = sl->top;
- while (m) {
+ apr_skiplistnode *m, *found = NULL;
+ for (m = sl->top; m; count++) {
if (m->next) {
int compared = comp(data, m->next->data);
if (compared == 0) {
- m = m->next;
- while (m->down) {
- m = m->down;
+ found = m = m->next;
+ if (!last) {
+ break;
}
- *ret = m;
- return count;
+ continue;
}
if (compared > 0) {
m = m->next;
- count++;
continue;
}
}
m = m->down;
- count++;
}
- *ret = NULL;
+ if (found) {
+ while (found->down) {
+ found = found->down;
+ }
+ *ret = found;
+ }
+ else {
+ *ret = NULL;
+ }
return count;
}
-APR_DECLARE(void *) apr_skiplist_find_compare(apr_skiplist *sli, void *data,
- apr_skiplistnode **iter,
- apr_skiplist_compare comp)
+static void *find_compare(apr_skiplist *sli, void *data,
+ apr_skiplistnode **iter,
+ apr_skiplist_compare comp,
+ int last)
{
apr_skiplistnode *m;
apr_skiplist *sl;
@@ -353,16 +359,36 @@ APR_DECLARE(void *) apr_skiplist_find_compare(apr_skiplist *sli, void *data,
}
sl = (apr_skiplist *) m->data;
}
- skiplisti_find_compare(sl, data, &m, sl->comparek);
+ skiplisti_find_compare(sl, data, &m, sl->comparek, last);
if (iter) {
*iter = m;
}
return (m) ? m->data : NULL;
}
+APR_DECLARE(void *) apr_skiplist_find_compare(apr_skiplist *sl, void *data,
+ apr_skiplistnode **iter,
+ apr_skiplist_compare comp)
+{
+ return find_compare(sl, data, iter, comp, 0);
+}
+
APR_DECLARE(void *) apr_skiplist_find(apr_skiplist *sl, void *data, apr_skiplistnode **iter)
{
- return apr_skiplist_find_compare(sl, data, iter, sl->compare);
+ return find_compare(sl, data, iter, sl->compare, 0);
+}
+
+APR_DECLARE(void *) apr_skiplist_last_compare(apr_skiplist *sl, void *data,
+ apr_skiplistnode **iter,
+ apr_skiplist_compare comp)
+{
+ return find_compare(sl, data, iter, comp, 1);
+}
+
+APR_DECLARE(void *) apr_skiplist_last(apr_skiplist *sl, void *data,
+ apr_skiplistnode **iter)
+{
+ return find_compare(sl, data, iter, sl->compare, 1);
}
@@ -392,6 +418,15 @@ APR_DECLARE(void *) apr_skiplist_previous(apr_skiplist *sl, apr_skiplistnode **i
return (*iter) ? ((*iter)->data) : NULL;
}
+APR_DECLARE(void *) apr_skiplist_element(apr_skiplistnode *iter)
+{
+ return (iter) ? iter->data : NULL;
+}
+
+/* forward declared */
+static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m,
+ apr_skiplist_freefunc myfree);
+
static APR_INLINE int skiplist_height(const apr_skiplist *sl)
{
/* Skiplists (even empty) always have a top node, although this
@@ -401,15 +436,12 @@ static APR_INLINE int skiplist_height(const apr_skiplist *sl)
return sl->height ? sl->height : 1;
}
-APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, void *data,
- apr_skiplist_compare comp)
+static apr_skiplistnode *insert_compare(apr_skiplist *sl, void *data,
+ apr_skiplist_compare comp, int add,
+ apr_skiplist_freefunc myfree)
{
apr_skiplistnode *m, *p, *tmp, *ret = NULL;
- int ch, nh = 1;
-
- if (!comp) {
- return NULL;
- }
+ int ch, top_nh, nh = 1;
ch = skiplist_height(sl);
if (sl->preheight) {
@@ -422,6 +454,7 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, vo
nh++;
}
}
+ top_nh = nh;
/* Now we have in nh the height at which we wish to insert our new node,
* and in ch the current height: don't create skip paths to the inserted
@@ -432,14 +465,34 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, vo
*/
m = sl->top;
while (m) {
+ /*
+ * To maintain stability, dups (compared == 0) must be added
+ * AFTER each other.
+ */
if (m->next) {
int compared = comp(data, m->next->data);
if (compared == 0) {
- /* Keep the existing element(s) */
- skiplist_qclear(&sl->stack_q);
- return NULL;
+ if (!add) {
+ /* Keep the existing element(s) */
+ skiplist_qclear(&sl->stack_q);
+ return NULL;
+ }
+ if (add < 0) {
+ /* Remove this element and continue with the next node
+ * or the new top if the current one is also removed.
+ */
+ apr_skiplistnode *top = sl->top;
+ skiplisti_remove(sl, m->next, myfree);
+ if (top != sl->top) {
+ m = sl->top;
+ skiplist_qclear(&sl->stack_q);
+ ch = skiplist_height(sl);
+ nh = top_nh;
+ }
+ continue;
+ }
}
- if (compared > 0) {
+ if (compared >= 0) {
m = m->next;
continue;
}
@@ -514,7 +567,7 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, vo
li = ret;
for (p = apr_skiplist_getlist(sl->index); p; apr_skiplist_next(sl->index, &p)) {
apr_skiplist *sli = (apr_skiplist *)p->data;
- ni = apr_skiplist_insert_compare(sli, ret->data, sli->compare);
+ ni = insert_compare(sli, ret->data, sli->compare, 1, NULL);
li->nextindex = ni;
ni->previndex = li;
li = ni;
@@ -524,11 +577,50 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, vo
return ret;
}
+APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, void *data,
+ apr_skiplist_compare comp)
+{
+ if (!comp) {
+ return NULL;
+ }
+ return insert_compare(sl, data, comp, 0, NULL);
+}
+
APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert(apr_skiplist *sl, void *data)
{
return apr_skiplist_insert_compare(sl, data, sl->compare);
}
+APR_DECLARE(apr_skiplistnode *) apr_skiplist_add_compare(apr_skiplist *sl, void *data,
+ apr_skiplist_compare comp)
+{
+ if (!comp) {
+ return NULL;
+ }
+ return insert_compare(sl, data, comp, 1, NULL);
+}
+
+APR_DECLARE(apr_skiplistnode *) apr_skiplist_add(apr_skiplist *sl, void *data)
+{
+ return apr_skiplist_add_compare(sl, data, sl->compare);
+}
+
+APR_DECLARE(apr_skiplistnode *) apr_skiplist_replace_compare(apr_skiplist *sl,
+ void *data, apr_skiplist_freefunc myfree,
+ apr_skiplist_compare comp)
+{
+ if (!comp) {
+ return NULL;
+ }
+ return insert_compare(sl, data, comp, -1, myfree);
+}
+
+APR_DECLARE(apr_skiplistnode *) apr_skiplist_replace(apr_skiplist *sl,
+ void *data, apr_skiplist_freefunc myfree)
+{
+ return apr_skiplist_replace_compare(sl, data, myfree, sl->compare);
+}
+
#if 0
void skiplist_print_struct(apr_skiplist * sl, char *prefix)
{
@@ -548,7 +640,8 @@ void skiplist_print_struct(apr_skiplist * sl, char *prefix)
}
#endif
-static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_freefunc myfree)
+static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m,
+ apr_skiplist_freefunc myfree)
{
apr_skiplistnode *p;
if (!m) {
@@ -560,19 +653,20 @@ static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_
while (m->up) {
m = m->up;
}
- while (m) {
+ do {
p = m;
- p->prev->next = p->next;/* take me out of the list */
+ /* take me out of the list */
+ p->prev->next = p->next;
if (p->next) {
- p->next->prev = p->prev; /* take me out of the list */
+ p->next->prev = p->prev;
}
m = m->down;
/* This only frees the actual data in the bottom one */
if (!m && myfree && p->data) {
myfree(p->data);
}
- skiplist_free_node(sl, p);
- }
+ skiplist_put_node(sl, p);
+ } while (m);
sl->size--;
while (sl->top && sl->top->next == NULL) {
/* While the row is empty and we are not on the bottom row */
@@ -581,7 +675,7 @@ static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_
if (sl->top) {
sl->top->up = NULL; /* Make it think its the top */
}
- skiplist_free_node(sl, p);
+ skiplist_put_node(sl, p);
sl->height--;
}
if (!sl->top) {
@@ -591,6 +685,23 @@ static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_
return skiplist_height(sl);
}
+APR_DECLARE(int) apr_skiplist_remove_node(apr_skiplist *sl,
+ apr_skiplistnode *iter,
+ apr_skiplist_freefunc myfree)
+{
+ apr_skiplistnode *m = iter;
+ if (!m) {
+ return 0;
+ }
+ while (m->down) {
+ m = m->down;
+ }
+ while (m->previndex) {
+ m = m->previndex;
+ }
+ return skiplisti_remove(sl, m, myfree);
+}
+
APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sli,
void *data,
apr_skiplist_freefunc myfree, apr_skiplist_compare comp)
@@ -610,7 +721,7 @@ APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sli,
}
sl = (apr_skiplist *) m->data;
}
- skiplisti_find_compare(sl, data, &m, comp);
+ skiplisti_find_compare(sl, data, &m, comp, 0);
if (!m) {
return 0;
}
@@ -641,7 +752,7 @@ APR_DECLARE(void) apr_skiplist_remove_all(apr_skiplist *sl, apr_skiplist_freefun
}
do {
u = m->up;
- skiplist_free_node(sl, m);
+ skiplist_put_node(sl, m);
m = u;
} while (m);
m = p;
@@ -674,6 +785,26 @@ APR_DECLARE(void *) apr_skiplist_peek(apr_skiplist *a)
return NULL;
}
+APR_DECLARE(size_t) apr_skiplist_size(const apr_skiplist *sl)
+{
+ return sl->size;
+}
+
+APR_DECLARE(int) apr_skiplist_height(const apr_skiplist *sl)
+{
+ return skiplist_height(sl);
+}
+
+APR_DECLARE(int) apr_skiplist_preheight(const apr_skiplist *sl)
+{
+ return sl->preheight;
+}
+
+APR_DECLARE(void) apr_skiplist_set_preheight(apr_skiplist *sl, int to)
+{
+ sl->preheight = (to > 0) ? to : 0;
+}
+
static void skiplisti_destroy(void *vsl)
{
apr_skiplist_destroy(vsl, NULL);