diff options
Diffstat (limited to 'streaming.c')
-rw-r--r-- | streaming.c | 91 |
1 files changed, 84 insertions, 7 deletions
diff --git a/streaming.c b/streaming.c index 565f000790..4d978e54e4 100644 --- a/streaming.c +++ b/streaming.c @@ -60,12 +60,13 @@ struct filtered_istream { char obuf[FILTER_BUFFER]; int i_end, i_ptr; int o_end, o_ptr; + int input_finished; }; struct git_istream { const struct stream_vtbl *vtbl; unsigned long size; /* inflated size of full object */ - z_stream z; + git_zstream z; enum { z_unused, z_used, z_done, z_error } z_state; union { @@ -93,10 +94,12 @@ struct git_istream { int close_istream(struct git_istream *st) { - return st->vtbl->close(st); + int r = st->vtbl->close(st); + free(st); + return r; } -ssize_t read_istream(struct git_istream *st, char *buf, size_t sz) +ssize_t read_istream(struct git_istream *st, void *buf, size_t sz) { return st->vtbl->read(st, buf, sz); } @@ -118,7 +121,7 @@ static enum input_source istream_source(const unsigned char *sha1, case OI_LOOSE: return loose; case OI_PACKED: - if (!oi->u.packed.is_delta && big_file_threshold <= size) + if (!oi->u.packed.is_delta && big_file_threshold < size) return pack_non_delta; /* fallthru */ default: @@ -215,12 +218,30 @@ static read_method_decl(filtered) fs->o_end = FILTER_BUFFER - to_receive; continue; } + + /* tell the filter to drain upon no more input */ + if (fs->input_finished) { + size_t to_receive = FILTER_BUFFER; + if (stream_filter(fs->filter, + NULL, NULL, + fs->obuf, &to_receive)) + return -1; + fs->o_end = FILTER_BUFFER - to_receive; + if (!fs->o_end) + break; + continue; + } fs->i_end = fs->i_ptr = 0; /* refill the input from the upstream */ - fs->i_end = read_istream(fs->upstream, fs->ibuf, FILTER_BUFFER); - if (fs->i_end <= 0) - break; + if (!fs->input_finished) { + fs->i_end = read_istream(fs->upstream, fs->ibuf, FILTER_BUFFER); + if (fs->i_end < 0) + break; + if (fs->i_end) + continue; + } + fs->input_finished = 1; } return filled; } @@ -241,6 +262,7 @@ static struct git_istream *attach_stream_filter(struct git_istream *st, fs->filter = filter; fs->i_end = fs->i_ptr = 0; fs->o_end = fs->o_ptr = 0; + fs->input_finished = 0; ifs->size = -1; /* unknown */ return ifs; } @@ -467,3 +489,58 @@ static open_method_decl(incore) return st->u.incore.buf ? 0 : -1; } + + +/**************************************************************** + * Users of streaming interface + ****************************************************************/ + +int stream_blob_to_fd(int fd, unsigned const char *sha1, struct stream_filter *filter, + int can_seek) +{ + struct git_istream *st; + enum object_type type; + unsigned long sz; + ssize_t kept = 0; + int result = -1; + + st = open_istream(sha1, &type, &sz, filter); + if (!st) + return result; + if (type != OBJ_BLOB) + goto close_and_exit; + for (;;) { + char buf[1024 * 16]; + ssize_t wrote, holeto; + ssize_t readlen = read_istream(st, buf, sizeof(buf)); + + if (!readlen) + break; + if (can_seek && sizeof(buf) == readlen) { + for (holeto = 0; holeto < readlen; holeto++) + if (buf[holeto]) + break; + if (readlen == holeto) { + kept += holeto; + continue; + } + } + + if (kept && lseek(fd, kept, SEEK_CUR) == (off_t) -1) + goto close_and_exit; + else + kept = 0; + wrote = write_in_full(fd, buf, readlen); + + if (wrote != readlen) + goto close_and_exit; + } + if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 || + write(fd, "", 1) != 1)) + goto close_and_exit; + result = 0; + + close_and_exit: + close_istream(st); + return result; +} |