Coverage Report

Created: 2026-04-12 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}