Subject: [PATCH 5/9] pv4_tree_desc: allow decode_entries to return v4 trees, one at a time

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 9 Oct 2013 21:46:12 +0700

When PV4_TREE_CANONICAL is passed, decode_entries() generates <count>
tree entries in canonical format. When this flag is not passed _and_
count is 1, decode_entries fills struct name_entry and saves
sha1_index.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 packv4-parse.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
 packv4-parse.h | 10 ++++++++++
 2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/packv4-parse.c b/packv4-parse.c
index f5c486e..f222456 100644
--- a/packv4-parse.c
+++ b/packv4-parse.c
@@ -365,6 +365,12 @@ static int copy_canonical_tree_entries(struct pv4_tree_desc *v4, off_t offset,
 	while (start--)
 		update_tree_entry(desc);
 
+	if (!(v4->flags & PV4_TREE_CANONICAL)) {
+		v4->sha1_index = 0;
+		v4->pathlen = tree_entry_len(&desc->entry);
+		return 0;
+	}
+
 	from = desc->buffer;
 	while (count--)
 		update_tree_entry(desc);
@@ -462,6 +468,33 @@ static int generate_tree_entry(struct pv4_tree_desc *desc,
 	return 0;
 }
 
+static int get_tree_entry_v4(struct pv4_tree_desc *desc,
+			     const unsigned char **bufp,
+			     int what)
+{
+	const unsigned char *path;
+
+	path = get_pathref(desc->p, what >> 1, &desc->pathlen);
+	if (!path)
+		return -1;
+	desc->v2.entry.mode = (path[0] << 8) | path[1];
+	desc->v2.entry.path = (const char *)path + 2;
+
+	if (**bufp) {
+		desc->sha1_index = decode_varint(bufp);
+		if (desc->sha1_index < 1 ||
+		    desc->sha1_index - 1 > desc->p->num_objects)
+			return error("bad index in get_sha1ref");
+		desc->v2.entry.sha1 = desc->p->sha1_table + (desc->sha1_index - 1) * 20;
+	} else {
+		desc->sha1_index = 0;
+		desc->v2.entry.sha1 = *bufp + 1;
+		*bufp += 21;
+	}
+
+	return 0;
+}
+
 static int decode_entries(struct pv4_tree_desc *desc, off_t obj_offset,
 			  unsigned int start, unsigned int count)
 {
@@ -561,8 +594,14 @@ static int decode_entries(struct pv4_tree_desc *desc, off_t obj_offset,
 			/*
 			 * This is an actual tree entry to recreate.
 			 */
-			if (generate_tree_entry(desc, &scp, what))
-				return -1;
+			if (desc->flags & PV4_TREE_CANONICAL) {
+				if (generate_tree_entry(desc, &scp, what))
+					return -1;
+			} else if (count == 1) {
+				if (get_tree_entry_v4(desc, &scp, what))
+					return -1;
+			} else
+				die("generating multiple v4 entries is not supported");
 			count--;
 			curpos++;
 		} else if (what & 1) {
@@ -668,6 +707,7 @@ void *pv4_get_tree(struct packed_git *p, struct pack_window **w_curs,
 	int ret;
 
 	memset(&desc, 0, sizeof(desc));
+	desc.flags = PV4_TREE_CANONICAL;
 	desc.p = p;
 	desc.w_curs = *w_curs;
 	strbuf_init(&desc.buf, size);
diff --git a/packv4-parse.h b/packv4-parse.h
index 04b9a59..fe0ea38 100644
--- a/packv4-parse.h
+++ b/packv4-parse.h
@@ -24,10 +24,20 @@ void *pv4_get_commit(struct packed_git *p, struct pack_window **w_curs,
 void *pv4_get_tree(struct packed_git *p, struct pack_window **w_curs,
 		   off_t obj_offset, unsigned long size);
 
+/*
+ * These are private flags, never pass them directly to
+ * pv4_tree_desc_*
+ */
+#define PV4_TREE_CANONICAL   0x800
+
 struct pv4_tree_desc {
+	unsigned flags;
+
 	/* v4 entry */
 	struct packed_git *p;
 	struct pack_window *w_curs;
+	unsigned int sha1_index;
+	int pathlen;
 
 	/* v2 entry */
 	struct tree_desc v2;
-- 
1.8.2.83.gc99314b