Coverage Report

Created: 2026-02-14 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/odb/streaming.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2011, Google Inc.
3
 */
4
5
#include "git-compat-util.h"
6
#include "convert.h"
7
#include "environment.h"
8
#include "repository.h"
9
#include "object-file.h"
10
#include "odb.h"
11
#include "odb/streaming.h"
12
#include "replace-object.h"
13
#include "packfile.h"
14
15
0
#define FILTER_BUFFER (1024*16)
16
17
/*****************************************************************
18
 *
19
 * Filtered stream
20
 *
21
 *****************************************************************/
22
23
struct odb_filtered_read_stream {
24
  struct odb_read_stream base;
25
  struct odb_read_stream *upstream;
26
  struct stream_filter *filter;
27
  char ibuf[FILTER_BUFFER];
28
  char obuf[FILTER_BUFFER];
29
  int i_end, i_ptr;
30
  int o_end, o_ptr;
31
  int input_finished;
32
};
33
34
static int close_istream_filtered(struct odb_read_stream *_fs)
35
0
{
36
0
  struct odb_filtered_read_stream *fs = (struct odb_filtered_read_stream *)_fs;
37
0
  free_stream_filter(fs->filter);
38
0
  return odb_read_stream_close(fs->upstream);
39
0
}
40
41
static ssize_t read_istream_filtered(struct odb_read_stream *_fs, char *buf,
42
             size_t sz)
43
0
{
44
0
  struct odb_filtered_read_stream *fs = (struct odb_filtered_read_stream *)_fs;
45
0
  size_t filled = 0;
46
47
0
  while (sz) {
48
    /* do we already have filtered output? */
49
0
    if (fs->o_ptr < fs->o_end) {
50
0
      size_t to_move = fs->o_end - fs->o_ptr;
51
0
      if (sz < to_move)
52
0
        to_move = sz;
53
0
      memcpy(buf + filled, fs->obuf + fs->o_ptr, to_move);
54
0
      fs->o_ptr += to_move;
55
0
      sz -= to_move;
56
0
      filled += to_move;
57
0
      continue;
58
0
    }
59
0
    fs->o_end = fs->o_ptr = 0;
60
61
    /* do we have anything to feed the filter with? */
62
0
    if (fs->i_ptr < fs->i_end) {
63
0
      size_t to_feed = fs->i_end - fs->i_ptr;
64
0
      size_t to_receive = FILTER_BUFFER;
65
0
      if (stream_filter(fs->filter,
66
0
            fs->ibuf + fs->i_ptr, &to_feed,
67
0
            fs->obuf, &to_receive))
68
0
        return -1;
69
0
      fs->i_ptr = fs->i_end - to_feed;
70
0
      fs->o_end = FILTER_BUFFER - to_receive;
71
0
      continue;
72
0
    }
73
74
    /* tell the filter to drain upon no more input */
75
0
    if (fs->input_finished) {
76
0
      size_t to_receive = FILTER_BUFFER;
77
0
      if (stream_filter(fs->filter,
78
0
            NULL, NULL,
79
0
            fs->obuf, &to_receive))
80
0
        return -1;
81
0
      fs->o_end = FILTER_BUFFER - to_receive;
82
0
      if (!fs->o_end)
83
0
        break;
84
0
      continue;
85
0
    }
86
0
    fs->i_end = fs->i_ptr = 0;
87
88
    /* refill the input from the upstream */
89
0
    if (!fs->input_finished) {
90
0
      fs->i_end = odb_read_stream_read(fs->upstream, fs->ibuf, FILTER_BUFFER);
91
0
      if (fs->i_end < 0)
92
0
        return -1;
93
0
      if (fs->i_end)
94
0
        continue;
95
0
    }
96
0
    fs->input_finished = 1;
97
0
  }
98
0
  return filled;
99
0
}
100
101
static struct odb_read_stream *attach_stream_filter(struct odb_read_stream *st,
102
                struct stream_filter *filter)
103
0
{
104
0
  struct odb_filtered_read_stream *fs;
105
106
0
  CALLOC_ARRAY(fs, 1);
107
0
  fs->base.close = close_istream_filtered;
108
0
  fs->base.read = read_istream_filtered;
109
0
  fs->upstream = st;
110
0
  fs->filter = filter;
111
0
  fs->base.size = -1; /* unknown */
112
0
  fs->base.type = st->type;
113
114
0
  return &fs->base;
115
0
}
116
117
/*****************************************************************
118
 *
119
 * In-core stream
120
 *
121
 *****************************************************************/
122
123
struct odb_incore_read_stream {
124
  struct odb_read_stream base;
125
  char *buf; /* from odb_read_object_info_extended() */
126
  unsigned long read_ptr;
127
};
128
129
static int close_istream_incore(struct odb_read_stream *_st)
130
0
{
131
0
  struct odb_incore_read_stream *st = (struct odb_incore_read_stream *)_st;
132
0
  free(st->buf);
133
0
  return 0;
134
0
}
135
136
static ssize_t read_istream_incore(struct odb_read_stream *_st, char *buf, size_t sz)
137
0
{
138
0
  struct odb_incore_read_stream *st = (struct odb_incore_read_stream *)_st;
139
0
  size_t read_size = sz;
140
0
  size_t remainder = st->base.size - st->read_ptr;
141
142
0
  if (remainder <= read_size)
143
0
    read_size = remainder;
144
0
  if (read_size) {
145
0
    memcpy(buf, st->buf + st->read_ptr, read_size);
146
0
    st->read_ptr += read_size;
147
0
  }
148
0
  return read_size;
149
0
}
150
151
static int open_istream_incore(struct odb_read_stream **out,
152
             struct object_database *odb,
153
             const struct object_id *oid)
154
0
{
155
0
  struct object_info oi = OBJECT_INFO_INIT;
156
0
  struct odb_incore_read_stream stream = {
157
0
    .base.close = close_istream_incore,
158
0
    .base.read = read_istream_incore,
159
0
  };
160
0
  struct odb_incore_read_stream *st;
161
0
  int ret;
162
163
0
  oi.typep = &stream.base.type;
164
0
  oi.sizep = &stream.base.size;
165
0
  oi.contentp = (void **)&stream.buf;
166
0
  ret = odb_read_object_info_extended(odb, oid, &oi,
167
0
              OBJECT_INFO_DIE_IF_CORRUPT);
168
0
  if (ret)
169
0
    return ret;
170
171
0
  CALLOC_ARRAY(st, 1);
172
0
  *st = stream;
173
0
  *out = &st->base;
174
175
0
  return 0;
176
0
}
177
178
/*****************************************************************************
179
 * static helpers variables and functions for users of streaming interface
180
 *****************************************************************************/
181
182
static int istream_source(struct odb_read_stream **out,
183
        struct object_database *odb,
184
        const struct object_id *oid)
185
0
{
186
0
  struct odb_source *source;
187
188
0
  odb_prepare_alternates(odb);
189
0
  for (source = odb->sources; source; source = source->next) {
190
0
    if (!packfile_store_read_object_stream(out, source->packfiles, oid) ||
191
0
        !odb_source_loose_read_object_stream(out, source, oid))
192
0
      return 0;
193
0
  }
194
195
0
  return open_istream_incore(out, odb, oid);
196
0
}
197
198
/****************************************************************
199
 * Users of streaming interface
200
 ****************************************************************/
201
202
int odb_read_stream_close(struct odb_read_stream *st)
203
0
{
204
0
  int r = st->close(st);
205
0
  free(st);
206
0
  return r;
207
0
}
208
209
ssize_t odb_read_stream_read(struct odb_read_stream *st, void *buf, size_t sz)
210
0
{
211
0
  return st->read(st, buf, sz);
212
0
}
213
214
struct odb_read_stream *odb_read_stream_open(struct object_database *odb,
215
               const struct object_id *oid,
216
               struct stream_filter *filter)
217
0
{
218
0
  struct odb_read_stream *st;
219
0
  const struct object_id *real = lookup_replace_object(odb->repo, oid);
220
0
  int ret = istream_source(&st, odb, real);
221
222
0
  if (ret)
223
0
    return NULL;
224
225
0
  if (filter) {
226
    /* Add "&& !is_null_stream_filter(filter)" for performance */
227
0
    struct odb_read_stream *nst = attach_stream_filter(st, filter);
228
0
    if (!nst) {
229
0
      odb_read_stream_close(st);
230
0
      return NULL;
231
0
    }
232
0
    st = nst;
233
0
  }
234
235
0
  return st;
236
0
}
237
238
int odb_stream_blob_to_fd(struct object_database *odb,
239
        int fd,
240
        const struct object_id *oid,
241
        struct stream_filter *filter,
242
        int can_seek)
243
0
{
244
0
  struct odb_read_stream *st;
245
0
  ssize_t kept = 0;
246
0
  int result = -1;
247
248
0
  st = odb_read_stream_open(odb, oid, filter);
249
0
  if (!st) {
250
0
    if (filter)
251
0
      free_stream_filter(filter);
252
0
    return result;
253
0
  }
254
0
  if (st->type != OBJ_BLOB)
255
0
    goto close_and_exit;
256
0
  for (;;) {
257
0
    char buf[1024 * 16];
258
0
    ssize_t wrote, holeto;
259
0
    ssize_t readlen = odb_read_stream_read(st, buf, sizeof(buf));
260
261
0
    if (readlen < 0)
262
0
      goto close_and_exit;
263
0
    if (!readlen)
264
0
      break;
265
0
    if (can_seek && sizeof(buf) == readlen) {
266
0
      for (holeto = 0; holeto < readlen; holeto++)
267
0
        if (buf[holeto])
268
0
          break;
269
0
      if (readlen == holeto) {
270
0
        kept += holeto;
271
0
        continue;
272
0
      }
273
0
    }
274
275
0
    if (kept && lseek(fd, kept, SEEK_CUR) == (off_t) -1)
276
0
      goto close_and_exit;
277
0
    else
278
0
      kept = 0;
279
0
    wrote = write_in_full(fd, buf, readlen);
280
281
0
    if (wrote < 0)
282
0
      goto close_and_exit;
283
0
  }
284
0
  if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
285
0
         xwrite(fd, "", 1) != 1))
286
0
    goto close_and_exit;
287
0
  result = 0;
288
289
0
 close_and_exit:
290
0
  odb_read_stream_close(st);
291
0
  return result;
292
0
}