dovecot: Added a priority queue implementation.

dovecot at dovecot.org dovecot at dovecot.org
Thu Jan 3 23:19:37 EET 2008


details:   http://hg.dovecot.org/dovecot/rev/618472c2c3c5
changeset: 7097:618472c2c3c5
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Jan 03 23:18:46 2008 +0200
description:
Added a priority queue implementation.

diffstat:

4 files changed, 295 insertions(+), 1 deletion(-)
src/lib/Makefile.am  |    2 
src/lib/priorityq.c  |  158 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/lib/priorityq.h  |   35 +++++++++++
src/tests/test-lib.c |  101 +++++++++++++++++++++++++++++++

diffs (truncated from 354 to 300 lines):

diff -r bf1d4795085f -r 618472c2c3c5 src/lib/Makefile.am
--- 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 @@ liblib_a_SOURCES = \
 	primes.c \
 	printf-format-fix.c \
 	process-title.c \
+	priorityq.c \
 	randgen.c \
 	read-full.c \
 	restrict-access.c \
@@ -161,6 +162,7 @@ headers = \
 	primes.h \
 	printf-format-fix.h \
 	process-title.h \
+	priorityq.h \
 	randgen.h \
 	read-full.h \
 	restrict-access.h \
diff -r bf1d4795085f -r 618472c2c3c5 src/lib/priorityq.c
--- /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;
+}
diff -r bf1d4795085f -r 618472c2c3c5 src/lib/priorityq.h
--- /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
diff -r bf1d4795085f -r 618472c2c3c5 src/tests/test-lib.c
--- 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,8 +6,11 @@
 #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)
 {
@@ -212,7 +215,7 @@ static void test_mempool_alloconly(void)
 
 	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);
 
@@ -231,6 +234,101 @@ static void test_mempool_alloconly(void)
 		}
 	}
 	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)


More information about the dovecot-cvs mailing list