streaming: read non-delta incrementally from a pack

Helped-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed May 13, 2011 at 15:34 UTC 7ef2d9a2604b0cd554e59f948f2c879b3708dfa5
1 file changed +102 -3
streaming.c
+102 -3
@@ -51,6 +51,8 @@ static open_istream_fn open_istream_tbl[] = {
51 struct git_istream {
52 const struct stream_vtbl *vtbl;
53 unsigned long size; /* inflated size of full object */
54 + z_stream z;
55 + enum { z_unused, z_used, z_done, z_error } z_state;
56
57 union {
58 struct {
@@ -64,8 +66,8 @@ struct git_istream {
66 } loose;
67
68 struct {
67 - int fd; /* open for reading */
68 - /* NEEDSWORK: what else? */
69 + struct packed_git *pack;
70 + off_t pos;
71 } in_pack;
72 } u;
73 };
@@ -128,6 +130,20 @@ struct git_istream *open_istream(const unsigned char *sha1,
130 return st;
131 }
132
133 +
134 +/*****************************************************************
135 + *
136 + * Common helpers
137 + *
138 + *****************************************************************/
139 +
140 +static void close_deflated_stream(struct git_istream *st)
141 +{
142 + if (st->z_state == z_used)
143 + git_inflate_end(&st->z);
144 +}
145 +
146 +
147 /*****************************************************************
148 *
149 * Loose object stream
@@ -146,9 +162,92 @@ static open_method_decl(loose)
162 *
163 *****************************************************************/
164
165 +static read_method_decl(pack_non_delta)
166 +{
167 + size_t total_read = 0;
168 +
169 + switch (st->z_state) {
170 + case z_unused:
171 + memset(&st->z, 0, sizeof(st->z));
172 + git_inflate_init(&st->z);
173 + st->z_state = z_used;
174 + break;
175 + case z_done:
176 + return 0;
177 + case z_error:
178 + return -1;
179 + case z_used:
180 + break;
181 + }
182 +
183 + while (total_read < sz) {
184 + int status;
185 + struct pack_window *window = NULL;
186 + unsigned char *mapped;
187 +
188 + mapped = use_pack(st->u.in_pack.pack, &window,
189 + st->u.in_pack.pos, &st->z.avail_in);
190 +
191 + st->z.next_out = (unsigned char *)buf + total_read;
192 + st->z.avail_out = sz - total_read;
193 + st->z.next_in = mapped;
194 + status = git_inflate(&st->z, Z_FINISH);
195 +
196 + st->u.in_pack.pos += st->z.next_in - mapped;
197 + total_read = st->z.next_out - (unsigned char *)buf;
198 + unuse_pack(&window);
199 +
200 + if (status == Z_STREAM_END) {
201 + git_inflate_end(&st->z);
202 + st->z_state = z_done;
203 + break;
204 + }
205 + if (status != Z_OK && status != Z_BUF_ERROR) {
206 + git_inflate_end(&st->z);
207 + st->z_state = z_error;
208 + return -1;
209 + }
210 + }
211 + return total_read;
212 +}
213 +
214 +static close_method_decl(pack_non_delta)
215 +{
216 + close_deflated_stream(st);
217 + return 0;
218 +}
219 +
220 +static struct stream_vtbl pack_non_delta_vtbl = {
221 + close_istream_pack_non_delta,
222 + read_istream_pack_non_delta,
223 +};
224 +
225 static open_method_decl(pack_non_delta)
226 {
151 - return -1; /* for now */
227 + struct pack_window *window;
228 + enum object_type in_pack_type;
229 +
230 + st->u.in_pack.pack = oi->u.packed.pack;
231 + st->u.in_pack.pos = oi->u.packed.offset;
232 + window = NULL;
233 +
234 + in_pack_type = unpack_object_header(st->u.in_pack.pack,
235 + &window,
236 + &st->u.in_pack.pos,
237 + &st->size);
238 + unuse_pack(&window);
239 + switch (in_pack_type) {
240 + default:
241 + return -1; /* we do not do deltas for now */
242 + case OBJ_COMMIT:
243 + case OBJ_TREE:
244 + case OBJ_BLOB:
245 + case OBJ_TAG:
246 + break;
247 + }
248 + st->z_state = z_unused;
249 + st->vtbl = &pack_non_delta_vtbl;
250 + return 0;
251 }
252
253