summaryrefslogtreecommitdiffstats
path: root/src/lib/priorityq.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib/priorityq.c
parentInitial commit. (diff)
downloaddovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz
dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/priorityq.c')
-rw-r--r--src/lib/priorityq.c171
1 files changed, 171 insertions, 0 deletions
diff --git a/src/lib/priorityq.c b/src/lib/priorityq.c
new file mode 100644
index 0000000..e532de8
--- /dev/null
+++ b/src/lib/priorityq.c
@@ -0,0 +1,171 @@
+/* Copyright (c) 2008-2018 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(struct priorityq_item *) items;
+};
+
+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, count;
+
+ items = array_get_modifiable(&pq->items, &count);
+ while (idx > 0) {
+ parent_idx = PARENT_IDX(idx);
+
+ i_assert(idx < count);
+ 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_push_back(&pq->items, &item);
+ (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 && idx != count) {
+ 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);
+ item->idx = UINT_MAX;
+}
+
+struct priorityq_item *priorityq_peek(struct priorityq *pq)
+{
+ struct priorityq_item *const *items;
+
+ if (array_count(&pq->items) == 0)
+ return NULL;
+
+ items = array_front(&pq->items);
+ 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);
+ item->idx = UINT_MAX;
+ }
+ return item;
+}
+
+struct priorityq_item *const *priorityq_items(struct priorityq *pq)
+{
+ if (array_count(&pq->items) == 0)
+ return NULL;
+
+ return array_front(&pq->items);
+}