4fdd567a36a3f6b22a934838b46f6b2cd71fc3ec
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 int fd; /* open for reading */
65 /* NEEDSWORK: what else? */
66 } loose;
67
68 struct {
69 struct packed_git *pack;
70 off_t pos;
71 } in_pack;
72 } u;
73};
74
75int close_istream(struct git_istream *st)
76{
77 return st->vtbl->close(st);
78}
79
80ssize_t read_istream(struct git_istream *st, char *buf, size_t sz)
81{
82 return st->vtbl->read(st, buf, sz);
83}
84
85static enum input_source istream_source(const unsigned char *sha1,
86 enum object_type *type,
87 struct object_info *oi)
88{
89 unsigned long size;
90 int status;
91
92 oi->sizep = &size;
93 status = sha1_object_info_extended(sha1, oi);
94 if (status < 0)
95 return stream_error;
96 *type = status;
97
98 switch (oi->whence) {
99 case OI_LOOSE:
100 return loose;
101 case OI_PACKED:
102 if (!oi->u.packed.is_delta && big_file_threshold <= size)
103 return pack_non_delta;
104 /* fallthru */
105 default:
106 return incore;
107 }
108}
109
110struct git_istream *open_istream(const unsigned char *sha1,
111 enum object_type *type,
112 unsigned long *size)
113{
114 struct git_istream *st;
115 struct object_info oi;
116 const unsigned char *real = lookup_replace_object(sha1);
117 enum input_source src = istream_source(real, type, &oi);
118
119 if (src < 0)
120 return NULL;
121
122 st = xmalloc(sizeof(*st));
123 if (open_istream_tbl[src](st, &oi, real, type)) {
124 if (open_istream_incore(st, &oi, real, type)) {
125 free(st);
126 return NULL;
127 }
128 }
129 *size = st->size;
130 return st;
131}
132
133
134/*****************************************************************
135 *
136 * Common helpers
137 *
138 *****************************************************************/
139
140static 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
150 *
151 *****************************************************************/
152
153static open_method_decl(loose)
154{
155 return -1; /* for now */
156}
157
158
159/*****************************************************************
160 *
161 * Non-delta packed object stream
162 *
163 *****************************************************************/
164
165static 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
214static close_method_decl(pack_non_delta)
215{
216 close_deflated_stream(st);
217 return 0;
218}
219
220static struct stream_vtbl pack_non_delta_vtbl = {
221 close_istream_pack_non_delta,
222 read_istream_pack_non_delta,
223};
224
225static open_method_decl(pack_non_delta)
226{
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
254/*****************************************************************
255 *
256 * In-core stream
257 *
258 *****************************************************************/
259
260static close_method_decl(incore)
261{
262 free(st->u.incore.buf);
263 return 0;
264}
265
266static read_method_decl(incore)
267{
268 size_t read_size = sz;
269 size_t remainder = st->size - st->u.incore.read_ptr;
270
271 if (remainder <= read_size)
272 read_size = remainder;
273 if (read_size) {
274 memcpy(buf, st->u.incore.buf + st->u.incore.read_ptr, read_size);
275 st->u.incore.read_ptr += read_size;
276 }
277 return read_size;
278}
279
280static struct stream_vtbl incore_vtbl = {
281 close_istream_incore,
282 read_istream_incore,
283};
284
285static open_method_decl(incore)
286{
287 st->u.incore.buf = read_sha1_file_extended(sha1, type, &st->size, 0);
288 st->u.incore.read_ptr = 0;
289 st->vtbl = &incore_vtbl;
290
291 return st->u.incore.buf ? 0 : -1;
292}