| #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; | |
| } |