blob: 199775d5afd22a6a5a1fab180ee78f19816747b6 [file] [edit]
#include "git-compat-util.h"
#include "prio-queue.h"
static inline int compare(struct prio_queue *queue, size_t i, size_t j)
{
int cmp = queue->compare(queue->array[i].data, queue->array[j].data,
queue->cb_data);
if (!cmp)
cmp = (queue->array[i].ctr > queue->array[j].ctr) -
(queue->array[i].ctr < queue->array[j].ctr);
return cmp;
}
static inline void swap(struct prio_queue *queue, size_t i, size_t j)
{
SWAP(queue->array[i], queue->array[j]);
}
void prio_queue_reverse(struct prio_queue *queue)
{
size_t i, j;
if (queue->compare)
BUG("prio_queue_reverse() on non-LIFO queue");
if (!queue->nr_)
return;
for (i = 0; i < (j = (queue->nr_ - 1) - i); i++)
swap(queue, i, j);
}
void clear_prio_queue(struct prio_queue *queue)
{
FREE_AND_NULL(queue->array);
queue->nr_ = 0;
queue->alloc = 0;
queue->insertion_ctr = 0;
queue->get_pending = 0;
}
static void sift_down_root(struct prio_queue *queue)
{
size_t ix, child;
/* Push down the one at the root */
for (ix = 0; ix * 2 + 1 < queue->nr_; ix = child) {
child = ix * 2 + 1; /* left */
if (child + 1 < queue->nr_ &&
compare(queue, child, child + 1) >= 0)
child++; /* use right child */
if (compare(queue, ix, child) <= 0)
break;
swap(queue, child, ix);
}
}
static inline void flush_get(struct prio_queue *queue)
{
if (!queue->get_pending)
return;
queue->get_pending = 0;
queue->array[0] = queue->array[--queue->nr_];
sift_down_root(queue);
}
void prio_queue_put(struct prio_queue *queue, void *thing)
{
size_t ix, parent;
if (queue->get_pending) {
queue->get_pending = 0;
queue->array[0].ctr = queue->insertion_ctr++;
queue->array[0].data = thing;
sift_down_root(queue);
return;
}
/* Append at the end */
ALLOC_GROW(queue->array, queue->nr_ + 1, queue->alloc);
queue->array[queue->nr_].ctr = queue->insertion_ctr++;
queue->array[queue->nr_].data = thing;
queue->nr_++;
if (!queue->compare)
return; /* LIFO */
/* Bubble up the new one */
for (ix = queue->nr_ - 1; ix; ix = parent) {
parent = (ix - 1) / 2;
if (compare(queue, parent, ix) <= 0)
break;
swap(queue, parent, ix);
}
}
void *prio_queue_get(struct prio_queue *queue)
{
if (queue->nr_ <= queue->get_pending) {
queue->nr_ = 0;
queue->get_pending = 0;
return NULL;
}
if (!queue->compare)
return queue->array[--queue->nr_].data; /* LIFO */
flush_get(queue);
queue->get_pending = 1;
return queue->array[0].data;
}
void *prio_queue_peek(struct prio_queue *queue)
{
if (queue->nr_ <= queue->get_pending) {
queue->nr_ = 0;
queue->get_pending = 0;
return NULL;
}
if (!queue->compare)
return queue->array[queue->nr_ - 1].data;
flush_get(queue);
return queue->array[0].data;
}