2 * Copyright (c) 2011, Google Inc.
6 #include "repository.h"
7 #include "object-store.h"
8 #include "replace-object.h"
11 typedef int (*open_istream_fn
)(struct git_istream
*,
13 const struct object_id
*,
15 typedef int (*close_istream_fn
)(struct git_istream
*);
16 typedef ssize_t (*read_istream_fn
)(struct git_istream
*, char *, size_t);
18 #define FILTER_BUFFER (1024*16)
20 struct filtered_istream
{
21 struct git_istream
*upstream
;
22 struct stream_filter
*filter
;
23 char ibuf
[FILTER_BUFFER
];
24 char obuf
[FILTER_BUFFER
];
32 close_istream_fn close
;
35 unsigned long size
; /* inflated size of full object */
37 enum { z_unused
, z_used
, z_done
, z_error
} z_state
;
41 char *buf
; /* from oid_object_info_extended() */
42 unsigned long read_ptr
;
47 unsigned long mapsize
;
54 struct packed_git
*pack
;
58 struct filtered_istream filtered
;
62 /*****************************************************************
66 *****************************************************************/
68 static void close_deflated_stream(struct git_istream
*st
)
70 if (st
->z_state
== z_used
)
71 git_inflate_end(&st
->z
);
75 /*****************************************************************
79 *****************************************************************/
81 static int close_istream_filtered(struct git_istream
*st
)
83 free_stream_filter(st
->u
.filtered
.filter
);
84 return close_istream(st
->u
.filtered
.upstream
);
87 static ssize_t
read_istream_filtered(struct git_istream
*st
, char *buf
,
90 struct filtered_istream
*fs
= &(st
->u
.filtered
);
94 /* do we already have filtered output? */
95 if (fs
->o_ptr
< fs
->o_end
) {
96 size_t to_move
= fs
->o_end
- fs
->o_ptr
;
99 memcpy(buf
+ filled
, fs
->obuf
+ fs
->o_ptr
, to_move
);
100 fs
->o_ptr
+= to_move
;
105 fs
->o_end
= fs
->o_ptr
= 0;
107 /* do we have anything to feed the filter with? */
108 if (fs
->i_ptr
< fs
->i_end
) {
109 size_t to_feed
= fs
->i_end
- fs
->i_ptr
;
110 size_t to_receive
= FILTER_BUFFER
;
111 if (stream_filter(fs
->filter
,
112 fs
->ibuf
+ fs
->i_ptr
, &to_feed
,
113 fs
->obuf
, &to_receive
))
115 fs
->i_ptr
= fs
->i_end
- to_feed
;
116 fs
->o_end
= FILTER_BUFFER
- to_receive
;
120 /* tell the filter to drain upon no more input */
121 if (fs
->input_finished
) {
122 size_t to_receive
= FILTER_BUFFER
;
123 if (stream_filter(fs
->filter
,
125 fs
->obuf
, &to_receive
))
127 fs
->o_end
= FILTER_BUFFER
- to_receive
;
132 fs
->i_end
= fs
->i_ptr
= 0;
134 /* refill the input from the upstream */
135 if (!fs
->input_finished
) {
136 fs
->i_end
= read_istream(fs
->upstream
, fs
->ibuf
, FILTER_BUFFER
);
142 fs
->input_finished
= 1;
147 static struct git_istream
*attach_stream_filter(struct git_istream
*st
,
148 struct stream_filter
*filter
)
150 struct git_istream
*ifs
= xmalloc(sizeof(*ifs
));
151 struct filtered_istream
*fs
= &(ifs
->u
.filtered
);
153 ifs
->close
= close_istream_filtered
;
154 ifs
->read
= read_istream_filtered
;
157 fs
->i_end
= fs
->i_ptr
= 0;
158 fs
->o_end
= fs
->o_ptr
= 0;
159 fs
->input_finished
= 0;
160 ifs
->size
= -1; /* unknown */
164 /*****************************************************************
166 * Loose object stream
168 *****************************************************************/
170 static ssize_t
read_istream_loose(struct git_istream
*st
, char *buf
, size_t sz
)
172 size_t total_read
= 0;
174 switch (st
->z_state
) {
183 if (st
->u
.loose
.hdr_used
< st
->u
.loose
.hdr_avail
) {
184 size_t to_copy
= st
->u
.loose
.hdr_avail
- st
->u
.loose
.hdr_used
;
187 memcpy(buf
, st
->u
.loose
.hdr
+ st
->u
.loose
.hdr_used
, to_copy
);
188 st
->u
.loose
.hdr_used
+= to_copy
;
189 total_read
+= to_copy
;
192 while (total_read
< sz
) {
195 st
->z
.next_out
= (unsigned char *)buf
+ total_read
;
196 st
->z
.avail_out
= sz
- total_read
;
197 status
= git_inflate(&st
->z
, Z_FINISH
);
199 total_read
= st
->z
.next_out
- (unsigned char *)buf
;
201 if (status
== Z_STREAM_END
) {
202 git_inflate_end(&st
->z
);
203 st
->z_state
= z_done
;
206 if (status
!= Z_OK
&& (status
!= Z_BUF_ERROR
|| total_read
< sz
)) {
207 git_inflate_end(&st
->z
);
208 st
->z_state
= z_error
;
215 static int close_istream_loose(struct git_istream
*st
)
217 close_deflated_stream(st
);
218 munmap(st
->u
.loose
.mapped
, st
->u
.loose
.mapsize
);
222 static int open_istream_loose(struct git_istream
*st
, struct repository
*r
,
223 const struct object_id
*oid
,
224 enum object_type
*type
)
226 struct object_info oi
= OBJECT_INFO_INIT
;
227 oi
.sizep
= &st
->size
;
230 st
->u
.loose
.mapped
= map_loose_object(r
, oid
, &st
->u
.loose
.mapsize
);
231 if (!st
->u
.loose
.mapped
)
233 switch (unpack_loose_header(&st
->z
, st
->u
.loose
.mapped
,
234 st
->u
.loose
.mapsize
, st
->u
.loose
.hdr
,
235 sizeof(st
->u
.loose
.hdr
), NULL
)) {
242 if (parse_loose_header(st
->u
.loose
.hdr
, &oi
) < 0 || *type
< 0)
245 st
->u
.loose
.hdr_used
= strlen(st
->u
.loose
.hdr
) + 1;
246 st
->u
.loose
.hdr_avail
= st
->z
.total_out
;
247 st
->z_state
= z_used
;
248 st
->close
= close_istream_loose
;
249 st
->read
= read_istream_loose
;
253 git_inflate_end(&st
->z
);
254 munmap(st
->u
.loose
.mapped
, st
->u
.loose
.mapsize
);
259 /*****************************************************************
261 * Non-delta packed object stream
263 *****************************************************************/
265 static ssize_t
read_istream_pack_non_delta(struct git_istream
*st
, char *buf
,
268 size_t total_read
= 0;
270 switch (st
->z_state
) {
272 memset(&st
->z
, 0, sizeof(st
->z
));
273 git_inflate_init(&st
->z
);
274 st
->z_state
= z_used
;
284 while (total_read
< sz
) {
286 struct pack_window
*window
= NULL
;
287 unsigned char *mapped
;
289 mapped
= use_pack(st
->u
.in_pack
.pack
, &window
,
290 st
->u
.in_pack
.pos
, &st
->z
.avail_in
);
292 st
->z
.next_out
= (unsigned char *)buf
+ total_read
;
293 st
->z
.avail_out
= sz
- total_read
;
294 st
->z
.next_in
= mapped
;
295 status
= git_inflate(&st
->z
, Z_FINISH
);
297 st
->u
.in_pack
.pos
+= st
->z
.next_in
- mapped
;
298 total_read
= st
->z
.next_out
- (unsigned char *)buf
;
301 if (status
== Z_STREAM_END
) {
302 git_inflate_end(&st
->z
);
303 st
->z_state
= z_done
;
308 * Unlike the loose object case, we do not have to worry here
309 * about running out of input bytes and spinning infinitely. If
310 * we get Z_BUF_ERROR due to too few input bytes, then we'll
311 * replenish them in the next use_pack() call when we loop. If
312 * we truly hit the end of the pack (i.e., because it's corrupt
313 * or truncated), then use_pack() catches that and will die().
315 if (status
!= Z_OK
&& status
!= Z_BUF_ERROR
) {
316 git_inflate_end(&st
->z
);
317 st
->z_state
= z_error
;
324 static int close_istream_pack_non_delta(struct git_istream
*st
)
326 close_deflated_stream(st
);
330 static int open_istream_pack_non_delta(struct git_istream
*st
,
331 struct repository
*r UNUSED
,
332 const struct object_id
*oid UNUSED
,
333 enum object_type
*type UNUSED
)
335 struct pack_window
*window
;
336 enum object_type in_pack_type
;
340 in_pack_type
= unpack_object_header(st
->u
.in_pack
.pack
,
345 switch (in_pack_type
) {
347 return -1; /* we do not do deltas for now */
354 st
->z_state
= z_unused
;
355 st
->close
= close_istream_pack_non_delta
;
356 st
->read
= read_istream_pack_non_delta
;
362 /*****************************************************************
366 *****************************************************************/
368 static int close_istream_incore(struct git_istream
*st
)
370 free(st
->u
.incore
.buf
);
374 static ssize_t
read_istream_incore(struct git_istream
*st
, char *buf
, size_t sz
)
376 size_t read_size
= sz
;
377 size_t remainder
= st
->size
- st
->u
.incore
.read_ptr
;
379 if (remainder
<= read_size
)
380 read_size
= remainder
;
382 memcpy(buf
, st
->u
.incore
.buf
+ st
->u
.incore
.read_ptr
, read_size
);
383 st
->u
.incore
.read_ptr
+= read_size
;
388 static int open_istream_incore(struct git_istream
*st
, struct repository
*r
,
389 const struct object_id
*oid
, enum object_type
*type
)
391 struct object_info oi
= OBJECT_INFO_INIT
;
393 st
->u
.incore
.read_ptr
= 0;
394 st
->close
= close_istream_incore
;
395 st
->read
= read_istream_incore
;
398 oi
.sizep
= &st
->size
;
399 oi
.contentp
= (void **)&st
->u
.incore
.buf
;
400 return oid_object_info_extended(r
, oid
, &oi
,
401 OBJECT_INFO_DIE_IF_CORRUPT
);
404 /*****************************************************************************
405 * static helpers variables and functions for users of streaming interface
406 *****************************************************************************/
408 static int istream_source(struct git_istream
*st
,
409 struct repository
*r
,
410 const struct object_id
*oid
,
411 enum object_type
*type
)
415 struct object_info oi
= OBJECT_INFO_INIT
;
419 status
= oid_object_info_extended(r
, oid
, &oi
, 0);
425 st
->open
= open_istream_loose
;
428 if (!oi
.u
.packed
.is_delta
&& big_file_threshold
< size
) {
429 st
->u
.in_pack
.pack
= oi
.u
.packed
.pack
;
430 st
->u
.in_pack
.pos
= oi
.u
.packed
.offset
;
431 st
->open
= open_istream_pack_non_delta
;
436 st
->open
= open_istream_incore
;
441 /****************************************************************
442 * Users of streaming interface
443 ****************************************************************/
445 int close_istream(struct git_istream
*st
)
447 int r
= st
->close(st
);
452 ssize_t
read_istream(struct git_istream
*st
, void *buf
, size_t sz
)
454 return st
->read(st
, buf
, sz
);
457 struct git_istream
*open_istream(struct repository
*r
,
458 const struct object_id
*oid
,
459 enum object_type
*type
,
461 struct stream_filter
*filter
)
463 struct git_istream
*st
= xmalloc(sizeof(*st
));
464 const struct object_id
*real
= lookup_replace_object(r
, oid
);
465 int ret
= istream_source(st
, r
, real
, type
);
472 if (st
->open(st
, r
, real
, type
)) {
473 if (open_istream_incore(st
, r
, real
, type
)) {
479 /* Add "&& !is_null_stream_filter(filter)" for performance */
480 struct git_istream
*nst
= attach_stream_filter(st
, filter
);
492 int stream_blob_to_fd(int fd
, const struct object_id
*oid
, struct stream_filter
*filter
,
495 struct git_istream
*st
;
496 enum object_type type
;
501 st
= open_istream(the_repository
, oid
, &type
, &sz
, filter
);
504 free_stream_filter(filter
);
507 if (type
!= OBJ_BLOB
)
511 ssize_t wrote
, holeto
;
512 ssize_t readlen
= read_istream(st
, buf
, sizeof(buf
));
518 if (can_seek
&& sizeof(buf
) == readlen
) {
519 for (holeto
= 0; holeto
< readlen
; holeto
++)
522 if (readlen
== holeto
) {
528 if (kept
&& lseek(fd
, kept
, SEEK_CUR
) == (off_t
) -1)
532 wrote
= write_in_full(fd
, buf
, readlen
);
537 if (kept
&& (lseek(fd
, kept
- 1, SEEK_CUR
) == (off_t
) -1 ||
538 xwrite(fd
, "", 1) != 1))