06029266443f3e49516abbdaf8afbb4663de9f98
1/*
2 * Copyright (c) 2011, Google Inc.
3 */
4#include "cache.h"
5#include "streaming.h"
6
7enum input_source {
8 stream_error = -1,
9 incore = 0,
10 loose = 1,
11 pack_non_delta = 2
12};
13
14typedef int (*open_istream_fn)(struct git_istream *,
15 struct object_info *,
16 const unsigned char *,
17 enum object_type *);
18typedef int (*close_istream_fn)(struct git_istream *);
19typedef ssize_t (*read_istream_fn)(struct git_istream *, char *, size_t);
20
21struct stream_vtbl {
22 close_istream_fn close;
23 read_istream_fn read;
24};
25
26#define open_method_decl(name) \
27 int open_istream_ ##name \
28 (struct git_istream *st, struct object_info *oi, \
29 const unsigned char *sha1, \
30 enum object_type *type)
31
32#define close_method_decl(name) \
33 int close_istream_ ##name \
34 (struct git_istream *st)
35
36#define read_method_decl(name) \
37 ssize_t read_istream_ ##name \
38 (struct git_istream *st, char *buf, size_t sz)
39
40/* forward declaration */
41static open_method_decl(incore);
42static open_method_decl(loose);
43static open_method_decl(pack_non_delta);
44
45static open_istream_fn open_istream_tbl[] = {
46 open_istream_incore,
47 open_istream_loose,
48 open_istream_pack_non_delta,
49};
50
51struct 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 {
59 char *buf; /* from read_object() */
60 unsigned long read_ptr;
61 } incore;
62
63 struct {
64 void *mapped;
65 unsigned long mapsize;
66 char hdr[32];
67 int hdr_avail;
68 int hdr_used;
69 } loose;
70
71 struct {
72 struct packed_git *pack;
73 off_t pos;
74 } in_pack;
75 } u;
76};
77
78int close_istream(struct git_istream *st)
79{
80 return st->vtbl->close(st);
81}
82
83ssize_t read_istream(struct git_istream *st, char *buf, size_t sz)
84{
85 return st->vtbl->read(st, buf, sz);
86}
87
88static enum input_source istream_source(const unsigned char *sha1,
89 enum object_type *type,
90 struct object_info *oi)
91{
92 unsigned long size;
93 int status;
94
95 oi->sizep = &size;
96 status = sha1_object_info_extended(sha1, oi);
97 if (status < 0)
98 return stream_error;
99 *type = status;
100
101 switch (oi->whence) {
102 case OI_LOOSE:
103 return loose;
104 case OI_PACKED:
105 if (!oi->u.packed.is_delta && big_file_threshold <= size)
106 return pack_non_delta;
107 /* fallthru */
108 default:
109 return incore;
110 }
111}
112
113struct git_istream *open_istream(const unsigned char *sha1,
114 enum object_type *type,
115 unsigned long *size)
116{
117 struct git_istream *st;
118 struct object_info oi;
119 const unsigned char *real = lookup_replace_object(sha1);
120 enum input_source src = istream_source(real, type, &oi);
121
122 if (src < 0)
123 return NULL;
124
125 st = xmalloc(sizeof(*st));
126 if (open_istream_tbl[src](st, &oi, real, type)) {
127 if (open_istream_incore(st, &oi, real, type)) {
128 free(st);
129 return NULL;
130 }
131 }
132 *size = st->size;
133 return st;
134}
135
136
137/*****************************************************************
138 *
139 * Common helpers
140 *
141 *****************************************************************/
142
143static void close_deflated_stream(struct git_istream *st)
144{
145 if (st->z_state == z_used)
146 git_inflate_end(&st->z);
147}
148
149
150/*****************************************************************
151 *
152 * Loose object stream
153 *
154 *****************************************************************/
155
156static read_method_decl(loose)
157{
158 size_t total_read = 0;
159
160 switch (st->z_state) {
161 case z_done:
162 return 0;
163 case z_error:
164 return -1;
165 default:
166 break;
167 }
168
169 if (st->u.loose.hdr_used < st->u.loose.hdr_avail) {
170 size_t to_copy = st->u.loose.hdr_avail - st->u.loose.hdr_used;
171 if (sz < to_copy)
172 to_copy = sz;
173 memcpy(buf, st->u.loose.hdr + st->u.loose.hdr_used, to_copy);
174 st->u.loose.hdr_used += to_copy;
175 total_read += to_copy;
176 }
177
178 while (total_read < sz) {
179 int status;
180
181 st->z.next_out = (unsigned char *)buf + total_read;
182 st->z.avail_out = sz - total_read;
183 status = git_inflate(&st->z, Z_FINISH);
184
185 total_read = st->z.next_out - (unsigned char *)buf;
186
187 if (status == Z_STREAM_END) {
188 git_inflate_end(&st->z);
189 st->z_state = z_done;
190 break;
191 }
192 if (status != Z_OK && status != Z_BUF_ERROR) {
193 git_inflate_end(&st->z);
194 st->z_state = z_error;
195 return -1;
196 }
197 }
198 return total_read;
199}
200
201static close_method_decl(loose)
202{
203 close_deflated_stream(st);
204 munmap(st->u.loose.mapped, st->u.loose.mapsize);
205 return 0;
206}
207
208static struct stream_vtbl loose_vtbl = {
209 close_istream_loose,
210 read_istream_loose,
211};
212
213static open_method_decl(loose)
214{
215 st->u.loose.mapped = map_sha1_file(sha1, &st->u.loose.mapsize);
216 if (!st->u.loose.mapped)
217 return -1;
218 if (unpack_sha1_header(&st->z,
219 st->u.loose.mapped,
220 st->u.loose.mapsize,
221 st->u.loose.hdr,
222 sizeof(st->u.loose.hdr)) < 0) {
223 git_inflate_end(&st->z);
224 munmap(st->u.loose.mapped, st->u.loose.mapsize);
225 return -1;
226 }
227
228 parse_sha1_header(st->u.loose.hdr, &st->size);
229 st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
230 st->u.loose.hdr_avail = st->z.total_out;
231 st->z_state = z_used;
232
233 st->vtbl = &loose_vtbl;
234 return 0;
235}
236
237
238/*****************************************************************
239 *
240 * Non-delta packed object stream
241 *
242 *****************************************************************/
243
244static read_method_decl(pack_non_delta)
245{
246 size_t total_read = 0;
247
248 switch (st->z_state) {
249 case z_unused:
250 memset(&st->z, 0, sizeof(st->z));
251 git_inflate_init(&st->z);
252 st->z_state = z_used;
253 break;
254 case z_done:
255 return 0;
256 case z_error:
257 return -1;
258 case z_used:
259 break;
260 }
261
262 while (total_read < sz) {
263 int status;
264 struct pack_window *window = NULL;
265 unsigned char *mapped;
266
267 mapped = use_pack(st->u.in_pack.pack, &window,
268 st->u.in_pack.pos, &st->z.avail_in);
269
270 st->z.next_out = (unsigned char *)buf + total_read;
271 st->z.avail_out = sz - total_read;
272 st->z.next_in = mapped;
273 status = git_inflate(&st->z, Z_FINISH);
274
275 st->u.in_pack.pos += st->z.next_in - mapped;
276 total_read = st->z.next_out - (unsigned char *)buf;
277 unuse_pack(&window);
278
279 if (status == Z_STREAM_END) {
280 git_inflate_end(&st->z);
281 st->z_state = z_done;
282 break;
283 }
284 if (status != Z_OK && status != Z_BUF_ERROR) {
285 git_inflate_end(&st->z);
286 st->z_state = z_error;
287 return -1;
288 }
289 }
290 return total_read;
291}
292
293static close_method_decl(pack_non_delta)
294{
295 close_deflated_stream(st);
296 return 0;
297}
298
299static struct stream_vtbl pack_non_delta_vtbl = {
300 close_istream_pack_non_delta,
301 read_istream_pack_non_delta,
302};
303
304static open_method_decl(pack_non_delta)
305{
306 struct pack_window *window;
307 enum object_type in_pack_type;
308
309 st->u.in_pack.pack = oi->u.packed.pack;
310 st->u.in_pack.pos = oi->u.packed.offset;
311 window = NULL;
312
313 in_pack_type = unpack_object_header(st->u.in_pack.pack,
314 &window,
315 &st->u.in_pack.pos,
316 &st->size);
317 unuse_pack(&window);
318 switch (in_pack_type) {
319 default:
320 return -1; /* we do not do deltas for now */
321 case OBJ_COMMIT:
322 case OBJ_TREE:
323 case OBJ_BLOB:
324 case OBJ_TAG:
325 break;
326 }
327 st->z_state = z_unused;
328 st->vtbl = &pack_non_delta_vtbl;
329 return 0;
330}
331
332
333/*****************************************************************
334 *
335 * In-core stream
336 *
337 *****************************************************************/
338
339static close_method_decl(incore)
340{
341 free(st->u.incore.buf);
342 return 0;
343}
344
345static read_method_decl(incore)
346{
347 size_t read_size = sz;
348 size_t remainder = st->size - st->u.incore.read_ptr;
349
350 if (remainder <= read_size)
351 read_size = remainder;
352 if (read_size) {
353 memcpy(buf, st->u.incore.buf + st->u.incore.read_ptr, read_size);
354 st->u.incore.read_ptr += read_size;
355 }
356 return read_size;
357}
358
359static struct stream_vtbl incore_vtbl = {
360 close_istream_incore,
361 read_istream_incore,
362};
363
364static open_method_decl(incore)
365{
366 st->u.incore.buf = read_sha1_file_extended(sha1, type, &st->size, 0);
367 st->u.incore.read_ptr = 0;
368 st->vtbl = &incore_vtbl;
369
370 return st->u.incore.buf ? 0 : -1;
371}