/* * 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ı * 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; } void git_pqueue_clear(git_pqueue *q) { q->size = 1; } 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]; }