Mercurial > dovecot > original-hg > dovecot-1.2
changeset 7097:618472c2c3c5 HEAD
Added a priority queue implementation.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 03 Jan 2008 23:18:46 +0200 |
parents | bf1d4795085f |
children | becdf2eacdce |
files | src/lib/Makefile.am src/lib/priorityq.c src/lib/priorityq.h src/tests/test-lib.c |
diffstat | 4 files changed, 295 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib/Makefile.am Thu Jan 03 21:18:39 2008 +0200 +++ b/src/lib/Makefile.am Thu Jan 03 23:18:46 2008 +0200 @@ -78,6 +78,7 @@ primes.c \ printf-format-fix.c \ process-title.c \ + priorityq.c \ randgen.c \ read-full.c \ restrict-access.c \ @@ -161,6 +162,7 @@ primes.h \ printf-format-fix.h \ process-title.h \ + priorityq.h \ randgen.h \ read-full.h \ restrict-access.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/priorityq.c Thu Jan 03 23:18:46 2008 +0200 @@ -0,0 +1,158 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "priorityq.h" + +/* Macros for moving inside an array implementation of binary tree where + [0] is the root. */ +#define PARENT_IDX(idx) \ + (((idx) - 1) / 2) +#define LEFT_CHILD_IDX(idx) \ + ((idx) * 2 + 1) +#define RIGHT_CHILD_IDX(idx) \ + ((idx) * 2 + 2) + +struct priorityq { + priorityq_cmp_callback_t *cmp_callback; + + ARRAY_DEFINE(items, struct priorityq_item *); +}; + +struct priorityq * +priorityq_init(priorityq_cmp_callback_t *cmp_callback, unsigned int init_size) +{ + struct priorityq *pq; + + pq = i_new(struct priorityq, 1); + pq->cmp_callback = cmp_callback; + i_array_init(&pq->items, init_size); + return pq; +} + +void priorityq_deinit(struct priorityq **_pq) +{ + struct priorityq *pq = *_pq; + + *_pq = NULL; + array_free(&pq->items); + i_free(pq); +} + +unsigned int priorityq_count(const struct priorityq *pq) +{ + return array_count(&pq->items); +} + +static void heap_items_swap(struct priorityq_item **items, + unsigned int idx1, unsigned int idx2) +{ + struct priorityq_item *tmp; + + /* swap the item indexes */ + i_assert(items[idx1]->idx == idx1); + i_assert(items[idx2]->idx == idx2); + + items[idx1]->idx = idx2; + items[idx2]->idx = idx1; + + /* swap the item pointers */ + tmp = items[idx1]; + items[idx1] = items[idx2]; + items[idx2] = tmp; +} + +static unsigned int +heap_item_bubble_up(struct priorityq *pq, unsigned int idx) +{ + struct priorityq_item **items; + unsigned int parent_idx; + + items = array_idx_modifiable(&pq->items, 0); + while (idx > 0) { + parent_idx = PARENT_IDX(idx); + if (pq->cmp_callback(items[idx], items[parent_idx]) >= 0) + break; + + /* wrong order - swap */ + heap_items_swap(items, idx, parent_idx); + idx = parent_idx; + } + return idx; +} + +static void heap_item_bubble_down(struct priorityq *pq, unsigned int idx) +{ + struct priorityq_item **items; + unsigned int left_idx, right_idx, min_child_idx, count; + + items = array_get_modifiable(&pq->items, &count); + while ((left_idx = LEFT_CHILD_IDX(idx)) < count) { + right_idx = RIGHT_CHILD_IDX(idx); + if (right_idx >= count || + pq->cmp_callback(items[left_idx], items[right_idx]) < 0) + min_child_idx = left_idx; + else + min_child_idx = right_idx; + + if (pq->cmp_callback(items[min_child_idx], items[idx]) >= 0) + break; + + /* wrong order - swap */ + heap_items_swap(items, idx, min_child_idx); + idx = min_child_idx; + } +} + +void priorityq_add(struct priorityq *pq, struct priorityq_item *item) +{ + item->idx = array_count(&pq->items); + array_append(&pq->items, &item, 1); + (void)heap_item_bubble_up(pq, item->idx); +} + +static void priorityq_remove_idx(struct priorityq *pq, unsigned int idx) +{ + struct priorityq_item **items; + unsigned int count; + + items = array_get_modifiable(&pq->items, &count); + i_assert(idx < count); + + /* move last item over the removed one and fix the heap */ + count--; + heap_items_swap(items, idx, count); + array_delete(&pq->items, count, 1); + + if (count > 0) { + if (idx > 0) + idx = heap_item_bubble_up(pq, idx); + heap_item_bubble_down(pq, idx); + } +} + +void priorityq_remove(struct priorityq *pq, struct priorityq_item *item) +{ + priorityq_remove_idx(pq, item->idx); +} + +struct priorityq_item *priorityq_peek(struct priorityq *pq) +{ + struct priorityq_item *const *items; + + if (array_count(&pq->items) == 0) + return NULL; + + items = array_idx(&pq->items, 0); + return items[0]; +} + +struct priorityq_item *priorityq_pop(struct priorityq *pq) +{ + struct priorityq_item *item; + + item = priorityq_peek(pq); + if (item != NULL) + priorityq_remove_idx(pq, 0); + return item; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/priorityq.h Thu Jan 03 23:18:46 2008 +0200 @@ -0,0 +1,35 @@ +#ifndef PRIORITYQ_H +#define PRIORITYQ_H + +/* Priority queue implementation using heap. The items you add to the queue + must begin with a struct priorityq_item. This is necessary for + priorityq_remove() to work fast. */ + +struct priorityq_item { + /* Current index in the queue array, updated automatically. */ + unsigned int idx; + /* [your own data] */ +}; + +/* Returns <0, 0 or >0 */ +typedef int priorityq_cmp_callback_t(const void *p1, const void *p2); + +/* Create a new priority queue. Callback is used to compare added items. */ +struct priorityq * +priorityq_init(priorityq_cmp_callback_t *cmp_callback, unsigned int init_size); +void priorityq_deinit(struct priorityq **pq); + +/* Return number of items in the queue. */ +unsigned int priorityq_count(const struct priorityq *pq); + +/* Add a new item to the queue. */ +void priorityq_add(struct priorityq *pq, struct priorityq_item *item); +/* Remove the specified item from the queue. */ +void priorityq_remove(struct priorityq *pq, struct priorityq_item *item); + +/* Return the item with the highest priority. Returns NULL if queue is empty. */ +struct priorityq_item *priorityq_peek(struct priorityq *pq); +/* Like priorityq_peek(), but also remove the returned item from the queue. */ +struct priorityq_item *priorityq_pop(struct priorityq *pq); + +#endif
--- a/src/tests/test-lib.c Thu Jan 03 21:18:39 2008 +0200 +++ b/src/tests/test-lib.c Thu Jan 03 23:18:46 2008 +0200 @@ -6,9 +6,12 @@ #include "base64.h" #include "bsearch-insert-pos.h" #include "aqueue.h" +#include "priorityq.h" #include "seq-range-array.h" #include "str-sanitize.h" +#include <stdlib.h> + static void test_base64_encode(void) { static const char *input[] = { @@ -212,7 +215,7 @@ for (i = 0; i < 64; i++) { for (j = 1; j <= 128; j++) { - pool = pool_alloconly_create("test", i); + pool = pool_alloconly_create(MEMPOOL_GROWING"test", i); mem[0] = p_malloc(pool, j); memset(mem[0], j, j); @@ -233,6 +236,101 @@ test_out("mempool_alloconly", success); } +struct pq_test_item { + struct priorityq_item item; + int num; +}; + +static int cmp_int(const void *p1, const void *p2) +{ + const struct pq_test_item *i1 = p1, *i2 = p2; + + return i1->num - i2->num; +} + +static void test_priorityq(void) +{ +#define PQ_MAX_ITEMS 100 + static const int input[] = { + 1, 2, 3, 4, 5, 6, 7, 8, -1, + 8, 7, 6, 5, 4, 3, 2, 1, -1, + 8, 7, 5, 6, 1, 3, 4, 2, -1, + -1 + }; + static const int output[] = { + 1, 2, 3, 4, 5, 6, 7, 8 + }; + struct pq_test_item *item, items[PQ_MAX_ITEMS]; + unsigned int i, j; + struct priorityq *pq; + pool_t pool; + int prev; + bool success = TRUE; + + pool = pool_alloconly_create("priorityq items", 1024); + + /* simple tests with popping only */ + for (i = 0; input[i] != -1; i++) { + p_clear(pool); + pq = priorityq_init(cmp_int, 1); + for (j = 0; input[i] != -1; i++, j++) { + if (priorityq_count(pq) != j) + success = FALSE; + item = p_new(pool, struct pq_test_item, 1); + item->num = input[i]; + priorityq_add(pq, &item->item); + } + for (j = 0; j < N_ELEMENTS(output); j++) { + if (priorityq_count(pq) != N_ELEMENTS(output) - j) + success = FALSE; + + item = (struct pq_test_item *)priorityq_peek(pq); + if (output[j] != item->num) + success = FALSE; + item = (struct pq_test_item *)priorityq_pop(pq); + if (output[j] != item->num) + success = FALSE; + } + if (priorityq_count(pq) != 0) + success = FALSE; + if (priorityq_peek(pq) != NULL || priorityq_pop(pq) != NULL) + success = FALSE; + priorityq_deinit(&pq); + } + test_out("priorityq(1)", success); + + /* randomized tests, remove elements */ + success = TRUE; + for (i = 0; i < 100; i++) { + pq = priorityq_init(cmp_int, 1); + for (j = 0; j < PQ_MAX_ITEMS; j++) { + items[j].num = rand(); + priorityq_add(pq, &items[j].item); + } + for (j = 0; j < PQ_MAX_ITEMS; j++) { + if (rand() % 3 == 0) { + priorityq_remove(pq, &items[j].item); + items[j].num = -1; + } + } + prev = 0; + while (priorityq_count(pq) > 0) { + item = (struct pq_test_item *)priorityq_pop(pq); + if (item->num < 0 || prev > item->num) + success = FALSE; + prev = item->num; + item->num = -1; + } + for (j = 0; j < PQ_MAX_ITEMS; j++) { + if (items[j].num != -1) + success = FALSE; + } + priorityq_deinit(&pq); + } + test_out("priorityq(2)", success); + pool_unref(&pool); +} + static void test_seq_range_array(void) { static const unsigned int input_min = 1, input_max = 5; @@ -319,6 +417,7 @@ test_base64_decode, test_bsearch_insert_pos, test_mempool_alloconly, + test_priorityq, test_seq_range_array, test_str_sanitize,