Skip to content

Commit 140fa89

Browse files
chiyutianyigitster
authored andcommitted
object-file.c: add "stream_loose_object()" to handle large object
If we want unpack and write a loose object using "write_loose_object", we have to feed it with a buffer with the same size of the object, which will consume lots of memory and may cause OOM. This can be improved by feeding data to "stream_loose_object()" in a stream. Add a new function "stream_loose_object()", which is a stream version of "write_loose_object()" but with a low memory footprint. We will use this function to unpack large blob object in later commit. Another difference with "write_loose_object()" is that we have no chance to run "write_object_file_prepare()" to calculate the oid in advance. In "write_loose_object()", we know the oid and we can write the temporary file in the same directory as the final object, but for an object with an undetermined oid, we don't know the exact directory for the object. Still, we need to save the temporary file we're preparing somewhere. We'll do that in the top-level ".git/objects/" directory (or whatever "GIT_OBJECT_DIRECTORY" is set to). Once we've streamed it we'll know the OID, and will move it to its canonical path. "freshen_packed_object()" or "freshen_loose_object()" will be called inside "stream_loose_object()" after obtaining the "oid". Helped-by: René Scharfe <[email protected]> Helped-by: Ævar Arnfjörð Bjarmason <[email protected]> Helped-by: Jiang Xin <[email protected]> Signed-off-by: Han Xin <[email protected]> Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3100a1e commit 140fa89

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

object-file.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,106 @@ static int freshen_packed_object(const struct object_id *oid)
21192119
return 1;
21202120
}
21212121

2122+
int stream_loose_object(struct input_stream *in_stream, size_t len,
2123+
struct object_id *oid)
2124+
{
2125+
int fd, ret, err = 0, flush = 0;
2126+
unsigned char compressed[4096];
2127+
git_zstream stream;
2128+
git_hash_ctx c;
2129+
struct strbuf tmp_file = STRBUF_INIT;
2130+
struct strbuf filename = STRBUF_INIT;
2131+
int dirlen;
2132+
char hdr[MAX_HEADER_LEN];
2133+
int hdrlen;
2134+
2135+
/* Since oid is not determined, save tmp file to odb path. */
2136+
strbuf_addf(&filename, "%s/", get_object_directory());
2137+
hdrlen = format_object_header(hdr, sizeof(hdr), OBJ_BLOB, len);
2138+
2139+
/*
2140+
* Common steps for write_loose_object and stream_loose_object to
2141+
* start writing loose objects:
2142+
*
2143+
* - Create tmpfile for the loose object.
2144+
* - Setup zlib stream for compression.
2145+
* - Start to feed header to zlib stream.
2146+
*/
2147+
fd = start_loose_object_common(&tmp_file, filename.buf, 0,
2148+
&stream, compressed, sizeof(compressed),
2149+
&c, hdr, hdrlen);
2150+
if (fd < 0) {
2151+
err = -1;
2152+
goto cleanup;
2153+
}
2154+
2155+
/* Then the data itself.. */
2156+
do {
2157+
unsigned char *in0 = stream.next_in;
2158+
2159+
if (!stream.avail_in && !in_stream->is_finished) {
2160+
const void *in = in_stream->read(in_stream, &stream.avail_in);
2161+
stream.next_in = (void *)in;
2162+
in0 = (unsigned char *)in;
2163+
/* All data has been read. */
2164+
if (in_stream->is_finished)
2165+
flush = 1;
2166+
}
2167+
ret = write_loose_object_common(&c, &stream, flush, in0, fd,
2168+
compressed, sizeof(compressed));
2169+
/*
2170+
* Unlike write_loose_object(), we do not have the entire
2171+
* buffer. If we get Z_BUF_ERROR due to too few input bytes,
2172+
* then we'll replenish them in the next input_stream->read()
2173+
* call when we loop.
2174+
*/
2175+
} while (ret == Z_OK || ret == Z_BUF_ERROR);
2176+
2177+
if (stream.total_in != len + hdrlen)
2178+
die(_("write stream object %ld != %"PRIuMAX), stream.total_in,
2179+
(uintmax_t)len + hdrlen);
2180+
2181+
/* Common steps for write_loose_object and stream_loose_object to
2182+
* end writing loose oject:
2183+
*
2184+
* - End the compression of zlib stream.
2185+
* - Get the calculated oid.
2186+
*/
2187+
if (ret != Z_STREAM_END)
2188+
die(_("unable to stream deflate new object (%d)"), ret);
2189+
ret = end_loose_object_common(&c, &stream, oid);
2190+
if (ret != Z_OK)
2191+
die(_("deflateEnd on stream object failed (%d)"), ret);
2192+
close_loose_object(fd, tmp_file.buf);
2193+
2194+
if (freshen_packed_object(oid) || freshen_loose_object(oid)) {
2195+
unlink_or_warn(tmp_file.buf);
2196+
goto cleanup;
2197+
}
2198+
2199+
loose_object_path(the_repository, &filename, oid);
2200+
2201+
/* We finally know the object path, and create the missing dir. */
2202+
dirlen = directory_size(filename.buf);
2203+
if (dirlen) {
2204+
struct strbuf dir = STRBUF_INIT;
2205+
strbuf_add(&dir, filename.buf, dirlen);
2206+
2207+
if (mkdir_in_gitdir(dir.buf) && errno != EEXIST) {
2208+
err = error_errno(_("unable to create directory %s"), dir.buf);
2209+
strbuf_release(&dir);
2210+
goto cleanup;
2211+
}
2212+
strbuf_release(&dir);
2213+
}
2214+
2215+
err = finalize_object_file(tmp_file.buf, filename.buf);
2216+
cleanup:
2217+
strbuf_release(&tmp_file);
2218+
strbuf_release(&filename);
2219+
return err;
2220+
}
2221+
21222222
int write_object_file_flags(const void *buf, unsigned long len,
21232223
enum object_type type, struct object_id *oid,
21242224
unsigned flags)

object-store.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ struct object_directory {
4646
char *path;
4747
};
4848

49+
struct input_stream {
50+
const void *(*read)(struct input_stream *, unsigned long *len);
51+
void *data;
52+
int is_finished;
53+
};
54+
4955
KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
5056
struct object_directory *, 1, fspathhash, fspatheq)
5157

@@ -269,6 +275,8 @@ static inline int write_object_file(const void *buf, unsigned long len,
269275
int write_object_file_literally(const void *buf, unsigned long len,
270276
const char *type, struct object_id *oid,
271277
unsigned flags);
278+
int stream_loose_object(struct input_stream *in_stream, size_t len,
279+
struct object_id *oid);
272280

273281
/*
274282
* Add an object file to the in-memory object store, without writing it

0 commit comments

Comments
 (0)