Commit d8f6fee36efb4a656a317f873b617888c6f861f4

Patrick Steinhardt 2019-12-13T14:57:53

pack: refactor unpacking of raw objects to use `git_zstream` While we do have a zstream abstraction that encapsulates all the calls to zlib as well as its error handling, we do not use it in our pack file code. Refactor it to make the code a lot easier to understand.

diff --git a/src/pack.c b/src/pack.c
index 4c4eb9b..92a3abe 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -7,12 +7,13 @@
 
 #include "pack.h"
 
-#include "odb.h"
 #include "delta.h"
-#include "sha1_lookup.h"
-#include "mwindow.h"
 #include "futils.h"
+#include "mwindow.h"
+#include "odb.h"
 #include "oid.h"
+#include "sha1_lookup.h"
+#include "zstream.h"
 
 #include <zlib.h>
 
@@ -845,64 +846,60 @@ void git_packfile_stream_dispose(git_packfile_stream *obj)
 static int packfile_unpack_compressed(
 	git_rawobj *obj,
 	struct git_pack_file *p,
-	git_mwindow **w_curs,
-	off64_t *curpos,
+	git_mwindow **mwindow,
+	off64_t *position,
 	size_t size,
 	git_object_t type)
 {
-	size_t buf_size;
-	int st;
-	z_stream stream;
-	unsigned char *buffer, *in;
-
-	GIT_ERROR_CHECK_ALLOC_ADD(&buf_size, size, 1);
-	buffer = git__calloc(1, buf_size);
-	GIT_ERROR_CHECK_ALLOC(buffer);
+	git_zstream zstream = GIT_ZSTREAM_INIT;
+	size_t buffer_len, total = 0;
+	char *data = NULL;
+	int error;
 
-	memset(&stream, 0, sizeof(stream));
-	stream.next_out = buffer;
-	stream.avail_out = (uInt)buf_size;
-	stream.zalloc = use_git_alloc;
-	stream.zfree = use_git_free;
+	GIT_ERROR_CHECK_ALLOC_ADD(&buffer_len, size, 1);
+	data = git__calloc(1, buffer_len);
+	GIT_ERROR_CHECK_ALLOC(data);
 
-	st = inflateInit(&stream);
-	if (st != Z_OK) {
-		git__free(buffer);
+	if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0) {
 		git_error_set(GIT_ERROR_ZLIB, "failed to init zlib stream on unpack");
-
-		return -1;
+		goto out;
 	}
 
 	do {
-		in = pack_window_open(p, w_curs, *curpos, &stream.avail_in);
-		stream.next_in = in;
-		st = inflate(&stream, Z_FINISH);
-		git_mwindow_close(w_curs);
+		size_t bytes = buffer_len - total;
+		unsigned int window_len;
+		unsigned char *in;
 
-		if (!stream.avail_out)
-			break; /* the payload is larger than it should be */
+		in = pack_window_open(p, mwindow, *position, &window_len);
 
-		if (st == Z_BUF_ERROR && in == NULL) {
-			inflateEnd(&stream);
-			git__free(buffer);
-			return GIT_EBUFS;
+		if ((error = git_zstream_set_input(&zstream, in, window_len)) < 0 ||
+		    (error = git_zstream_get_output_chunk(data + total, &bytes, &zstream)) < 0) {
+			git_mwindow_close(mwindow);
+			goto out;
 		}
 
-		*curpos += stream.next_in - in;
-	} while (st == Z_OK || st == Z_BUF_ERROR);
+		git_mwindow_close(mwindow);
 
-	inflateEnd(&stream);
+		*position += window_len - zstream.in_len;
+		total += bytes;
+	} while (total < size);
 
-	if ((st != Z_STREAM_END) || stream.total_out != size) {
-		git__free(buffer);
+	if (total != size || !git_zstream_eos(&zstream)) {
 		git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream");
-		return -1;
+		error = -1;
+		goto out;
 	}
 
 	obj->type = type;
 	obj->len = size;
-	obj->data = buffer;
-	return 0;
+	obj->data = data;
+
+out:
+	git_zstream_free(&zstream);
+	if (error)
+		git__free(data);
+
+	return error;
 }
 
 /*
diff --git a/src/zstream.c b/src/zstream.c
index fc8bfb8..975ead2 100644
--- a/src/zstream.c
+++ b/src/zstream.c
@@ -77,6 +77,11 @@ bool git_zstream_done(git_zstream *zstream)
 	return (!zstream->in_len && zstream->zerr == Z_STREAM_END);
 }
 
+bool git_zstream_eos(git_zstream *zstream)
+{
+	return zstream->zerr == Z_STREAM_END;
+}
+
 size_t git_zstream_suggest_output_len(git_zstream *zstream)
 {
 	if (zstream->in_len > ZSTREAM_BUFFER_SIZE)
diff --git a/src/zstream.h b/src/zstream.h
index 47ecc13..db0cc47 100644
--- a/src/zstream.h
+++ b/src/zstream.h
@@ -44,6 +44,7 @@ int git_zstream_get_output_chunk(
 int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream);
 
 bool git_zstream_done(git_zstream *zstream);
+bool git_zstream_eos(git_zstream *zstream);
 
 void git_zstream_reset(git_zstream *zstream);