Apache HTTPD
apr_skiplist.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Modified to use APR and APR pools.
19 * TODO: Is malloc() better? Will long running skiplists grow too much?
20 * Keep the skiplist_alloc() and skiplist_free() until we know
21 * Yeah, if using pools it means some bogus cycles for checks
22 * (and an useless function call for skiplist_free) which we
23 * can removed if/when needed.
24 */
25
26#include "apr_skiplist.h"
27
28typedef struct {
30 size_t size, pos;
33
51
62
63static unsigned int get_b_rand(void)
64{
65 static unsigned int ph = 32; /* More bits than we will ever use */
66 static unsigned int randseq;
67 if (ph > 31) { /* Num bits in return of rand() */
68 ph = 0;
69 randseq = rand();
70 }
71 return randseq & (1U << ph++);
72}
73
74typedef struct {
75 size_t size;
77} memlist_t;
78
79typedef struct {
80 void *ptr;
81 char inuse;
82} chunk_t;
83
85{
86 if (sl->pool) {
87 void *ptr;
88 int found_size = 0;
89 int i;
91 memlist_t *memlist = (memlist_t *)sl->memlist->elts;
92 for (i = 0; i < sl->memlist->nelts; i++) {
93 if (memlist->size == size) {
94 int j;
95 chunk_t *chunk = (chunk_t *)memlist->list->elts;
96 found_size = 1;
97 for (j = 0; j < memlist->list->nelts; j++) {
98 if (!chunk->inuse) {
99 chunk->inuse = 1;
100 return chunk->ptr;
101 }
102 chunk++;
103 }
104 break; /* no free of this size; punt */
105 }
106 memlist++;
107 }
108 /* no free chunks */
109 ptr = apr_palloc(sl->pool, size);
110 if (!ptr) {
111 return ptr;
112 }
113 /*
114 * is this a new sized chunk? If so, we need to create a new
115 * array of them. Otherwise, re-use what we already have.
116 */
117 if (!found_size) {
118 memlist = apr_array_push(sl->memlist);
119 memlist->size = size;
120 memlist->list = apr_array_make(sl->pool, 20, sizeof(chunk_t));
121 }
122 newchunk = apr_array_push(memlist->list);
123 newchunk->ptr = ptr;
124 newchunk->inuse = 1;
125 return ptr;
126 }
127 else {
128 return malloc(size);
129 }
130}
131
133{
134 if (!sl->pool) {
135 free(mem);
136 }
137 else {
138 int i;
139 memlist_t *memlist = (memlist_t *)sl->memlist->elts;
140 for (i = 0; i < sl->memlist->nelts; i++) {
141 int j;
142 chunk_t *chunk = (chunk_t *)memlist->list->elts;
143 for (j = 0; j < memlist->list->nelts; j++) {
144 if (chunk->ptr == mem) {
145 chunk->inuse = 0;
146 return;
147 }
148 chunk++;
149 }
150 memlist++;
151 }
152 }
153}
154
156{
157 if (q->pos >= q->size) {
159 size_t size = (q->pos) ? q->pos * 2 : 32;
160 if (q->p) {
161 data = apr_palloc(q->p, size * sizeof(*data));
162 if (data && q->data) {
163 memcpy(data, q->data, q->pos * sizeof(*data));
164 }
165 }
166 else {
167 data = realloc(q->data, size * sizeof(*data));
168 }
169 if (!data) {
170 return APR_ENOMEM;
171 }
172 q->data = data;
173 q->size = size;
174 }
175 q->data[q->pos++] = m;
176 return APR_SUCCESS;
177}
178
180{
181 return (q->pos > 0) ? q->data[--q->pos] : NULL;
182}
183
185{
186 q->pos = 0;
187}
188
190{
192 if (!m) {
193 if (sl->pool) {
194 m = apr_palloc(sl->pool, sizeof *m);
195 }
196 else {
197 m = malloc(sizeof *m);
198 }
199 }
200 return m;
201}
202
207
209{
210 apr_skiplist *sl;
211 if (p) {
212 sl = apr_pcalloc(p, sizeof(apr_skiplist));
213 sl->memlist = apr_array_make(p, 20, sizeof(memlist_t));
214 sl->pool = sl->nodes_q.p = sl->stack_q.p = p;
215 }
216 else {
217 sl = calloc(1, sizeof(apr_skiplist));
218 if (!sl) {
219 return APR_ENOMEM;
220 }
221 }
222 *s = sl;
223 return APR_SUCCESS;
224}
225
226static int indexing_comp(void *a, void *b)
227{
228 void *ac = (void *) (((apr_skiplist *) a)->compare);
229 void *bc = (void *) (((apr_skiplist *) b)->compare);
230 return ((ac < bc) ? -1 : ((ac > bc) ? 1 : 0));
231}
232
233static int indexing_compk(void *ac, void *b)
234{
235 void *bc = (void *) (((apr_skiplist *) b)->compare);
236 return ((ac < bc) ? -1 : ((ac > bc) ? 1 : 0));
237}
238
240{
241 apr_status_t rv;
242 apr_skiplist *sl;
243 rv = skiplisti_init(&sl, p);
244 if (rv != APR_SUCCESS) {
245 *s = NULL;
246 return rv;
247 }
248 rv = skiplisti_init(&sl->index, p);
249 if (rv != APR_SUCCESS) {
250 *s = NULL;
251 return rv;
252 }
254 *s = sl;
255 return APR_SUCCESS;
256}
257
261{
262 if (sl->compare && sl->comparek) {
264 }
265 else {
266 sl->compare = comp;
267 sl->comparek = compk;
268 }
269}
270
274{
277 int icount = 0;
278 apr_skiplist_find(sl->index, (void *)comp, &m);
279 if (m) {
280 return; /* Index already there! */
281 }
282 if (skiplisti_init(&ni, sl->pool) != APR_SUCCESS) {
283 abort();
284 return;
285 }
287 /* Build the new index... This can be expensive! */
289 while (m->prev) {
290 m = m->prev;
291 icount++;
292 }
293 for (m = apr_skiplist_getlist(sl); m; apr_skiplist_next(sl, &m)) {
294 int j = icount - 1;
296 nsln = apr_skiplist_insert(ni, m->data);
297 /* skip from main index down list */
298 while (j > 0) {
299 m = m->nextindex;
300 j--;
301 }
302 /* insert this node in the indexlist after m */
303 nsln->nextindex = m->nextindex;
304 if (m->nextindex) {
305 m->nextindex->previndex = nsln;
306 }
307 nsln->previndex = m;
308 m->nextindex = nsln;
309 }
310}
311
315 int last)
316{
317 int count = 0;
319 for (m = sl->top; m; count++) {
320 if (m->next) {
321 int compared = comp(data, m->next->data);
322 if (compared == 0) {
323 found = m = m->next;
324 if (!last) {
325 break;
326 }
327 continue;
328 }
329 if (compared > 0) {
330 m = m->next;
331 continue;
332 }
333 }
334 m = m->down;
335 }
336 if (found) {
337 while (found->down) {
338 found = found->down;
339 }
340 *ret = found;
341 }
342 else {
343 *ret = NULL;
344 }
345 return count;
346}
347
348static void *find_compare(apr_skiplist *sli, void *data,
351 int last)
352{
354 apr_skiplist *sl;
355 if (!comp) {
356 if (iter) {
357 *iter = NULL;
358 }
359 return NULL;
360 }
361 if (comp == sli->compare || !sli->index) {
362 sl = sli;
363 }
364 else {
365 apr_skiplist_find(sli->index, (void *)comp, &m);
366 if (!m) {
367 if (iter) {
368 *iter = NULL;
369 }
370 return NULL;
371 }
372 sl = (apr_skiplist *) m->data;
373 }
375 if (iter) {
376 *iter = m;
377 }
378 return (m) ? m->data : NULL;
379}
380
384{
385 return find_compare(sl, data, iter, comp, 0);
386}
387
389{
390 return find_compare(sl, data, iter, sl->compare, 0);
391}
392
396{
397 return find_compare(sl, data, iter, comp, 1);
398}
399
402{
403 return find_compare(sl, data, iter, sl->compare, 1);
404}
405
406
408{
409 if (!sl->bottom) {
410 return NULL;
411 }
412 return sl->bottom->next;
413}
414
416{
417 if (!*iter) {
418 return NULL;
419 }
420 *iter = (*iter)->next;
421 return (*iter) ? ((*iter)->data) : NULL;
422}
423
425{
426 if (!*iter) {
427 return NULL;
428 }
429 *iter = (*iter)->prev;
430 return (*iter) ? ((*iter)->data) : NULL;
431}
432
434{
435 return (iter) ? iter->data : NULL;
436}
437
438/* forward declared */
441
443{
444 /* Skiplists (even empty) always have a top node, although this
445 * implementation defers its creation until the first insert, or
446 * deletes it with the last remove. We want the real height here.
447 */
448 return sl->height ? sl->height : 1;
449}
450
452 apr_skiplist_compare comp, int add,
454{
455 apr_skiplistnode *m, *p, *tmp, *ret = NULL;
456 int ch, top_nh, nh = 1;
457
458 ch = skiplist_height(sl);
459 if (sl->preheight) {
460 while (nh < sl->preheight && get_b_rand()) {
461 nh++;
462 }
463 }
464 else {
465 while (nh <= ch && get_b_rand()) {
466 nh++;
467 }
468 }
469 top_nh = nh;
470
471 /* Now we have in nh the height at which we wish to insert our new node,
472 * and in ch the current height: don't create skip paths to the inserted
473 * element until the walk down through the tree (which decrements ch)
474 * reaches nh. From there, any walk down pushes the current node on a
475 * stack (the node(s) after which we would insert) to pop back through
476 * for insertion later.
477 */
478 m = sl->top;
479 while (m) {
480 /*
481 * To maintain stability, dups (compared == 0) must be added
482 * AFTER each other.
483 */
484 if (m->next) {
485 int compared = comp(data, m->next->data);
486 if (compared == 0) {
487 if (!add) {
488 /* Keep the existing element(s) */
490 return NULL;
491 }
492 if (add < 0) {
493 /* Remove this element and continue with the next node
494 * or the new top if the current one is also removed.
495 */
496 apr_skiplistnode *top = sl->top;
497 skiplisti_remove(sl, m->next, myfree);
498 if (top != sl->top) {
499 m = sl->top;
501 ch = skiplist_height(sl);
502 nh = top_nh;
503 }
504 continue;
505 }
506 }
507 if (compared >= 0) {
508 m = m->next;
509 continue;
510 }
511 }
512 if (ch <= nh) {
513 /* push on stack */
514 skiplist_qpush(&sl->stack_q, m);
515 }
516 m = m->down;
517 ch--;
518 }
519 /* Pop the stack and insert nodes */
520 p = NULL;
521 while ((m = skiplist_qpop(&sl->stack_q))) {
522 tmp = skiplist_new_node(sl);
523 tmp->next = m->next;
524 if (m->next) {
525 m->next->prev = tmp;
526 }
527 m->next = tmp;
528 tmp->prev = m;
529 tmp->up = NULL;
530 tmp->nextindex = tmp->previndex = NULL;
531 tmp->down = p;
532 if (p) {
533 p->up = tmp;
534 }
535 else {
536 /* This sets ret to the bottom-most node we are inserting */
537 ret = tmp;
538 }
539 tmp->data = data;
540 tmp->sl = sl;
541 p = tmp;
542 }
543
544 /* Now we are sure the node is inserted, grow our tree to 'nh' tall */
545 for (; sl->height < nh; sl->height++) {
546 m = skiplist_new_node(sl);
547 tmp = skiplist_new_node(sl);
548 m->up = m->prev = m->nextindex = m->previndex = NULL;
549 m->next = tmp;
550 m->down = sl->top;
551 m->data = NULL;
552 m->sl = sl;
553 if (sl->top) {
554 sl->top->up = m;
555 }
556 else {
557 sl->bottom = sl->bottomend = m;
558 }
559 sl->top = sl->topend = tmp->prev = m;
560 tmp->up = tmp->next = tmp->nextindex = tmp->previndex = NULL;
561 tmp->down = p;
562 tmp->data = data;
563 tmp->sl = sl;
564 if (p) {
565 p->up = tmp;
566 }
567 else {
568 /* This sets ret to the bottom-most node we are inserting */
569 ret = tmp;
570 }
571 p = tmp;
572 }
573 if (sl->index != NULL) {
574 /*
575 * this is a external insertion, we must insert into each index as
576 * well
577 */
579 li = ret;
580 for (p = apr_skiplist_getlist(sl->index); p; apr_skiplist_next(sl->index, &p)) {
581 apr_skiplist *sli = (apr_skiplist *)p->data;
582 ni = insert_compare(sli, ret->data, sli->compare, 1, NULL);
583 li->nextindex = ni;
584 ni->previndex = li;
585 li = ni;
586 }
587 }
588 sl->size++;
589 return ret;
590}
591
594{
595 if (!comp) {
596 return NULL;
597 }
598 return insert_compare(sl, data, comp, 0, NULL);
599}
600
602{
604}
605
608{
609 if (!comp) {
610 return NULL;
611 }
612 return insert_compare(sl, data, comp, 1, NULL);
613}
614
616{
617 return apr_skiplist_add_compare(sl, data, sl->compare);
618}
619
623{
624 if (!comp) {
625 return NULL;
626 }
627 return insert_compare(sl, data, comp, -1, myfree);
628}
629
632{
634}
635
636#if 0
638{
639 apr_skiplistnode *p, *q;
640 fprintf(stderr, "Skiplist Structure (height: %d)\n", sl->height);
641 p = sl->bottom;
642 while (p) {
643 q = p;
645 while (q) {
646 fprintf(stderr, "%p ", q->data);
647 q = q->up;
648 }
649 fprintf(stderr, "\n");
650 p = p->next;
651 }
652}
653#endif
654
657{
659 if (!m) {
660 return 0;
661 }
662 if (m->nextindex) {
663 skiplisti_remove(m->nextindex->sl, m->nextindex, NULL);
664 }
665 while (m->up) {
666 m = m->up;
667 }
668 do {
669 p = m;
670 /* take me out of the list */
671 p->prev->next = p->next;
672 if (p->next) {
673 p->next->prev = p->prev;
674 }
675 m = m->down;
676 /* This only frees the actual data in the bottom one */
677 if (!m && myfree && p->data) {
678 myfree(p->data);
679 }
680 skiplist_put_node(sl, p);
681 } while (m);
682 sl->size--;
683 while (sl->top && sl->top->next == NULL) {
684 /* While the row is empty and we are not on the bottom row */
685 p = sl->top;
686 sl->top = sl->top->down;/* Move top down one */
687 if (sl->top) {
688 sl->top->up = NULL; /* Make it think its the top */
689 }
690 skiplist_put_node(sl, p);
691 sl->height--;
692 }
693 if (!sl->top) {
694 sl->bottom = sl->bottomend = NULL;
695 sl->topend = NULL;
696 }
697 return skiplist_height(sl);
698}
699
703{
705 if (!m) {
706 return 0;
707 }
708 while (m->down) {
709 m = m->down;
710 }
711 while (m->previndex) {
712 m = m->previndex;
713 }
714 return skiplisti_remove(sl, m, myfree);
715}
716
718 void *data,
720{
722 apr_skiplist *sl;
723 if (!comp) {
724 return 0;
725 }
726 if (comp == sli->comparek || !sli->index) {
727 sl = sli;
728 }
729 else {
730 apr_skiplist_find(sli->index, (void *)comp, &m);
731 if (!m) {
732 return 0;
733 }
734 sl = (apr_skiplist *) m->data;
735 }
737 if (!m) {
738 return 0;
739 }
740 while (m->previndex) {
741 m = m->previndex;
742 }
743 return skiplisti_remove(sl, m, myfree);
744}
745
747{
749}
750
752{
753 /*
754 * This must remove even the place holder nodes (bottom though top)
755 * because we specify in the API that one can free the Skiplist after
756 * making this call without memory leaks
757 */
758 apr_skiplistnode *m, *p, *u;
759 m = sl->bottom;
760 while (m) {
761 p = m->next;
762 if (myfree && p && p->data) {
763 myfree(p->data);
764 }
765 do {
766 u = m->up;
767 skiplist_put_node(sl, m);
768 m = u;
769 } while (m);
770 m = p;
771 }
772 sl->top = sl->bottom = NULL;
773 sl->topend = sl->bottomend = NULL;
774 sl->height = 0;
775 sl->size = 0;
776}
777
779{
781 void *data = NULL;
783 if (sln) {
784 data = sln->data;
786 }
787 return data;
788}
789
791{
794 if (sln) {
795 return sln->data;
796 }
797 return NULL;
798}
799
801{
802 return sl->size;
803}
804
806{
807 return skiplist_height(sl);
808}
809
811{
812 return sl->preheight;
813}
814
816{
817 sl->preheight = (to > 0) ? to : 0;
818}
819
820static void skiplisti_destroy(void *vsl)
821{
823}
824
826{
828 ;
830 if (!sl->pool) {
831 while (sl->nodes_q.pos)
832 free(sl->nodes_q.data[--sl->nodes_q.pos]);
833 free(sl->nodes_q.data);
834 free(sl->stack_q.data);
835 free(sl);
836 }
837}
838
840{
841 /* Check integrity! */
843 struct apr_skiplistnode *b2;
844 if (sl1->bottomend == NULL || sl1->bottomend->prev == NULL) {
846 temp = *sl1;
847 *sl1 = *sl2;
848 *sl2 = temp;
849 /* swap them so that sl2 can be freed normally upon return. */
850 return sl1;
851 }
852 if(sl2->bottom == NULL || sl2->bottom->next == NULL) {
854 return sl1;
855 }
856 /* This is what makes it brute force... Just insert :/ */
858 while (b2) {
859 apr_skiplist_insert(sl1, b2->data);
861 }
863 return sl1;
864}
HANDLE char int int nh
static apr_status_t skiplist_qpush(apr_skiplist_q *q, apr_skiplistnode *m)
static apr_skiplistnode * insert_compare(apr_skiplist *sl, void *data, apr_skiplist_compare comp, int add, apr_skiplist_freefunc myfree)
static void * find_compare(apr_skiplist *sli, void *data, apr_skiplistnode **iter, apr_skiplist_compare comp, int last)
static int indexing_comp(void *a, void *b)
static apr_status_t skiplist_put_node(apr_skiplist *sl, apr_skiplistnode *m)
static apr_skiplistnode * skiplist_new_node(apr_skiplist *sl)
static void skiplisti_destroy(void *vsl)
static int skiplisti_find_compare(apr_skiplist *sl, void *data, apr_skiplistnode **ret, apr_skiplist_compare comp, int last)
static APR_INLINE void skiplist_qclear(apr_skiplist_q *q)
static int indexing_compk(void *ac, void *b)
static unsigned int get_b_rand(void)
static APR_INLINE int skiplist_height(const apr_skiplist *sl)
static APR_INLINE apr_skiplistnode * skiplist_qpop(apr_skiplist_q *q)
static apr_status_t skiplisti_init(apr_skiplist **s, apr_pool_t *p)
static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_freefunc myfree)
APR skip list implementation.
return found
Definition core.c:2840
#define APR_ENOMEM
Definition apr_errno.h:683
unsigned int count
Definition apr_md5.h:152
apr_bucket apr_bucket_brigade * a
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
APR_DECLARE(void)
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
void apr_skiplistnode ** iter
void(* apr_skiplist_freefunc)(void *)
void apr_skiplistnode apr_skiplist_compare comp
apr_skiplist * sl2
void * mem
int(* apr_skiplist_compare)(void *, void *)
void apr_skiplist_freefunc myfree
int to
const char char ** last
const char * s
Definition apr_strings.h:95
const void * m
apr_pool_t * p
Definition md_event.c:32
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
apr_pool_t * p
apr_skiplistnode ** data
apr_array_header_t * memlist
apr_skiplist_compare comparek
apr_pool_t * pool
apr_skiplist_q stack_q
apr_skiplistnode * bottom
apr_skiplistnode * top
apr_skiplist * index
apr_skiplistnode * topend
apr_skiplist_compare compare
apr_skiplist_q nodes_q
apr_skiplistnode * bottomend
apr_skiplistnode * previndex
apr_skiplistnode * nextindex
apr_skiplist * sl
apr_skiplistnode * prev
apr_skiplistnode * next
apr_skiplistnode * up
apr_skiplistnode * down
char inuse
void * ptr
size_t size
apr_array_header_t * list