Commit 86d7e1ca6f54161a9e4d1ebe7a2f8e4802dc9639

Vicent Marti 2011-02-28T12:46:13

Fix searching in git_vector We now store only one sorting callback that does entry comparison. This is used when sorting the entries using a quicksort, and when looking for a specific entry with the new search methods. The following search methods now exist: git_vector_search(vector, entry) git_vector_search2(vector, custom_search_callback, key) git_vector_bsearch(vector, entry) git_vector_bsearch2(vector, custom_search_callback, key) The sorting state of the vector is now stored internally. Signed-off-by: Vicent Marti <tanoku@gmail.com>

diff --git a/src/commit.c b/src/commit.c
index 44e1e94..3eebb92 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -112,7 +112,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int
 
 	/* first parse; the vector hasn't been initialized yet */
 	if (commit->parents.contents == NULL) {
-		git_vector_init(&commit->parents, 4, NULL, NULL);
+		git_vector_init(&commit->parents, 4, NULL);
 	}
 
 	clear_parents(commit);
diff --git a/src/index.c b/src/index.c
index e83d115..46bbafd 100644
--- a/src/index.c
+++ b/src/index.c
@@ -141,10 +141,7 @@ static int index_initialize(git_index **index_out, git_repository *owner, const 
 
 	index->repository = owner;
 
-	git_vector_init(&index->entries, 32, index_cmp, index_srch);
-
-	/* the index is empty; the index is sorted */
-	index->sorted = 1;
+	git_vector_init(&index->entries, 32, index_cmp);
 
 	/* Check if index file is stored on disk already */
 	if (gitfo_exists(index->index_file_path) == 0)
@@ -209,7 +206,6 @@ void git_index_clear(git_index *index)
 
 	git_vector_clear(&index->entries);
 	index->last_modified = 0;
-	index->sorted = 1;
 
 	free_tree(index->tree);
 	index->tree = NULL;
@@ -259,8 +255,7 @@ int git_index_write(git_index *index)
 	struct stat indexst;
 	int error;
 
-	if (!index->sorted)
-		sort_index(index);
+	sort_index(index);
 
 	if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS)
 		return error;
@@ -340,10 +335,7 @@ int git_index_add(git_index *index, const char *rel_path, int stage)
 
 void sort_index(git_index *index)
 {
-	if (index->sorted == 0) {
-		git_vector_sort(&index->entries);
-		index->sorted = 1;
-	}
+	git_vector_sort(&index->entries);
 }
 
 int git_index_insert(git_index *index, const git_index_entry *source_entry)
@@ -388,8 +380,6 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry)
 		if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
 			return GIT_ENOMEM;
 
-		index->sorted = 0;
-
 	/* if a previous entry exists, replace it */
 	} else {
 		git_index_entry **entry_array = (git_index_entry **)index->entries.contents;
@@ -413,7 +403,7 @@ int git_index_remove(git_index *index, int position)
 int git_index_find(git_index *index, const char *path)
 {
 	sort_index(index);
-	return git_vector_search(&index->entries, path);
+	return git_vector_bsearch2(&index->entries, index_srch, path);
 }
 
 static git_index_tree *read_tree_internal(
@@ -678,6 +668,9 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
 
 #undef seek_forward
 
+	/* force sorting in the vector: the entries are
+	 * assured to be sorted on the index */
+	index->entries.sorted = 1;
 	return GIT_SUCCESS;
 }
 
diff --git a/src/index.h b/src/index.h
index 43aadff..e1e752f 100644
--- a/src/index.h
+++ b/src/index.h
@@ -27,9 +27,7 @@ struct git_index {
 	time_t last_modified;
 	git_vector entries;
 
-	unsigned int sorted:1,
-				 on_disk:1;
-
+	unsigned int on_disk:1;
 	git_index_tree *tree;
 };
 
diff --git a/src/odb.c b/src/odb.c
index 4e54c74..2013ac2 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -162,7 +162,7 @@ int git_odb_new(git_odb **out)
 	if (!db)
 		return GIT_ENOMEM;
 
-	if (git_vector_init(&db->backends, 4, backend_sort_cmp, NULL) < 0) {
+	if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
 		free(db);
 		return GIT_ENOMEM;
 	}
diff --git a/src/refs.c b/src/refs.c
index f8da561..542401c 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -751,7 +751,7 @@ static int packed_write(git_repository *repo)
 	assert(repo && repo->references.packfile);
 
 	total_refs = repo->references.packfile->key_count;
-	if ((error = git_vector_init(&packing_list, total_refs, packed_sort, NULL)) < GIT_SUCCESS)
+	if ((error = git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS)
 		return error;
 
 	/* Load all the packfile into a vector */
diff --git a/src/tree.c b/src/tree.c
index 656798c..5ea0622 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -83,10 +83,7 @@ git_tree *git_tree__new(void)
 
 	memset(tree, 0x0, sizeof(struct git_tree));
 
-	if (git_vector_init(&tree->entries, 
-				DEFAULT_TREE_SIZE,
-				entry_sort_cmp,
-				entry_search_cmp) < GIT_SUCCESS) {
+	if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) {
 		free(tree);
 		return NULL;
 	}
@@ -173,7 +170,7 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
 	if (!tree->sorted)
 		sort_entries(tree);
 
-	idx = git_vector_search(&tree->entries, filename);
+	idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename);
 	if (idx == GIT_ENOTFOUND)
 		return NULL;
 
@@ -253,7 +250,7 @@ int git_tree_remove_entry_byname(git_tree *tree, const char *filename)
 	if (!tree->sorted)
 		sort_entries(tree);
 
-	idx = git_vector_search(&tree->entries, filename);
+	idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename);
 	if (idx == GIT_ENOTFOUND)
 		return GIT_ENOTFOUND;
 
diff --git a/src/vector.c b/src/vector.c
index f298804..126aec1 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -50,7 +50,7 @@ void git_vector_free(git_vector *v)
 	free(v->contents);
 }
 
-int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp, git_vector_srch srch)
+int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp)
 {
 	assert(v);
 
@@ -61,9 +61,9 @@ int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp
 
 	v->_alloc_size = initial_size;
 	v->_cmp = cmp;
-	v->_srch = srch;
 	
 	v->length = 0;
+	v->sorted = 1;
 
 	v->contents = git__malloc(v->_alloc_size * sizeof(void *));
 	if (v->contents == NULL)
@@ -82,6 +82,7 @@ int git_vector_insert(git_vector *v, void *element)
 	}
 
 	v->contents[v->length++] = element;
+	v->sorted = 0;
 
 	return GIT_SUCCESS;
 }
@@ -90,22 +91,56 @@ void git_vector_sort(git_vector *v)
 {
 	assert(v);
 
-	if (v->_cmp != NULL)
-		qsort(v->contents, v->length, sizeof(void *), v->_cmp);
+	if (v->sorted || v->_cmp == NULL)
+		return;
+
+	qsort(v->contents, v->length, sizeof(void *), v->_cmp);
+	v->sorted = 1;
 }
 
-int git_vector_search(git_vector *v, const void *key)
+int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *key)
 {
 	void **find;
 
-	if (v->_srch == NULL)
+	assert(v && key && key_lookup);
+
+	git_vector_sort(v);
+
+	find = bsearch(key, v->contents, v->length, sizeof(void *), key_lookup);
+	if (find != NULL)
+		return (int)(find - v->contents);
+
+	return GIT_ENOTFOUND;
+}
+
+int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key)
+{
+	unsigned int i;
+
+	assert(v && key && key_lookup);
+
+	for (i = 0; i < v->length; ++i) {
+		if (key_lookup(key, v->contents[i]) == 0)
+			return i;
+	}
+
+	return GIT_ENOTFOUND;
+}
+
+int git_vector_search(git_vector *v, const void *key)
+{
+	if (v->_cmp == NULL)
 		return GIT_ENOTFOUND;
 
-	find = bsearch(key, v->contents, v->length, sizeof(void *), v->_srch);
-	if (find == NULL)
+	return git_vector_search2(v, v->_cmp, key);
+}
+
+int git_vector_bsearch(git_vector *v, const void *key)
+{
+	if (v->_cmp == NULL)
 		return GIT_ENOTFOUND;
 
-	return (int)(find - v->contents);
+	return git_vector_bsearch2(v, v->_cmp, key);
 }
 
 int git_vector_remove(git_vector *v, unsigned int idx)
@@ -128,6 +163,7 @@ void git_vector_clear(git_vector *v)
 {
 	assert(v);
 	v->length = 0;
+	v->sorted = 1;
 }
 
 
diff --git a/src/vector.h b/src/vector.h
index dfd6047..256452e 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -3,25 +3,26 @@
 
 #include "git2/common.h"
 
-
 typedef int (*git_vector_cmp)(const void *, const void *);
-typedef int (*git_vector_srch)(const void *, const void *);
 
 typedef struct git_vector {
 	unsigned int _alloc_size;
 	git_vector_cmp _cmp;
-	git_vector_srch _srch;
-
 	void **contents;
 	unsigned int length;
+	int sorted;
 } git_vector;
 
-
-int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp, git_vector_srch srch);
+int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp);
 void git_vector_free(git_vector *v);
 void git_vector_clear(git_vector *v);
 
-int git_vector_search(git_vector *v, const void *key);
+int git_vector_search(git_vector *v, const void *entry);
+int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);
+
+int git_vector_bsearch(git_vector *v, const void *entry);
+int git_vector_bsearch2(git_vector *v, git_vector_cmp cmp, const void *key);
+
 void git_vector_sort(git_vector *v);
 
 GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
diff --git a/tests/t00-core.c b/tests/t00-core.c
index 3c1b621..1f6c06a 100644
--- a/tests/t00-core.c
+++ b/tests/t00-core.c
@@ -64,7 +64,7 @@ END_TEST
 BEGIN_TEST(vector0, "initial size of 1 would cause writing past array bounds")
   git_vector x;
   int i;
-  git_vector_init(&x, 1, NULL, NULL);
+  git_vector_init(&x, 1, NULL);
   for (i = 0; i < 10; ++i) {
     git_vector_insert(&x, (void*) 0xabc);
   }
@@ -74,7 +74,7 @@ END_TEST
 BEGIN_TEST(vector1, "don't read past array bounds on remove()")
   git_vector x;
   // make initial capacity exact for our insertions.
-  git_vector_init(&x, 3, NULL, NULL);
+  git_vector_init(&x, 3, NULL);
   git_vector_insert(&x, (void*) 0xabc);
   git_vector_insert(&x, (void*) 0xdef);
   git_vector_insert(&x, (void*) 0x123);
diff --git a/tests/t06-index.c b/tests/t06-index.c
index 0c748cc..19b4da5 100644
--- a/tests/t06-index.c
+++ b/tests/t06-index.c
@@ -55,7 +55,7 @@ BEGIN_TEST(read0, "load an empty index")
 
 	must_be_true(index->on_disk == 0);
 	must_be_true(git_index_entrycount(index) == 0);
-	must_be_true(index->sorted);
+	must_be_true(index->entries.sorted);
 
 	git_index_free(index);
 END_TEST
@@ -72,7 +72,7 @@ BEGIN_TEST(read1, "load a standard index (default test index)")
 
 	must_be_true(index->on_disk);
 	must_be_true(git_index_entrycount(index) == TEST_INDEX_ENTRY_COUNT);
-	must_be_true(index->sorted);
+	must_be_true(index->entries.sorted);
 
 	entries = (git_index_entry **)index->entries.contents;
 
@@ -97,7 +97,7 @@ BEGIN_TEST(read2, "load a standard index (git.git index)")
 
 	must_be_true(index->on_disk);
 	must_be_true(git_index_entrycount(index) == TEST_INDEX2_ENTRY_COUNT);
-	must_be_true(index->sorted);
+	must_be_true(index->entries.sorted);
 	must_be_true(index->tree != NULL);
 
 	git_index_free(index);
@@ -168,7 +168,7 @@ BEGIN_TEST(sort1, "sort the entires in an empty index")
 	must_pass(git_index_open_bare(&index, "fake-index"));
 
 	/* FIXME: this test is slightly dumb */
-	must_be_true(index->sorted);
+	must_be_true(index->entries.sorted);
 
 	git_index_free(index);
 END_TEST