Commit 60ecdf59d3af87125467fbed97b575f783129f70

David Michael Barr 2012-09-10T11:48:21

pack: iterate objects in offset order Compute the ordering on demand and persist until the index is freed.

diff --git a/src/pack.c b/src/pack.c
index e1fa085..9346ace 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -54,6 +54,10 @@ static int packfile_error(const char *message)
 
 static void pack_index_free(struct git_pack_file *p)
 {
+	if (p->oids) {
+		git__free(p->oids);
+		p->oids = NULL;
+	}
 	if (p->index_map.data) {
 		git_futils_mmap_free(&p->index_map);
 		p->index_map.data = NULL;
@@ -686,13 +690,16 @@ static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_
 	}
 }
 
+static int git__memcmp4(const void *a, const void *b) {
+	return memcmp(a, b, 4);
+}
+
 int git_pack_foreach_entry(
 	struct git_pack_file *p,
 	int (*cb)(git_oid *oid, void *data),
 	void *data)
 {
 	const unsigned char *index = p->index_map.data, *current;
-	unsigned stride;
 	uint32_t i;
 
 	if (index == NULL) {
@@ -712,21 +719,38 @@ int git_pack_foreach_entry(
 
 	index += 4 * 256;
 
-	if (p->index_version > 1) {
-		stride = 20;
-	} else {
-		stride = 24;
-		index += 4;
-	}
+	if (p->oids == NULL) {
+		git_vector offsets, oids;
+		int error;
 
-	current = index;
-	for (i = 0; i < p->num_objects; i++) {
-		if (cb((git_oid *)current, data))
-			return GIT_EUSER;
+		if ((error = git_vector_init(&oids, p->num_objects, NULL)))
+			return error;
+
+		if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4)))
+			return error;
 
-		current += stride;
+		if (p->index_version > 1) {
+			const unsigned char *off = index + 24 * p->num_objects;
+			for (i = 0; i < p->num_objects; i++)
+				git_vector_insert(&offsets, (void*)&off[4 * i]);
+			git_vector_sort(&offsets);
+			git_vector_foreach(&offsets, i, current)
+				git_vector_insert(&oids, (void*)&index[5 * (current - off)]);
+		} else {
+			for (i = 0; i < p->num_objects; i++)
+				git_vector_insert(&offsets, (void*)&index[24 * i]);
+			git_vector_sort(&offsets);
+			git_vector_foreach(&offsets, i, current)
+				git_vector_insert(&oids, (void*)&current[4]);
+		}
+		git_vector_free(&offsets);
+		p->oids = (git_oid **)oids.contents;
 	}
 
+	for (i = 0; i < p->num_objects; i++)
+		if (cb(p->oids[i], data))
+			return GIT_EUSER;
+
 	return 0;
 }
 
diff --git a/src/pack.h b/src/pack.h
index 1785456..af87b7c 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -64,6 +64,7 @@ struct git_pack_file {
 	unsigned pack_local:1, pack_keep:1, has_cache:1;
 	git_oid sha1;
 	git_vector cache;
+	git_oid **oids;
 
 	/* something like ".git/objects/pack/xxxxx.pack" */
 	char pack_name[GIT_FLEX_ARRAY]; /* more */