/src/dovecot/src/lib/priorityq.c
Line | Count | Source |
1 | | /* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "array.h" |
5 | | #include "priorityq.h" |
6 | | |
7 | | /* Macros for moving inside an array implementation of binary tree where |
8 | | [0] is the root. */ |
9 | | #define PARENT_IDX(idx) \ |
10 | 0 | (((idx) - 1) / 2) |
11 | | #define LEFT_CHILD_IDX(idx) \ |
12 | 0 | ((idx) * 2 + 1) |
13 | | #define RIGHT_CHILD_IDX(idx) \ |
14 | 0 | ((idx) * 2 + 2) |
15 | | |
16 | | struct priorityq { |
17 | | priorityq_cmp_callback_t *cmp_callback; |
18 | | |
19 | | ARRAY(struct priorityq_item *) items; |
20 | | }; |
21 | | |
22 | | struct priorityq * |
23 | | priorityq_init(priorityq_cmp_callback_t *cmp_callback, unsigned int init_size) |
24 | 0 | { |
25 | 0 | struct priorityq *pq; |
26 | |
|
27 | 0 | pq = i_new(struct priorityq, 1); |
28 | 0 | pq->cmp_callback = cmp_callback; |
29 | 0 | i_array_init(&pq->items, init_size); |
30 | 0 | return pq; |
31 | 0 | } |
32 | | |
33 | | void priorityq_deinit(struct priorityq **_pq) |
34 | 0 | { |
35 | 0 | struct priorityq *pq = *_pq; |
36 | |
|
37 | 0 | *_pq = NULL; |
38 | 0 | array_free(&pq->items); |
39 | 0 | i_free(pq); |
40 | 0 | } |
41 | | |
42 | | unsigned int priorityq_count(const struct priorityq *pq) |
43 | 0 | { |
44 | 0 | return array_count(&pq->items); |
45 | 0 | } |
46 | | |
47 | | static void heap_items_swap(struct priorityq_item **items, |
48 | | unsigned int idx1, unsigned int idx2) |
49 | 0 | { |
50 | 0 | struct priorityq_item *tmp; |
51 | | |
52 | | /* swap the item indexes */ |
53 | 0 | i_assert(items[idx1]->idx == idx1); |
54 | 0 | i_assert(items[idx2]->idx == idx2); |
55 | | |
56 | 0 | items[idx1]->idx = idx2; |
57 | 0 | items[idx2]->idx = idx1; |
58 | | |
59 | | /* swap the item pointers */ |
60 | 0 | tmp = items[idx1]; |
61 | 0 | items[idx1] = items[idx2]; |
62 | 0 | items[idx2] = tmp; |
63 | 0 | } |
64 | | |
65 | | static unsigned int |
66 | | heap_item_bubble_up(struct priorityq *pq, unsigned int idx) |
67 | 0 | { |
68 | 0 | struct priorityq_item **items; |
69 | 0 | unsigned int parent_idx, count; |
70 | |
|
71 | 0 | items = array_get_modifiable(&pq->items, &count); |
72 | 0 | while (idx > 0) { |
73 | 0 | parent_idx = PARENT_IDX(idx); |
74 | |
|
75 | 0 | i_assert(idx < count); |
76 | 0 | if (pq->cmp_callback(items[idx], items[parent_idx]) >= 0) |
77 | 0 | break; |
78 | | |
79 | | /* wrong order - swap */ |
80 | 0 | heap_items_swap(items, idx, parent_idx); |
81 | 0 | idx = parent_idx; |
82 | 0 | } |
83 | 0 | return idx; |
84 | 0 | } |
85 | | |
86 | | static void heap_item_bubble_down(struct priorityq *pq, unsigned int idx) |
87 | 0 | { |
88 | 0 | struct priorityq_item **items; |
89 | 0 | unsigned int left_idx, right_idx, min_child_idx, count; |
90 | |
|
91 | 0 | items = array_get_modifiable(&pq->items, &count); |
92 | 0 | while ((left_idx = LEFT_CHILD_IDX(idx)) < count) { |
93 | 0 | right_idx = RIGHT_CHILD_IDX(idx); |
94 | 0 | if (right_idx >= count || |
95 | 0 | pq->cmp_callback(items[left_idx], items[right_idx]) < 0) |
96 | 0 | min_child_idx = left_idx; |
97 | 0 | else |
98 | 0 | min_child_idx = right_idx; |
99 | |
|
100 | 0 | if (pq->cmp_callback(items[min_child_idx], items[idx]) >= 0) |
101 | 0 | break; |
102 | | |
103 | | /* wrong order - swap */ |
104 | 0 | heap_items_swap(items, idx, min_child_idx); |
105 | 0 | idx = min_child_idx; |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | void priorityq_add(struct priorityq *pq, struct priorityq_item *item) |
110 | 0 | { |
111 | 0 | item->idx = array_count(&pq->items); |
112 | 0 | array_push_back(&pq->items, &item); |
113 | 0 | (void)heap_item_bubble_up(pq, item->idx); |
114 | 0 | } |
115 | | |
116 | | static void priorityq_remove_idx(struct priorityq *pq, unsigned int idx) |
117 | 0 | { |
118 | 0 | struct priorityq_item **items; |
119 | 0 | unsigned int count; |
120 | |
|
121 | 0 | items = array_get_modifiable(&pq->items, &count); |
122 | 0 | i_assert(idx < count); |
123 | | |
124 | | /* move last item over the removed one and fix the heap */ |
125 | 0 | count--; |
126 | 0 | heap_items_swap(items, idx, count); |
127 | 0 | array_delete(&pq->items, count, 1); |
128 | |
|
129 | 0 | if (count > 0 && idx != count) { |
130 | 0 | if (idx > 0) |
131 | 0 | idx = heap_item_bubble_up(pq, idx); |
132 | 0 | heap_item_bubble_down(pq, idx); |
133 | 0 | } |
134 | 0 | } |
135 | | |
136 | | void priorityq_remove(struct priorityq *pq, struct priorityq_item *item) |
137 | 0 | { |
138 | 0 | priorityq_remove_idx(pq, item->idx); |
139 | 0 | item->idx = UINT_MAX; |
140 | 0 | } |
141 | | |
142 | | struct priorityq_item *priorityq_peek(struct priorityq *pq) |
143 | 0 | { |
144 | 0 | struct priorityq_item *const *items; |
145 | |
|
146 | 0 | if (array_count(&pq->items) == 0) |
147 | 0 | return NULL; |
148 | | |
149 | 0 | items = array_front(&pq->items); |
150 | 0 | return items[0]; |
151 | 0 | } |
152 | | |
153 | | struct priorityq_item *priorityq_pop(struct priorityq *pq) |
154 | 0 | { |
155 | 0 | struct priorityq_item *item; |
156 | |
|
157 | 0 | item = priorityq_peek(pq); |
158 | 0 | if (item != NULL) { |
159 | 0 | priorityq_remove_idx(pq, 0); |
160 | 0 | item->idx = UINT_MAX; |
161 | 0 | } |
162 | 0 | return item; |
163 | 0 | } |
164 | | |
165 | | struct priorityq_item *const *priorityq_items(struct priorityq *pq) |
166 | 0 | { |
167 | 0 | if (array_count(&pq->items) == 0) |
168 | 0 | return NULL; |
169 | | |
170 | 0 | return array_front(&pq->items); |
171 | 0 | } |