mirror of
https://git.proxmox.com/git/libgit2
synced 2025-12-31 23:59:11 +00:00
Convert pqueue to just be a git_vector
This updates the git_pqueue to simply be a set of specialized init/insert/pop functions on a git_vector. To preserve the pqueue feature of having a fixed size heap, I converted the "sorted" field in git_vectors to a more general "flags" field so that pqueue could mix in it's own flag. This had a bunch of ramifications because a number of places were directly looking at the vector "sorted" field - I added a couple new git_vector helpers (is_sorted, set_sorted) so the specific representation of this information could be abstracted.
This commit is contained in:
parent
af4bc6615d
commit
882c774271
@ -1564,7 +1564,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
|
||||
}
|
||||
|
||||
/* entries are guaranteed to be sorted on-disk */
|
||||
index->reuc.sorted = 1;
|
||||
git_vector_set_sorted(&index->reuc, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1610,7 +1610,7 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
|
||||
#undef read_conflict_name
|
||||
|
||||
/* entries are guaranteed to be sorted on-disk */
|
||||
index->names.sorted = 1;
|
||||
git_vector_set_sorted(&index->names, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1812,7 +1812,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
|
||||
#undef seek_forward
|
||||
|
||||
/* Entries are stored case-sensitively on disk. */
|
||||
index->entries.sorted = !index->ignore_case;
|
||||
git_vector_set_sorted(&index->entries, index->ignore_case);
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
return 0;
|
||||
|
||||
57
src/pqueue.c
57
src/pqueue.c
@ -15,25 +15,29 @@
|
||||
int git_pqueue_init(
|
||||
git_pqueue *pq,
|
||||
uint32_t flags,
|
||||
size_t est_size,
|
||||
size_t init_size,
|
||||
git_vector_cmp cmp)
|
||||
{
|
||||
pq->flags = flags;
|
||||
pq->initial_size = est_size;
|
||||
return git_vector_init(&pq->values, est_size, cmp);
|
||||
}
|
||||
int error = git_vector_init(pq, init_size, cmp);
|
||||
|
||||
void git_pqueue_free(git_pqueue *pq)
|
||||
{
|
||||
git_vector_free(&pq->values);
|
||||
if (!error) {
|
||||
/* mix in our flags */
|
||||
pq->flags |= flags;
|
||||
|
||||
/* if fixed size heap, pretend vector is exactly init_size elements */
|
||||
if ((flags & GIT_PQUEUE_FIXED_SIZE) && init_size > 0)
|
||||
pq->_alloc_size = init_size;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void pqueue_up(git_pqueue *pq, size_t el)
|
||||
{
|
||||
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);
|
||||
while (el > 0 && git_vector_cmp_elements(pq, parent_el, el) > 0) {
|
||||
git_vector_swap_elements(pq, el, parent_el);
|
||||
|
||||
el = parent_el;
|
||||
parent_el = PQUEUE_PARENT_OF(el);
|
||||
@ -42,19 +46,19 @@ static void pqueue_up(git_pqueue *pq, size_t el)
|
||||
|
||||
static void pqueue_down(git_pqueue *pq, size_t el)
|
||||
{
|
||||
size_t last = git_vector_length(&pq->values);
|
||||
size_t last = git_pqueue_size(pq);
|
||||
|
||||
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)
|
||||
if (rkid < last && git_vector_cmp_elements(pq, kid, rkid) > 0)
|
||||
kid = rkid;
|
||||
|
||||
if (git_vector_cmp_elements(&pq->values, el, kid) < 0)
|
||||
if (git_vector_cmp_elements(pq, el, kid) < 0)
|
||||
break;
|
||||
|
||||
git_vector_swap_elements(&pq->values, el, kid);
|
||||
git_vector_swap_elements(pq, el, kid);
|
||||
el = kid;
|
||||
}
|
||||
}
|
||||
@ -65,32 +69,33 @@ int git_pqueue_insert(git_pqueue *pq, void *item)
|
||||
|
||||
/* 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)
|
||||
pq->length >= pq->_alloc_size)
|
||||
{
|
||||
/* skip item if below min item in heap */
|
||||
if (pq->values._cmp(item, git_vector_get(&pq->values, 0)) <= 0)
|
||||
/* skip this item if below min item in heap */
|
||||
if (pq->_cmp(item, git_vector_get(pq, 0)) <= 0)
|
||||
return 0;
|
||||
/* otherwise remove the min item before inserting new */
|
||||
(void)git_pqueue_pop(pq);
|
||||
}
|
||||
|
||||
error = git_vector_insert(&pq->values, item);
|
||||
|
||||
if (!error)
|
||||
pqueue_up(pq, pq->values.length - 1);
|
||||
if (!(error = git_vector_insert(pq, item)))
|
||||
pqueue_up(pq, pq->length - 1);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void *git_pqueue_pop(git_pqueue *pq)
|
||||
{
|
||||
void *rval = git_vector_get(&pq->values, 0);
|
||||
void *rval = git_pqueue_get(pq, 0);
|
||||
|
||||
if (git_vector_length(&pq->values) > 1) {
|
||||
pq->values.contents[0] = git_vector_last(&pq->values);
|
||||
git_vector_pop(&pq->values);
|
||||
if (git_pqueue_size(pq) > 1) {
|
||||
/* move last item to top of heap, shrink, and push item down */
|
||||
pq->contents[0] = git_vector_last(pq);
|
||||
git_vector_pop(pq);
|
||||
pqueue_down(pq, 0);
|
||||
} else {
|
||||
git_vector_pop(&pq->values);
|
||||
/* all we need to do is shrink the heap in this case */
|
||||
git_vector_pop(pq);
|
||||
}
|
||||
|
||||
return rval;
|
||||
|
||||
38
src/pqueue.h
38
src/pqueue.h
@ -9,15 +9,11 @@
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
typedef struct {
|
||||
git_vector values;
|
||||
size_t initial_size;
|
||||
uint32_t flags;
|
||||
} git_pqueue;
|
||||
typedef git_vector git_pqueue;
|
||||
|
||||
enum {
|
||||
GIT_PQUEUE_DEFAULT = 0,
|
||||
GIT_PQUEUE_FIXED_SIZE = (1 << 0), /* don't grow heap, keep highest */
|
||||
/* flag meaning: don't grow heap, keep highest values only */
|
||||
GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -25,36 +21,20 @@ enum {
|
||||
*
|
||||
* @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 init_size The initial queue size
|
||||
* @param cmp The entry priority comparison function
|
||||
* @return 0 on success, <0 on error
|
||||
*/
|
||||
extern int git_pqueue_init(
|
||||
git_pqueue *pq,
|
||||
uint32_t flags,
|
||||
size_t est_size,
|
||||
size_t init_size,
|
||||
git_vector_cmp cmp);
|
||||
|
||||
/**
|
||||
* Free the queue memory
|
||||
*/
|
||||
extern void git_pqueue_free(git_pqueue *pq);
|
||||
|
||||
/**
|
||||
* Get the number of items in the queue
|
||||
*/
|
||||
GIT_INLINE(size_t) git_pqueue_size(const git_pqueue *pq)
|
||||
{
|
||||
return git_vector_length(&pq->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item in the queue
|
||||
*/
|
||||
GIT_INLINE(void *) git_pqueue_get(const git_pqueue *pq, size_t pos)
|
||||
{
|
||||
return git_vector_get(&pq->values, pos);
|
||||
}
|
||||
#define git_pqueue_free git_vector_free
|
||||
#define git_pqueue_clear git_vector_clear
|
||||
#define git_pqueue_size git_vector_length
|
||||
#define git_pqueue_get git_vector_get
|
||||
|
||||
/**
|
||||
* Insert a new item into the queue
|
||||
|
||||
@ -543,7 +543,7 @@ void git_revwalk_reset(git_revwalk *walk)
|
||||
commit->uninteresting = 0;
|
||||
});
|
||||
|
||||
git_pqueue_free(&walk->iterator_time);
|
||||
git_pqueue_clear(&walk->iterator_time);
|
||||
git_commit_list_free(&walk->iterator_topo);
|
||||
git_commit_list_free(&walk->iterator_rand);
|
||||
git_commit_list_free(&walk->iterator_reverse);
|
||||
|
||||
@ -321,7 +321,7 @@ size_t git_sortedcache_entrycount(const git_sortedcache *sc)
|
||||
void *git_sortedcache_entry(git_sortedcache *sc, size_t pos)
|
||||
{
|
||||
/* make sure the items are sorted so this gets the correct item */
|
||||
if (!sc->items.sorted)
|
||||
if (!git_vector_is_sorted(&sc->items))
|
||||
git_vector_sort(&sc->items);
|
||||
|
||||
return git_vector_get(&sc->items, pos);
|
||||
|
||||
@ -283,7 +283,8 @@ static const git_tree_entry *entry_fromname(
|
||||
{
|
||||
size_t idx;
|
||||
|
||||
assert(tree->entries.sorted); /* be safe when we cast away constness */
|
||||
/* be safe when we cast away constness - i.e. don't trigger a sort */
|
||||
assert(git_vector_is_sorted(&tree->entries));
|
||||
|
||||
if (tree_key_search(&idx, (git_vector *)&tree->entries, name, name_len) < 0)
|
||||
return NULL;
|
||||
@ -333,7 +334,8 @@ int git_tree__prefix_position(const git_tree *tree, const char *path)
|
||||
ksearch.filename = path;
|
||||
ksearch.filename_len = strlen(path);
|
||||
|
||||
assert(tree->entries.sorted); /* be safe when we cast away constness */
|
||||
/* be safe when we cast away constness - i.e. don't trigger a sort */
|
||||
assert(git_vector_is_sorted(&tree->entries));
|
||||
|
||||
/* Find tree entry with appropriate prefix */
|
||||
git_vector_bsearch2(
|
||||
|
||||
17
src/vector.c
17
src/vector.c
@ -56,7 +56,9 @@ int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
|
||||
v->_alloc_size = src->length;
|
||||
v->_cmp = cmp;
|
||||
v->length = src->length;
|
||||
v->sorted = src->sorted && cmp == src->_cmp;
|
||||
v->flags = src->flags;
|
||||
if (cmp != src->_cmp)
|
||||
git_vector_set_sorted(v, 0);
|
||||
v->contents = git__malloc(bytes);
|
||||
GITERR_CHECK_ALLOC(v->contents);
|
||||
|
||||
@ -97,7 +99,7 @@ int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
|
||||
v->_alloc_size = 0;
|
||||
v->_cmp = cmp;
|
||||
v->length = 0;
|
||||
v->sorted = 1;
|
||||
v->flags = GIT_VECTOR_SORTED;
|
||||
v->contents = NULL;
|
||||
|
||||
return resize_vector(v, max(initial_size, MIN_ALLOCSIZE));
|
||||
@ -128,7 +130,8 @@ int git_vector_insert(git_vector *v, void *element)
|
||||
return -1;
|
||||
|
||||
v->contents[v->length++] = element;
|
||||
v->sorted = 0;
|
||||
|
||||
git_vector_set_sorted(v, v->length <= 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -141,7 +144,7 @@ int git_vector_insert_sorted(
|
||||
|
||||
assert(v && v->_cmp);
|
||||
|
||||
if (!v->sorted)
|
||||
if (!git_vector_is_sorted(v))
|
||||
git_vector_sort(v);
|
||||
|
||||
if (v->length >= v->_alloc_size &&
|
||||
@ -171,11 +174,11 @@ void git_vector_sort(git_vector *v)
|
||||
{
|
||||
assert(v);
|
||||
|
||||
if (v->sorted || !v->_cmp)
|
||||
if (git_vector_is_sorted(v) || !v->_cmp)
|
||||
return;
|
||||
|
||||
git__tsort(v->contents, v->length, v->_cmp);
|
||||
v->sorted = 1;
|
||||
git_vector_set_sorted(v, 1);
|
||||
}
|
||||
|
||||
int git_vector_bsearch2(
|
||||
@ -291,7 +294,7 @@ void git_vector_clear(git_vector *v)
|
||||
{
|
||||
assert(v);
|
||||
v->length = 0;
|
||||
v->sorted = 1;
|
||||
git_vector_set_sorted(v, 1);
|
||||
}
|
||||
|
||||
void git_vector_swap(git_vector *a, git_vector *b)
|
||||
|
||||
17
src/vector.h
17
src/vector.h
@ -11,12 +11,17 @@
|
||||
|
||||
typedef int (*git_vector_cmp)(const void *, const void *);
|
||||
|
||||
enum {
|
||||
GIT_VECTOR_SORTED = (1u << 0),
|
||||
GIT_VECTOR_FLAG_MAX = (1u << 1),
|
||||
};
|
||||
|
||||
typedef struct git_vector {
|
||||
size_t _alloc_size;
|
||||
git_vector_cmp _cmp;
|
||||
void **contents;
|
||||
size_t length;
|
||||
int sorted;
|
||||
uint32_t flags;
|
||||
} git_vector;
|
||||
|
||||
#define GIT_VECTOR_INIT {0}
|
||||
@ -86,12 +91,20 @@ void git_vector_remove_matching(
|
||||
int git_vector_resize_to(git_vector *v, size_t new_length);
|
||||
int git_vector_set(void **old, git_vector *v, size_t position, void *value);
|
||||
|
||||
/** Check if vector is sorted */
|
||||
#define git_vector_is_sorted(V) (((V)->flags & GIT_VECTOR_SORTED) != 0)
|
||||
|
||||
/** Directly set sorted state of vector */
|
||||
#define git_vector_set_sorted(V,S) do { \
|
||||
(V)->flags = (S) ? ((V)->flags | GIT_VECTOR_SORTED) : \
|
||||
((V)->flags & ~GIT_VECTOR_SORTED); } while (0)
|
||||
|
||||
/** Set the comparison function used for sorting the vector */
|
||||
GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp)
|
||||
{
|
||||
if (cmp != v->_cmp) {
|
||||
v->_cmp = cmp;
|
||||
v->sorted = 0;
|
||||
git_vector_set_sorted(v, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ void test_index_tests__empty_index(void)
|
||||
cl_assert(index->on_disk == 0);
|
||||
|
||||
cl_assert(git_index_entrycount(index) == 0);
|
||||
cl_assert(index->entries.sorted);
|
||||
cl_assert(git_vector_is_sorted(&index->entries));
|
||||
|
||||
git_index_free(index);
|
||||
}
|
||||
@ -95,7 +95,7 @@ void test_index_tests__default_test_index(void)
|
||||
cl_assert(index->on_disk);
|
||||
|
||||
cl_assert(git_index_entrycount(index) == index_entry_count);
|
||||
cl_assert(index->entries.sorted);
|
||||
cl_assert(git_vector_is_sorted(&index->entries));
|
||||
|
||||
entries = (git_index_entry **)index->entries.contents;
|
||||
|
||||
@ -118,7 +118,7 @@ void test_index_tests__gitgit_index(void)
|
||||
cl_assert(index->on_disk);
|
||||
|
||||
cl_assert(git_index_entrycount(index) == index_entry_count_2);
|
||||
cl_assert(index->entries.sorted);
|
||||
cl_assert(git_vector_is_sorted(&index->entries));
|
||||
cl_assert(index->tree != NULL);
|
||||
|
||||
git_index_free(index);
|
||||
@ -195,7 +195,7 @@ void test_index_tests__sort1(void)
|
||||
cl_git_pass(git_index_open(&index, "fake-index"));
|
||||
|
||||
/* FIXME: this test is slightly dumb */
|
||||
cl_assert(index->entries.sorted);
|
||||
cl_assert(git_vector_is_sorted(&index->entries));
|
||||
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user