mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-09 22:39:38 +00:00
158 lines
3.1 KiB
C
158 lines
3.1 KiB
C
/*
|
||
* 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;
|
||
}
|
||
|
||
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];
|
||
}
|