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-ll.h"
11 #include "replace-object.h"
14 typedef int (*open_istream_fn
)(struct git_istream
*,
16 const struct object_id
*,
18 typedef int (*close_istream_fn
)(struct git_istream
*);
19 typedef ssize_t (*read_istream_fn
)(struct git_istream
*, char *, size_t);
21 #define FILTER_BUFFER (1024*16)
23 struct filtered_istream
{
24 struct git_istream
*upstream
;
25 struct stream_filter
*filter
;
26 char ibuf
[FILTER_BUFFER
];
27 char obuf
[FILTER_BUFFER
];
35 close_istream_fn close
;
38 unsigned long size
; /* inflated size of full object */
40 enum { z_unused
, z_used
, z_done
, z_error
} z_state
;
44 char *buf
; /* from oid_object_info_extended() */
45 unsigned long read_ptr
;
50 unsigned long mapsize
;
57 struct packed_git
*pack
;
61 struct filtered_istream filtered
;
65 /*****************************************************************
69 *****************************************************************/
71 static void close_deflated_stream(struct git_istream
*st
)
73 if (st
->z_state
== z_used
)
74 git_inflate_end(&st
->z
);
78 /*****************************************************************
82 *****************************************************************/
84 static int close_istream_filtered(struct git_istream
*st
)
86 free_stream_filter(st
->u
.filtered
.filter
);
87 return close_istream(st
->u
.filtered
.upstream
);
90 static ssize_t
read_istream_filtered(struct git_istream
*st
, char *buf
,
93 struct filtered_istream
*fs
= &(st
->u
.filtered
);
97 /* do we already have filtered output? */
98 if (fs
->o_ptr
< fs
->o_end
) {
99 size_t to_move
= fs
->o_end
- fs
->o_ptr
;
102 memcpy(buf
+ filled
, fs
->obuf
+ fs
->o_ptr
, to_move
);
103 fs
->o_ptr
+= to_move
;
108 fs
->o_end
= fs
->o_ptr
= 0;
110 /* do we have anything to feed the filter with? */
111 if (fs
->i_ptr
< fs
->i_end
) {
112 size_t to_feed
= fs
->i_end
- fs
->i_ptr
;
113 size_t to_receive
= FILTER_BUFFER
;
114 if (stream_filter(fs
->filter
,
115 fs
->ibuf
+ fs
->i_ptr
, &to_feed
,
116 fs
->obuf
, &to_receive
))
118 fs
->i_ptr
= fs
->i_end
- to_feed
;
119 fs
->o_end
= FILTER_BUFFER
- to_receive
;
123 /* tell the filter to drain upon no more input */
124 if (fs
->input_finished
) {
125 size_t to_receive
= FILTER_BUFFER
;
126 if (stream_filter(fs
->filter
,
128 fs
->obuf
, &to_receive
))
130 fs
->o_end
= FILTER_BUFFER
- to_receive
;
135 fs
->i_end
= fs
->i_ptr
= 0;
137 /* refill the input from the upstream */
138 if (!fs
->input_finished
) {
139 fs
->i_end
= read_istream(fs
->upstream
, fs
->ibuf
, FILTER_BUFFER
);
145 fs
->input_finished
= 1;
150 static struct git_istream
*attach_stream_filter(struct git_istream
*st
,
151 struct stream_filter
*filter
)
153 struct git_istream
*ifs
= xmalloc(sizeof(*ifs
));
154 struct filtered_istream
*fs
= &(ifs
->u
.filtered
);
156 ifs
->close
= close_istream_filtered
;
157 ifs
->read
= read_istream_filtered
;
160 fs
->i_end
= fs
->i_ptr
= 0;
161 fs
->o_end
= fs
->o_ptr
= 0;
162 fs
->input_finished
= 0;
163 ifs
->size
= -1; /* unknown */
167 /*****************************************************************
169 * Loose object stream
171 *****************************************************************/
173 static ssize_t
read_istream_loose(struct git_istream
*st
, char *buf
, size_t sz
)
175 size_t total_read
= 0;
177 switch (st
->z_state
) {
186 if (st
->u
.loose
.hdr_used
< st
->u
.loose
.hdr_avail
) {
187 size_t to_copy
= st
->u
.loose
.hdr_avail
- st
->u
.loose
.hdr_used
;
190 memcpy(buf
, st
->u
.loose
.hdr
+ st
->u
.loose
.hdr_used
, to_copy
);
191 st
->u
.loose
.hdr_used
+= to_copy
;
192 total_read
+= to_copy
;
195 while (total_read
< sz
) {
198 st
->z
.next_out
= (unsigned char *)buf
+ total_read
;
199 st
->z
.avail_out
= sz
- total_read
;
200 status
= git_inflate(&st
->z
, Z_FINISH
);
202 total_read
= st
->z
.next_out
- (unsigned char *)buf
;
204 if (status
== Z_STREAM_END
) {
205 git_inflate_end(&st
->z
);
206 st
->z_state
= z_done
;
209 if (status
!= Z_OK
&& (status
!= Z_BUF_ERROR
|| total_read
< sz
)) {
210 git_inflate_end(&st
->z
);
211 st
->z_state
= z_error
;
218 static int close_istream_loose(struct git_istream
*st
)
220 close_deflated_stream(st
);
221 munmap(st
->u
.loose
.mapped
, st
->u
.loose
.mapsize
);
225 static int open_istream_loose(struct git_istream
*st
, struct repository
*r
,
226 const struct object_id
*oid
,
227 enum object_type
*type
)
229 struct object_info oi
= OBJECT_INFO_INIT
;
230 oi
.sizep
= &st
->size
;
233 st
->u
.loose
.mapped
= map_loose_object(r
, oid
, &st
->u
.loose
.mapsize
);
234 if (!st
->u
.loose
.mapped
)
236 switch (unpack_loose_header(&st
->z
, st
->u
.loose
.mapped
,
237 st
->u
.loose
.mapsize
, st
->u
.loose
.hdr
,
238 sizeof(st
->u
.loose
.hdr
), NULL
)) {
245 if (parse_loose_header(st
->u
.loose
.hdr
, &oi
) < 0 || *type
< 0)
248 st
->u
.loose
.hdr_used
= strlen(st
->u
.loose
.hdr
) + 1;
249 st
->u
.loose
.hdr_avail
= st
->z
.total_out
;
250 st
->z_state
= z_used
;
251 st
->close
= close_istream_loose
;
252 st
->read
= read_istream_loose
;
256 git_inflate_end(&st
->z
);
257 munmap(st
->u
.loose
.mapped
, st
->u
.loose
.mapsize
);
262 /*****************************************************************
264 * Non-delta packed object stream
266 *****************************************************************/
268 static ssize_t
read_istream_pack_non_delta(struct git_istream
*st
, char *buf
,
271 size_t total_read
= 0;
273 switch (st
->z_state
) {
275 memset(&st
->z
, 0, sizeof(st
->z
));
276 git_inflate_init(&st
->z
);
277 st
->z_state
= z_used
;
287 while (total_read
< sz
) {
289 struct pack_window
*window
= NULL
;
290 unsigned char *mapped
;
292 mapped
= use_pack(st
->u
.in_pack
.pack
, &window
,
293 st
->u
.in_pack
.pos
, &st
->z
.avail_in
);
295 st
->z
.next_out
= (unsigned char *)buf
+ total_read
;
296 st
->z
.avail_out
= sz
- total_read
;
297 st
->z
.next_in
= mapped
;
298 status
= git_inflate(&st
->z
, Z_FINISH
);
300 st
->u
.in_pack
.pos
+= st
->z
.next_in
- mapped
;
301 total_read
= st
->z
.next_out
- (unsigned char *)buf
;
304 if (status
== Z_STREAM_END
) {
305 git_inflate_end(&st
->z
);
306 st
->z_state
= z_done
;
311 * Unlike the loose object case, we do not have to worry here
312 * about running out of input bytes and spinning infinitely. If
313 * we get Z_BUF_ERROR due to too few input bytes, then we'll
314 * replenish them in the next use_pack() call when we loop. If
315 * we truly hit the end of the pack (i.e., because it's corrupt
316 * or truncated), then use_pack() catches that and will die().
318 if (status
!= Z_OK
&& status
!= Z_BUF_ERROR
) {
319 git_inflate_end(&st
->z
);
320 st
->z_state
= z_error
;
327 static int close_istream_pack_non_delta(struct git_istream
*st
)
329 close_deflated_stream(st
);
333 static int open_istream_pack_non_delta(struct git_istream
*st
,
334 struct repository
*r UNUSED
,
335 const struct object_id
*oid UNUSED
,
336 enum object_type
*type UNUSED
)
338 struct pack_window
*window
;
339 enum object_type in_pack_type
;
343 in_pack_type
= unpack_object_header(st
->u
.in_pack
.pack
,
348 switch (in_pack_type
) {
350 return -1; /* we do not do deltas for now */
357 st
->z_state
= z_unused
;
358 st
->close
= close_istream_pack_non_delta
;
359 st
->read
= read_istream_pack_non_delta
;
365 /*****************************************************************
369 *****************************************************************/
371 static int close_istream_incore(struct git_istream
*st
)
373 free(st
->u
.incore
.buf
);
377 static ssize_t
read_istream_incore(struct git_istream
*st
, char *buf
, size_t sz
)
379 size_t read_size
= sz
;
380 size_t remainder
= st
->size
- st
->u
.incore
.read_ptr
;
382 if (remainder
<= read_size
)
383 read_size
= remainder
;
385 memcpy(buf
, st
->u
.incore
.buf
+ st
->u
.incore
.read_ptr
, read_size
);
386 st
->u
.incore
.read_ptr
+= read_size
;
391 static int open_istream_incore(struct git_istream
*st
, struct repository
*r
,
392 const struct object_id
*oid
, enum object_type
*type
)
394 struct object_info oi
= OBJECT_INFO_INIT
;
396 st
->u
.incore
.read_ptr
= 0;
397 st
->close
= close_istream_incore
;
398 st
->read
= read_istream_incore
;
401 oi
.sizep
= &st
->size
;
402 oi
.contentp
= (void **)&st
->u
.incore
.buf
;
403 return oid_object_info_extended(r
, oid
, &oi
,
404 OBJECT_INFO_DIE_IF_CORRUPT
);
407 /*****************************************************************************
408 * static helpers variables and functions for users of streaming interface
409 *****************************************************************************/
411 static int istream_source(struct git_istream
*st
,
412 struct repository
*r
,
413 const struct object_id
*oid
,
414 enum object_type
*type
)
418 struct object_info oi
= OBJECT_INFO_INIT
;
422 status
= oid_object_info_extended(r
, oid
, &oi
, 0);
428 st
->open
= open_istream_loose
;
431 if (!oi
.u
.packed
.is_delta
&& big_file_threshold
< size
) {
432 st
->u
.in_pack
.pack
= oi
.u
.packed
.pack
;
433 st
->u
.in_pack
.pos
= oi
.u
.packed
.offset
;
434 st
->open
= open_istream_pack_non_delta
;
439 st
->open
= open_istream_incore
;
444 /****************************************************************
445 * Users of streaming interface
446 ****************************************************************/
448 int close_istream(struct git_istream
*st
)
450 int r
= st
->close(st
);
455 ssize_t
read_istream(struct git_istream
*st
, void *buf
, size_t sz
)
457 return st
->read(st
, buf
, sz
);
460 struct git_istream
*open_istream(struct repository
*r
,
461 const struct object_id
*oid
,
462 enum object_type
*type
,
464 struct stream_filter
*filter
)
466 struct git_istream
*st
= xmalloc(sizeof(*st
));
467 const struct object_id
*real
= lookup_replace_object(r
, oid
);
468 int ret
= istream_source(st
, r
, real
, type
);
475 if (st
->open(st
, r
, real
, type
)) {
476 if (open_istream_incore(st
, r
, real
, type
)) {
482 /* Add "&& !is_null_stream_filter(filter)" for performance */
483 struct git_istream
*nst
= attach_stream_filter(st
, filter
);
495 int stream_blob_to_fd(int fd
, const struct object_id
*oid
, struct stream_filter
*filter
,
498 struct git_istream
*st
;
499 enum object_type type
;
504 st
= open_istream(the_repository
, oid
, &type
, &sz
, filter
);
507 free_stream_filter(filter
);
510 if (type
!= OBJ_BLOB
)
514 ssize_t wrote
, holeto
;
515 ssize_t readlen
= read_istream(st
, buf
, sizeof(buf
));
521 if (can_seek
&& sizeof(buf
) == readlen
) {
522 for (holeto
= 0; holeto
< readlen
; holeto
++)
525 if (readlen
== holeto
) {
531 if (kept
&& lseek(fd
, kept
, SEEK_CUR
) == (off_t
) -1)
535 wrote
= write_in_full(fd
, buf
, readlen
);
540 if (kept
&& (lseek(fd
, kept
- 1, SEEK_CUR
) == (off_t
) -1 ||
541 xwrite(fd
, "", 1) != 1))