2 * Copyright (c) 2011, Google Inc.
4 #include "git-compat-util.h"
6 #include "environment.h"
8 #include "repository.h"
9 #include "object-file.h"
10 #include "object-store.h"
11 #include "replace-object.h"
15 typedef int (*open_istream_fn
)(struct git_istream
*,
17 const struct object_id
*,
19 typedef int (*close_istream_fn
)(struct git_istream
*);
20 typedef ssize_t (*read_istream_fn
)(struct git_istream
*, char *, size_t);
22 #define FILTER_BUFFER (1024*16)
24 struct filtered_istream
{
25 struct git_istream
*upstream
;
26 struct stream_filter
*filter
;
27 char ibuf
[FILTER_BUFFER
];
28 char obuf
[FILTER_BUFFER
];
36 close_istream_fn close
;
39 unsigned long size
; /* inflated size of full object */
41 enum { z_unused
, z_used
, z_done
, z_error
} z_state
;
45 char *buf
; /* from oid_object_info_extended() */
46 unsigned long read_ptr
;
51 unsigned long mapsize
;
58 struct packed_git
*pack
;
62 struct filtered_istream filtered
;
66 /*****************************************************************
70 *****************************************************************/
72 static void close_deflated_stream(struct git_istream
*st
)
74 if (st
->z_state
== z_used
)
75 git_inflate_end(&st
->z
);
79 /*****************************************************************
83 *****************************************************************/
85 static int close_istream_filtered(struct git_istream
*st
)
87 free_stream_filter(st
->u
.filtered
.filter
);
88 return close_istream(st
->u
.filtered
.upstream
);
91 static ssize_t
read_istream_filtered(struct git_istream
*st
, char *buf
,
94 struct filtered_istream
*fs
= &(st
->u
.filtered
);
98 /* do we already have filtered output? */
99 if (fs
->o_ptr
< fs
->o_end
) {
100 size_t to_move
= fs
->o_end
- fs
->o_ptr
;
103 memcpy(buf
+ filled
, fs
->obuf
+ fs
->o_ptr
, to_move
);
104 fs
->o_ptr
+= to_move
;
109 fs
->o_end
= fs
->o_ptr
= 0;
111 /* do we have anything to feed the filter with? */
112 if (fs
->i_ptr
< fs
->i_end
) {
113 size_t to_feed
= fs
->i_end
- fs
->i_ptr
;
114 size_t to_receive
= FILTER_BUFFER
;
115 if (stream_filter(fs
->filter
,
116 fs
->ibuf
+ fs
->i_ptr
, &to_feed
,
117 fs
->obuf
, &to_receive
))
119 fs
->i_ptr
= fs
->i_end
- to_feed
;
120 fs
->o_end
= FILTER_BUFFER
- to_receive
;
124 /* tell the filter to drain upon no more input */
125 if (fs
->input_finished
) {
126 size_t to_receive
= FILTER_BUFFER
;
127 if (stream_filter(fs
->filter
,
129 fs
->obuf
, &to_receive
))
131 fs
->o_end
= FILTER_BUFFER
- to_receive
;
136 fs
->i_end
= fs
->i_ptr
= 0;
138 /* refill the input from the upstream */
139 if (!fs
->input_finished
) {
140 fs
->i_end
= read_istream(fs
->upstream
, fs
->ibuf
, FILTER_BUFFER
);
146 fs
->input_finished
= 1;
151 static struct git_istream
*attach_stream_filter(struct git_istream
*st
,
152 struct stream_filter
*filter
)
154 struct git_istream
*ifs
= xmalloc(sizeof(*ifs
));
155 struct filtered_istream
*fs
= &(ifs
->u
.filtered
);
157 ifs
->close
= close_istream_filtered
;
158 ifs
->read
= read_istream_filtered
;
161 fs
->i_end
= fs
->i_ptr
= 0;
162 fs
->o_end
= fs
->o_ptr
= 0;
163 fs
->input_finished
= 0;
164 ifs
->size
= -1; /* unknown */
168 /*****************************************************************
170 * Loose object stream
172 *****************************************************************/
174 static ssize_t
read_istream_loose(struct git_istream
*st
, char *buf
, size_t sz
)
176 size_t total_read
= 0;
178 switch (st
->z_state
) {
187 if (st
->u
.loose
.hdr_used
< st
->u
.loose
.hdr_avail
) {
188 size_t to_copy
= st
->u
.loose
.hdr_avail
- st
->u
.loose
.hdr_used
;
191 memcpy(buf
, st
->u
.loose
.hdr
+ st
->u
.loose
.hdr_used
, to_copy
);
192 st
->u
.loose
.hdr_used
+= to_copy
;
193 total_read
+= to_copy
;
196 while (total_read
< sz
) {
199 st
->z
.next_out
= (unsigned char *)buf
+ total_read
;
200 st
->z
.avail_out
= sz
- total_read
;
201 status
= git_inflate(&st
->z
, Z_FINISH
);
203 total_read
= st
->z
.next_out
- (unsigned char *)buf
;
205 if (status
== Z_STREAM_END
) {
206 git_inflate_end(&st
->z
);
207 st
->z_state
= z_done
;
210 if (status
!= Z_OK
&& (status
!= Z_BUF_ERROR
|| total_read
< sz
)) {
211 git_inflate_end(&st
->z
);
212 st
->z_state
= z_error
;
219 static int close_istream_loose(struct git_istream
*st
)
221 close_deflated_stream(st
);
222 munmap(st
->u
.loose
.mapped
, st
->u
.loose
.mapsize
);
226 static int open_istream_loose(struct git_istream
*st
, struct repository
*r
,
227 const struct object_id
*oid
,
228 enum object_type
*type
)
230 struct object_info oi
= OBJECT_INFO_INIT
;
231 oi
.sizep
= &st
->size
;
234 st
->u
.loose
.mapped
= map_loose_object(r
, oid
, &st
->u
.loose
.mapsize
);
235 if (!st
->u
.loose
.mapped
)
237 switch (unpack_loose_header(&st
->z
, st
->u
.loose
.mapped
,
238 st
->u
.loose
.mapsize
, st
->u
.loose
.hdr
,
239 sizeof(st
->u
.loose
.hdr
), NULL
)) {
246 if (parse_loose_header(st
->u
.loose
.hdr
, &oi
) < 0 || *type
< 0)
249 st
->u
.loose
.hdr_used
= strlen(st
->u
.loose
.hdr
) + 1;
250 st
->u
.loose
.hdr_avail
= st
->z
.total_out
;
251 st
->z_state
= z_used
;
252 st
->close
= close_istream_loose
;
253 st
->read
= read_istream_loose
;
257 git_inflate_end(&st
->z
);
258 munmap(st
->u
.loose
.mapped
, st
->u
.loose
.mapsize
);
263 /*****************************************************************
265 * Non-delta packed object stream
267 *****************************************************************/
269 static ssize_t
read_istream_pack_non_delta(struct git_istream
*st
, char *buf
,
272 size_t total_read
= 0;
274 switch (st
->z_state
) {
276 memset(&st
->z
, 0, sizeof(st
->z
));
277 git_inflate_init(&st
->z
);
278 st
->z_state
= z_used
;
288 while (total_read
< sz
) {
290 struct pack_window
*window
= NULL
;
291 unsigned char *mapped
;
293 mapped
= use_pack(st
->u
.in_pack
.pack
, &window
,
294 st
->u
.in_pack
.pos
, &st
->z
.avail_in
);
296 st
->z
.next_out
= (unsigned char *)buf
+ total_read
;
297 st
->z
.avail_out
= sz
- total_read
;
298 st
->z
.next_in
= mapped
;
299 status
= git_inflate(&st
->z
, Z_FINISH
);
301 st
->u
.in_pack
.pos
+= st
->z
.next_in
- mapped
;
302 total_read
= st
->z
.next_out
- (unsigned char *)buf
;
305 if (status
== Z_STREAM_END
) {
306 git_inflate_end(&st
->z
);
307 st
->z_state
= z_done
;
312 * Unlike the loose object case, we do not have to worry here
313 * about running out of input bytes and spinning infinitely. If
314 * we get Z_BUF_ERROR due to too few input bytes, then we'll
315 * replenish them in the next use_pack() call when we loop. If
316 * we truly hit the end of the pack (i.e., because it's corrupt
317 * or truncated), then use_pack() catches that and will die().
319 if (status
!= Z_OK
&& status
!= Z_BUF_ERROR
) {
320 git_inflate_end(&st
->z
);
321 st
->z_state
= z_error
;
328 static int close_istream_pack_non_delta(struct git_istream
*st
)
330 close_deflated_stream(st
);
334 static int open_istream_pack_non_delta(struct git_istream
*st
,
335 struct repository
*r UNUSED
,
336 const struct object_id
*oid UNUSED
,
337 enum object_type
*type UNUSED
)
339 struct pack_window
*window
;
340 enum object_type in_pack_type
;
344 in_pack_type
= unpack_object_header(st
->u
.in_pack
.pack
,
349 switch (in_pack_type
) {
351 return -1; /* we do not do deltas for now */
358 st
->z_state
= z_unused
;
359 st
->close
= close_istream_pack_non_delta
;
360 st
->read
= read_istream_pack_non_delta
;
366 /*****************************************************************
370 *****************************************************************/
372 static int close_istream_incore(struct git_istream
*st
)
374 free(st
->u
.incore
.buf
);
378 static ssize_t
read_istream_incore(struct git_istream
*st
, char *buf
, size_t sz
)
380 size_t read_size
= sz
;
381 size_t remainder
= st
->size
- st
->u
.incore
.read_ptr
;
383 if (remainder
<= read_size
)
384 read_size
= remainder
;
386 memcpy(buf
, st
->u
.incore
.buf
+ st
->u
.incore
.read_ptr
, read_size
);
387 st
->u
.incore
.read_ptr
+= read_size
;
392 static int open_istream_incore(struct git_istream
*st
, struct repository
*r
,
393 const struct object_id
*oid
, enum object_type
*type
)
395 struct object_info oi
= OBJECT_INFO_INIT
;
397 st
->u
.incore
.read_ptr
= 0;
398 st
->close
= close_istream_incore
;
399 st
->read
= read_istream_incore
;
402 oi
.sizep
= &st
->size
;
403 oi
.contentp
= (void **)&st
->u
.incore
.buf
;
404 return oid_object_info_extended(r
, oid
, &oi
,
405 OBJECT_INFO_DIE_IF_CORRUPT
);
408 /*****************************************************************************
409 * static helpers variables and functions for users of streaming interface
410 *****************************************************************************/
412 static int istream_source(struct git_istream
*st
,
413 struct repository
*r
,
414 const struct object_id
*oid
,
415 enum object_type
*type
)
419 struct object_info oi
= OBJECT_INFO_INIT
;
423 status
= oid_object_info_extended(r
, oid
, &oi
, 0);
429 st
->open
= open_istream_loose
;
432 if (!oi
.u
.packed
.is_delta
&& big_file_threshold
< size
) {
433 st
->u
.in_pack
.pack
= oi
.u
.packed
.pack
;
434 st
->u
.in_pack
.pos
= oi
.u
.packed
.offset
;
435 st
->open
= open_istream_pack_non_delta
;
440 st
->open
= open_istream_incore
;
445 /****************************************************************
446 * Users of streaming interface
447 ****************************************************************/
449 int close_istream(struct git_istream
*st
)
451 int r
= st
->close(st
);
456 ssize_t
read_istream(struct git_istream
*st
, void *buf
, size_t sz
)
458 return st
->read(st
, buf
, sz
);
461 struct git_istream
*open_istream(struct repository
*r
,
462 const struct object_id
*oid
,
463 enum object_type
*type
,
465 struct stream_filter
*filter
)
467 struct git_istream
*st
= xmalloc(sizeof(*st
));
468 const struct object_id
*real
= lookup_replace_object(r
, oid
);
469 int ret
= istream_source(st
, r
, real
, type
);
476 if (st
->open(st
, r
, real
, type
)) {
477 if (open_istream_incore(st
, r
, real
, type
)) {
483 /* Add "&& !is_null_stream_filter(filter)" for performance */
484 struct git_istream
*nst
= attach_stream_filter(st
, filter
);
496 int stream_blob_to_fd(int fd
, const struct object_id
*oid
, struct stream_filter
*filter
,
499 struct git_istream
*st
;
500 enum object_type type
;
505 st
= open_istream(the_repository
, oid
, &type
, &sz
, filter
);
508 free_stream_filter(filter
);
511 if (type
!= OBJ_BLOB
)
515 ssize_t wrote
, holeto
;
516 ssize_t readlen
= read_istream(st
, buf
, sizeof(buf
));
522 if (can_seek
&& sizeof(buf
) == readlen
) {
523 for (holeto
= 0; holeto
< readlen
; holeto
++)
526 if (readlen
== holeto
) {
532 if (kept
&& lseek(fd
, kept
, SEEK_CUR
) == (off_t
) -1)
536 wrote
= write_in_full(fd
, buf
, readlen
);
541 if (kept
&& (lseek(fd
, kept
- 1, SEEK_CUR
) == (off_t
) -1 ||
542 xwrite(fd
, "", 1) != 1))