Skip to content

Commit a97b769

Browse files
committed
odb: avoid inflating the full delta to read the header
When we read the header, we want to know the size and type of the object. We're currently inflating the full delta in order to read the first few bytes. This can mean hundreds of kB needlessly inflated for large objects. Instead use a packfile stream to read just enough so we can read the two varints in the header and avoid inflating most of the delta.
1 parent 88284df commit a97b769

File tree

3 files changed

+48
-6
lines changed

3 files changed

+48
-6
lines changed

src/delta-apply.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,37 @@ int git__delta_read_header(
4949
return 0;
5050
}
5151

52+
#define DELTA_HEADER_BUFFER_LEN 16
53+
int git__delta_read_header_fromstream(size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
54+
{
55+
static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
56+
unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
57+
const unsigned char *delta, *delta_end;
58+
size_t len;
59+
ssize_t read;
60+
61+
len = read = 0;
62+
while (len < buffer_len) {
63+
read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
64+
65+
if (read == 0)
66+
break;
67+
68+
if (read == GIT_EBUFS)
69+
continue;
70+
71+
len += read;
72+
}
73+
74+
delta = buffer;
75+
delta_end = delta + len;
76+
if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
77+
(hdr_sz(res_sz, &delta, delta_end) < 0))
78+
return -1;
79+
80+
return 0;
81+
}
82+
5283
int git__delta_apply(
5384
git_rawobj *out,
5485
const unsigned char *base,

src/delta-apply.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define INCLUDE_delta_apply_h__
99

1010
#include "odb.h"
11+
#include "pack.h"
1112

1213
/**
1314
* Apply a git binary delta to recover the original content.
@@ -47,4 +48,15 @@ extern int git__delta_read_header(
4748
size_t *base_sz,
4849
size_t *res_sz);
4950

51+
/**
52+
* Read the header of a git binary delta
53+
*
54+
* This variant reads just enough from the packfile stream to read the
55+
* delta header.
56+
*/
57+
extern int git__delta_read_header_fromstream(
58+
size_t *base_sz,
59+
size_t *res_sz,
60+
git_packfile_stream *stream);
61+
5062
#endif

src/pack.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -499,15 +499,14 @@ int git_packfile_resolve_header(
499499

500500
if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
501501
size_t base_size;
502-
git_rawobj delta;
502+
git_packfile_stream stream;
503+
503504
base_offset = get_delta_base(p, &w_curs, &curpos, type, offset);
504505
git_mwindow_close(&w_curs);
505-
error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type);
506-
git_mwindow_close(&w_curs);
507-
if (error < 0)
506+
if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
508507
return error;
509-
error = git__delta_read_header(delta.data, delta.len, &base_size, size_p);
510-
git__free(delta.data);
508+
error = git__delta_read_header_fromstream(&base_size, size_p, &stream);
509+
git_packfile_stream_free(&stream);
511510
if (error < 0)
512511
return error;
513512
} else

0 commit comments

Comments
 (0)