summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commit_list.c6
-rw-r--r--src/commit_list.h2
-rw-r--r--src/graph.c35
-rw-r--r--src/merge.c15
-rw-r--r--src/pqueue.c184
-rw-r--r--src/pqueue.h111
-rw-r--r--src/revwalk.c5
-rw-r--r--src/vector.h10
8 files changed, 142 insertions, 226 deletions
diff --git a/src/commit_list.c b/src/commit_list.c
index 64416e54d..9db3f5633 100644
--- a/src/commit_list.c
+++ b/src/commit_list.c
@@ -11,10 +11,10 @@
#include "pool.h"
#include "odb.h"
-int git_commit_list_time_cmp(void *a, void *b)
+int git_commit_list_time_cmp(const void *a, const void *b)
{
- git_commit_list_node *commit_a = (git_commit_list_node *)a;
- git_commit_list_node *commit_b = (git_commit_list_node *)b;
+ const git_commit_list_node *commit_a = a;
+ const git_commit_list_node *commit_b = b;
return (commit_a->time < commit_b->time);
}
diff --git a/src/commit_list.h b/src/commit_list.h
index d2f54b3ca..490d841be 100644
--- a/src/commit_list.h
+++ b/src/commit_list.h
@@ -39,7 +39,7 @@ typedef struct git_commit_list {
} git_commit_list;
git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
-int git_commit_list_time_cmp(void *a, void *b);
+int git_commit_list_time_cmp(const void *a, const void *b);
void git_commit_list_free(git_commit_list **list_p);
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p);
diff --git a/src/graph.c b/src/graph.c
index f39af5ed5..96fda7add 100644
--- a/src/graph.c
+++ b/src/graph.c
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
@@ -13,9 +12,9 @@
static int interesting(git_pqueue *list, git_commit_list *roots)
{
unsigned int i;
- /* element 0 isn't used - we need to start at 1 */
- for (i = 1; i < list->size; i++) {
- git_commit_list_node *commit = list->d[i];
+
+ for (i = 0; i < git_pqueue_size(list); i++) {
+ git_commit_list_node *commit = git_pqueue_get(list, i);
if ((commit->flags & STALE) == 0)
return 1;
}
@@ -42,7 +41,7 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
return 0;
}
- if (git_pqueue_init(&list, 2, git_commit_list_time_cmp) < 0)
+ if (git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp) < 0)
return -1;
if (git_commit_list_parse(walk, one) < 0)
@@ -59,10 +58,9 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
/* as long as there are non-STALE commits */
while (interesting(&list, roots)) {
- git_commit_list_node *commit;
+ git_commit_list_node *commit = git_pqueue_pop(&list);
int flags;
- commit = git_pqueue_pop(&list);
if (commit == NULL)
break;
@@ -110,16 +108,16 @@ static int ahead_behind(git_commit_list_node *one, git_commit_list_node *two,
{
git_commit_list_node *commit;
git_pqueue pq;
- int i;
+ int error = 0, i;
*ahead = 0;
*behind = 0;
- if (git_pqueue_init(&pq, 2, git_commit_list_time_cmp) < 0)
+ if (git_pqueue_init(&pq, 0, 2, git_commit_list_time_cmp) < 0)
return -1;
- if (git_pqueue_insert(&pq, one) < 0)
- goto on_error;
- if (git_pqueue_insert(&pq, two) < 0)
- goto on_error;
+
+ if ((error = git_pqueue_insert(&pq, one)) < 0 ||
+ (error = git_pqueue_insert(&pq, two)) < 0)
+ goto done;
while ((commit = git_pqueue_pop(&pq)) != NULL) {
if (commit->flags & RESULT ||
@@ -132,18 +130,15 @@ static int ahead_behind(git_commit_list_node *one, git_commit_list_node *two,
for (i = 0; i < commit->out_degree; i++) {
git_commit_list_node *p = commit->parents[i];
- if (git_pqueue_insert(&pq, p) < 0)
- return -1;
+ if ((error = git_pqueue_insert(&pq, p)) < 0)
+ goto done;
}
commit->flags |= RESULT;
}
+done:
git_pqueue_free(&pq);
- return 0;
-
-on_error:
- git_pqueue_free(&pq);
- return -1;
+ return error;
}
int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo,
diff --git a/src/merge.c b/src/merge.c
index 20cfc0e23..d004554cf 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -161,10 +161,10 @@ on_error:
static int interesting(git_pqueue *list)
{
- unsigned int i;
- /* element 0 isn't used - we need to start at 1 */
- for (i = 1; i < list->size; i++) {
- git_commit_list_node *commit = list->d[i];
+ size_t i;
+
+ for (i = 0; i < git_pqueue_size(list); i++) {
+ git_commit_list_node *commit = git_pqueue_get(list, i);
if ((commit->flags & STALE) == 0)
return 1;
}
@@ -186,7 +186,7 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
return git_commit_list_insert(one, out) ? 0 : -1;
}
- if (git_pqueue_init(&list, twos->length * 2, git_commit_list_time_cmp) < 0)
+ if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
return -1;
if (git_commit_list_parse(walk, one) < 0)
@@ -205,10 +205,11 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
/* as long as there are non-STALE commits */
while (interesting(&list)) {
- git_commit_list_node *commit;
+ git_commit_list_node *commit = git_pqueue_pop(&list);
int flags;
- commit = git_pqueue_pop(&list);
+ if (commit == NULL)
+ break;
flags = commit->flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) {
diff --git a/src/pqueue.c b/src/pqueue.c
index 7819ed41e..ddbad7a54 100644
--- a/src/pqueue.c
+++ b/src/pqueue.c
@@ -3,161 +3,95 @@
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
- *
- * This file is based on a modified version of the priority queue found
- * in the Apache project and libpqueue library.
- *
- * https://github.com/vy/libpqueue
- *
- * Original file notice:
- *
- * Copyright 2010 Volkan Yazici <volkan.yazici@gmail.com>
- * Copyright 2006-2010 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
*/
-#include "common.h"
#include "pqueue.h"
+#include "util.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. */
- q->d = git__malloc((n + 1) * sizeof(void *));
- GITERR_CHECK_ALLOC(q->d);
-
- q->size = 1;
- q->avail = q->step = (n + 1); /* see comment above about n+1 */
- q->cmppri = cmppri;
-
- return 0;
-}
-
+#define PQUEUE_LCHILD_OF(I) (((I)<<1)+1)
+#define PQUEUE_RCHILD_OF(I) (((I)<<1)+2)
+#define PQUEUE_PARENT_OF(I) (((I)-1)>>1)
-void git_pqueue_free(git_pqueue *q)
+int git_pqueue_init(
+ git_pqueue *pq,
+ uint32_t flags,
+ size_t est_size,
+ git_vector_cmp cmp)
{
- git__free(q->d);
- q->d = NULL;
+ pq->flags = flags;
+ pq->initial_size = est_size;
+ return git_vector_init(&pq->values, est_size, cmp);
}
-void git_pqueue_clear(git_pqueue *q)
+void git_pqueue_free(git_pqueue *pq)
{
- q->size = 1;
+ git_vector_free(&pq->values);
}
-size_t git_pqueue_size(git_pqueue *q)
+static void pqueue_up(git_pqueue *pq, size_t el)
{
- /* queue element 0 exists but doesn't count since it isn't used. */
- return (q->size - 1);
-}
+ size_t parent_el = PQUEUE_PARENT_OF(el);
+ while (el > 0 && git_vector_cmp_elements(&pq->values, parent_el, el) > 0) {
+ git_vector_swap_elements(&pq->values, el, parent_el);
-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];
+ el = parent_el;
+ parent_el = PQUEUE_PARENT_OF(el);
}
-
- q->d[i] = moving_node;
}
-
-static size_t maxchild(git_pqueue *q, size_t i)
+static void pqueue_down(git_pqueue *pq, size_t el)
{
- size_t child_node = left(i);
+ size_t last = git_vector_length(&pq->values);
- if (child_node >= q->size)
- return 0;
+ while (1) {
+ size_t kid = PQUEUE_LCHILD_OF(el), rkid = PQUEUE_RCHILD_OF(el);
+ if (kid >= last)
+ break;
+ if (rkid < last && git_vector_cmp_elements(&pq->values, kid, rkid) > 0)
+ kid = rkid;
- 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;
-}
+ if (git_vector_cmp_elements(&pq->values, el, kid) < 0)
+ break;
-
-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;
+ git_vector_swap_elements(&pq->values, el, kid);
+ el = kid;
}
-
- q->d[i] = moving_node;
}
-
-int git_pqueue_insert(git_pqueue *q, void *d)
+int git_pqueue_insert(git_pqueue *pq, void *item)
{
- 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;
- tmp = git__realloc(q->d, sizeof(void *) * newsize);
- GITERR_CHECK_ALLOC(tmp);
-
- q->d = tmp;
- q->avail = newsize;
+ int error = 0;
+
+ /* if heap is full, pop the top element if new one should replace it */
+ if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 &&
+ pq->values.length >= pq->initial_size)
+ {
+ /* skip item if below min item in heap */
+ if (pq->values._cmp(item, git_vector_get(&pq->values, 0)) <= 0)
+ return 0;
+ (void)git_pqueue_pop(pq);
}
- /* insert item */
- i = q->size++;
- q->d[i] = d;
- bubble_up(q, i);
+ error = git_vector_insert(&pq->values, item);
- return 0;
-}
+ if (!error)
+ pqueue_up(pq, pq->values.length - 1);
-
-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;
+ return error;
}
-
-void *git_pqueue_peek(git_pqueue *q)
+void *git_pqueue_pop(git_pqueue *pq)
{
- if (!q || q->size == 1)
- return NULL;
- return q->d[1];
+ void *rval = git_vector_get(&pq->values, 0);
+
+ if (git_vector_length(&pq->values) > 1) {
+ pq->values.contents[0] = git_vector_last(&pq->values);
+ git_vector_pop(&pq->values);
+ pqueue_down(pq, 0);
+ } else {
+ git_vector_pop(&pq->values);
+ }
+
+ return rval;
}
diff --git a/src/pqueue.h b/src/pqueue.h
index 9061f8279..3c977e178 100644
--- a/src/pqueue.h
+++ b/src/pqueue.h
@@ -3,99 +3,74 @@
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
- *
- * This file is based on a modified version of the priority queue found
- * in the Apache project and libpqueue library.
- *
- * https://github.com/vy/libpqueue
- *
- * Original file notice:
- *
- * Copyright 2010 Volkan Yazici <volkan.yazici@gmail.com>
- * Copyright 2006-2010 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
*/
-
#ifndef INCLUDE_pqueue_h__
#define INCLUDE_pqueue_h__
-/** callback functions to get/set/compare the priority of an element */
-typedef int (*git_pqueue_cmp)(void *a, void *b);
+#include "vector.h"
-/** the priority queue handle */
typedef struct {
- size_t size, avail, step;
- git_pqueue_cmp cmppri;
- void **d;
+ git_vector values;
+ size_t initial_size;
+ uint32_t flags;
} git_pqueue;
+enum {
+ GIT_PQUEUE_DEFAULT = 0,
+ GIT_PQUEUE_FIXED_SIZE = (1 << 0), /* don't grow heap, keep highest */
+};
/**
- * initialize the queue
+ * Initialize priority queue
*
- * @param n the initial estimate of the number of queue items for which memory
- * should be preallocated
- * @param cmppri the callback function to compare two nodes of the queue
- *
- * @return the handle or NULL for insufficent memory
+ * @param pq The priority queue struct to initialize
+ * @param flags Flags (see above) to control queue behavior
+ * @param est_size The estimated/initial queue size
+ * @param cmp The entry priority comparison function
+ * @return 0 on success, <0 on error
*/
-int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri);
-
+extern int git_pqueue_init(
+ git_pqueue *pq,
+ uint32_t flags,
+ size_t est_size,
+ git_vector_cmp cmp);
/**
- * free all memory used by the queue
- * @param q the queue
+ * Free the queue memory
*/
-void git_pqueue_free(git_pqueue *q);
+extern void git_pqueue_free(git_pqueue *pq);
/**
- * clear all the elements in the queue
- * @param q the queue
+ * Get the number of items in the queue
*/
-void git_pqueue_clear(git_pqueue *q);
+GIT_INLINE(size_t) git_pqueue_size(const git_pqueue *pq)
+{
+ return git_vector_length(&pq->values);
+}
/**
- * return the size of the queue.
- * @param q the queue
+ * Get an item in the queue
*/
-size_t git_pqueue_size(git_pqueue *q);
-
+GIT_INLINE(void *) git_pqueue_get(const git_pqueue *pq, size_t pos)
+{
+ return git_vector_get(&pq->values, pos);
+}
/**
- * insert an item into the queue.
- * @param q the queue
- * @param d the item
- * @return 0 on success
- */
-int git_pqueue_insert(git_pqueue *q, void *d);
-
-
-/**
- * pop the highest-ranking item from the queue.
- * @param q the queue
- * @return NULL on error, otherwise the entry
+ * Insert a new item into the queue
+ *
+ * @param pq The priority queue
+ * @param item Pointer to the item data
+ * @return 0 on success, <0 on failure
*/
-void *git_pqueue_pop(git_pqueue *q);
-
+extern int git_pqueue_insert(git_pqueue *pq, void *item);
/**
- * access highest-ranking item without removing it.
- * @param q the queue
- * @return NULL on error, otherwise the entry
+ * Remove the top item in the priority queue
+ *
+ * @param pq The priority queue
+ * @return item from heap on success, NULL if queue is empty
*/
-void *git_pqueue_peek(git_pqueue *q);
-
-#endif /* PQUEUE_H */
-/** @} */
+extern void *git_pqueue_pop(git_pqueue *pq);
+#endif
diff --git a/src/revwalk.c b/src/revwalk.c
index c0a053211..d6dc10652 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -439,7 +439,8 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
walk->commits = git_oidmap_alloc();
GITERR_CHECK_ALLOC(walk->commits);
- if (git_pqueue_init(&walk->iterator_time, 8, git_commit_list_time_cmp) < 0 ||
+ if (git_pqueue_init(
+ &walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0 ||
git_vector_init(&walk->twos, 4, NULL) < 0 ||
git_pool_init(&walk->commit_pool, 1,
git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0)
@@ -542,7 +543,7 @@ void git_revwalk_reset(git_revwalk *walk)
commit->uninteresting = 0;
});
- git_pqueue_clear(&walk->iterator_time);
+ git_pqueue_free(&walk->iterator_time);
git_commit_list_free(&walk->iterator_topo);
git_commit_list_free(&walk->iterator_rand);
git_commit_list_free(&walk->iterator_reverse);
diff --git a/src/vector.h b/src/vector.h
index d318463c6..e8a967813 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -95,4 +95,14 @@ GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp)
}
}
+/** Swap two elements */
+#define git_vector_swap_elements(V, P1, P2) do { \
+ void *__t = (V)->contents[P1]; \
+ (V)->contents[P1] = (V)->contents[P2]; \
+ (V)->contents[P2] = __t; } while (0)
+
+/** Compare two elements */
+#define git_vector_cmp_elements(V, P1, P2) \
+ (V)->_cmp(git_vector_get(V,P1), git_vector_get(V,P2))
+
#endif