Commit 4d384d6bbe3efc72fb212a2c5c71c8064a2cee54

Edward Thomson 2016-05-02T13:59:51

Merge pull request #3759 from libgit2/cmn/faster-header odb: avoid inflating the full delta to read the header

diff --git a/src/delta-apply.c b/src/delta-apply.c
index 89745fa..6e86a81 100644
--- a/src/delta-apply.c
+++ b/src/delta-apply.c
@@ -49,6 +49,37 @@ int git__delta_read_header(
 	return 0;
 }
 
+#define DELTA_HEADER_BUFFER_LEN 16
+int git__delta_read_header_fromstream(size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
+{
+	static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
+	unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
+	const unsigned char *delta, *delta_end;
+	size_t len;
+	ssize_t read;
+
+	len = read = 0;
+	while (len < buffer_len) {
+		read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
+
+		if (read == 0)
+			break;
+
+		if (read == GIT_EBUFS)
+			continue;
+
+		len += read;
+	}
+
+	delta = buffer;
+	delta_end = delta + len;
+	if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
+	    (hdr_sz(res_sz, &delta, delta_end) < 0))
+		return -1;
+
+	return 0;
+}
+
 int git__delta_apply(
 	git_rawobj *out,
 	const unsigned char *base,
diff --git a/src/delta-apply.h b/src/delta-apply.h
index d7d99d0..eeeb786 100644
--- a/src/delta-apply.h
+++ b/src/delta-apply.h
@@ -8,6 +8,7 @@
 #define INCLUDE_delta_apply_h__
 
 #include "odb.h"
+#include "pack.h"
 
 /**
  * Apply a git binary delta to recover the original content.
@@ -47,4 +48,15 @@ extern int git__delta_read_header(
 	size_t *base_sz,
 	size_t *res_sz);
 
+/**
+ * Read the header of a git binary delta
+ *
+ * This variant reads just enough from the packfile stream to read the
+ * delta header.
+ */
+extern int git__delta_read_header_fromstream(
+	size_t *base_sz,
+	size_t *res_sz,
+	git_packfile_stream *stream);
+
 #endif
diff --git a/src/pack.c b/src/pack.c
index e7003e6..6a700e2 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -499,15 +499,14 @@ int git_packfile_resolve_header(
 
 	if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
 		size_t base_size;
-		git_rawobj delta;
+		git_packfile_stream stream;
+
 		base_offset = get_delta_base(p, &w_curs, &curpos, type, offset);
 		git_mwindow_close(&w_curs);
-		error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type);
-		git_mwindow_close(&w_curs);
-		if (error < 0)
+		if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
 			return error;
-		error = git__delta_read_header(delta.data, delta.len, &base_size, size_p);
-		git__free(delta.data);
+		error = git__delta_read_header_fromstream(&base_size, size_p, &stream);
+		git_packfile_stream_free(&stream);
 		if (error < 0)
 			return error;
 	} else