Coverage Report

Created: 2026-10-17 07:15

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