diff options
Diffstat (limited to 'src/pqueue.c')
-rw-r--r-- | src/pqueue.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/pqueue.c b/src/pqueue.c new file mode 100644 index 000000000..98152cb85 --- /dev/null +++ b/src/pqueue.c @@ -0,0 +1,153 @@ +/* + * BORING COPYRIGHT NOTICE: + * + * This file is a heavily modified version of the priority queue found + * in the Apache project and the libpqueue library. + * + * https://github.com/vy/libpqueue + * + * These are the original authors: + * + * Copyright 2010 Volkan Yazıcı <volkan.yazici@gmail.com> + * Copyright 2006-2010 The Apache Software Foundation + * + * This file is licensed under the Apache 2.0 license, which + * supposedly makes it compatible with the GPLv2 that libgit2 uses. + * + * Check the Apache license at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * So much licensing trouble for a binary heap. Oh well. + */ + +#include "common.h" +#include "pqueue.h" + +#define left(i) ((i) << 1) +#define right(i) (((i) << 1) + 1) +#define parent(i) ((i) >> 1) + +int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri) +{ + assert(q); + + /* Need to allocate n+1 elements since element 0 isn't used. */ + if ((q->d = malloc((n + 1) * sizeof(void *))) == NULL) + return GIT_ENOMEM; + + q->size = 1; + q->avail = q->step = (n + 1); /* see comment above about n+1 */ + q->cmppri = cmppri; + + return GIT_SUCCESS; +} + + +void git_pqueue_free(git_pqueue *q) +{ + free(q->d); + q->d = NULL; +} + + +size_t git_pqueue_size(git_pqueue *q) +{ + /* queue element 0 exists but doesn't count since it isn't used. */ + return (q->size - 1); +} + + +static void bubble_up(git_pqueue *q, size_t i) +{ + size_t parent_node; + void *moving_node = q->d[i]; + + for (parent_node = parent(i); + ((i > 1) && q->cmppri(q->d[parent_node], moving_node)); + i = parent_node, parent_node = parent(i)) { + q->d[i] = q->d[parent_node]; + } + + q->d[i] = moving_node; +} + + +static size_t maxchild(git_pqueue *q, size_t i) +{ + size_t child_node = left(i); + + if (child_node >= q->size) + return 0; + + if ((child_node + 1) < q->size && + q->cmppri(q->d[child_node], q->d[child_node + 1])) + child_node++; /* use right child instead of left */ + + return child_node; +} + + +static void percolate_down(git_pqueue *q, size_t i) +{ + size_t child_node; + void *moving_node = q->d[i]; + + while ((child_node = maxchild(q, i)) != 0 && + q->cmppri(moving_node, q->d[child_node])) { + q->d[i] = q->d[child_node]; + i = child_node; + } + + q->d[i] = moving_node; +} + + +int git_pqueue_insert(git_pqueue *q, void *d) +{ + void *tmp; + size_t i; + size_t newsize; + + if (!q) return 1; + + /* allocate more memory if necessary */ + if (q->size >= q->avail) { + newsize = q->size + q->step; + if ((tmp = realloc(q->d, sizeof(void *) * newsize)) == NULL) + return GIT_ENOMEM; + + q->d = tmp; + q->avail = newsize; + } + + /* insert item */ + i = q->size++; + q->d[i] = d; + bubble_up(q, i); + + return GIT_SUCCESS; +} + + +void *git_pqueue_pop(git_pqueue *q) +{ + void *head; + + if (!q || q->size == 1) + return NULL; + + head = q->d[1]; + q->d[1] = q->d[--q->size]; + percolate_down(q, 1); + + return head; +} + + +void *git_pqueue_peek(git_pqueue *q) +{ + if (!q || q->size == 1) + return NULL; + return q->d[1]; +} |