Systemd/src/basic/prioq.c
Lennart Poettering 0c69794138 tree-wide: remove Lennart's copyright lines
These lines are generally out-of-date, incomplete and unnecessary. With
SPDX and git repository much more accurate and fine grained information
about licensing and authorship is available, hence let's drop the
per-file copyright notice. Of course, removing copyright lines of others
is problematic, hence this commit only removes my own lines and leaves
all others untouched. It might be nicer if sooner or later those could
go away too, making git the only and accurate source of authorship
information.
2018-06-14 10:20:20 +02:00

302 lines
6.6 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Priority Queue
* The prioq object implements a priority queue. That is, it orders objects by
* their priority and allows O(1) access to the object with the highest
* priority. Insertion and removal are Θ(log n). Optionally, the caller can
* provide a pointer to an index which will be kept up-to-date by the prioq.
*
* The underlying algorithm used in this implementation is a Heap.
*/
#include <errno.h>
#include <stdlib.h>
#include "alloc-util.h"
#include "hashmap.h"
#include "prioq.h"
struct prioq_item {
void *data;
unsigned *idx;
};
struct Prioq {
compare_func_t compare_func;
unsigned n_items, n_allocated;
struct prioq_item *items;
};
Prioq *prioq_new(compare_func_t compare_func) {
Prioq *q;
q = new0(Prioq, 1);
if (!q)
return q;
q->compare_func = compare_func;
return q;
}
Prioq* prioq_free(Prioq *q) {
if (!q)
return NULL;
free(q->items);
return mfree(q);
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
assert(q);
if (*q)
return 0;
*q = prioq_new(compare_func);
if (!*q)
return -ENOMEM;
return 0;
}
static void swap(Prioq *q, unsigned j, unsigned k) {
void *saved_data;
unsigned *saved_idx;
assert(q);
assert(j < q->n_items);
assert(k < q->n_items);
assert(!q->items[j].idx || *(q->items[j].idx) == j);
assert(!q->items[k].idx || *(q->items[k].idx) == k);
saved_data = q->items[j].data;
saved_idx = q->items[j].idx;
q->items[j].data = q->items[k].data;
q->items[j].idx = q->items[k].idx;
q->items[k].data = saved_data;
q->items[k].idx = saved_idx;
if (q->items[j].idx)
*q->items[j].idx = j;
if (q->items[k].idx)
*q->items[k].idx = k;
}
static unsigned shuffle_up(Prioq *q, unsigned idx) {
assert(q);
while (idx > 0) {
unsigned k;
k = (idx-1)/2;
if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
break;
swap(q, idx, k);
idx = k;
}
return idx;
}
static unsigned shuffle_down(Prioq *q, unsigned idx) {
assert(q);
for (;;) {
unsigned j, k, s;
k = (idx+1)*2; /* right child */
j = k-1; /* left child */
if (j >= q->n_items)
break;
if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
/* So our left child is smaller than we are, let's
* remember this fact */
s = j;
else
s = idx;
if (k < q->n_items &&
q->compare_func(q->items[k].data, q->items[s].data) < 0)
/* So our right child is smaller than we are, let's
* remember this fact */
s = k;
/* s now points to the smallest of the three items */
if (s == idx)
/* No swap necessary, we're done */
break;
swap(q, idx, s);
idx = s;
}
return idx;
}
int prioq_put(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
if (q->n_items >= q->n_allocated) {
unsigned n;
struct prioq_item *j;
n = MAX((q->n_items+1) * 2, 16u);
j = reallocarray(q->items, n, sizeof(struct prioq_item));
if (!j)
return -ENOMEM;
q->items = j;
q->n_allocated = n;
}
k = q->n_items++;
i = q->items + k;
i->data = data;
i->idx = idx;
if (idx)
*idx = k;
shuffle_up(q, k);
return 0;
}
static void remove_item(Prioq *q, struct prioq_item *i) {
struct prioq_item *l;
assert(q);
assert(i);
l = q->items + q->n_items - 1;
if (i == l)
/* Last entry, let's just remove it */
q->n_items--;
else {
unsigned k;
/* Not last entry, let's replace the last entry with
* this one, and reshuffle */
k = i - q->items;
i->data = l->data;
i->idx = l->idx;
if (i->idx)
*i->idx = k;
q->n_items--;
k = shuffle_down(q, k);
shuffle_up(q, k);
}
}
_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
assert(q);
if (idx) {
if (*idx == PRIOQ_IDX_NULL ||
*idx > q->n_items)
return NULL;
i = q->items + *idx;
if (i->data != data)
return NULL;
return i;
} else {
for (i = q->items; i < q->items + q->n_items; i++)
if (i->data == data)
return i;
return NULL;
}
}
int prioq_remove(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
if (!q)
return 0;
i = find_item(q, data, idx);
if (!i)
return 0;
remove_item(q, i);
return 1;
}
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
assert(q);
i = find_item(q, data, idx);
if (!i)
return 0;
k = i - q->items;
k = shuffle_down(q, k);
shuffle_up(q, k);
return 1;
}
void *prioq_peek(Prioq *q) {
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
return q->items[0].data;
}
void *prioq_pop(Prioq *q) {
void *data;
if (!q)
return NULL;
if (q->n_items <= 0)
return NULL;
data = q->items[0].data;
remove_item(q, q->items);
return data;
}
unsigned prioq_size(Prioq *q) {
if (!q)
return 0;
return q->n_items;
}
bool prioq_isempty(Prioq *q) {
if (!q)
return true;
return q->n_items <= 0;
}