Coverage Report

Created: 2025-12-31 07:01

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
  if (!packfile_store_read_object_stream(out, odb->packfiles, oid))
189
0
    return 0;
190
191
0
  odb_prepare_alternates(odb);
192
0
  for (source = odb->sources; source; source = source->next)
193
0
    if (!odb_source_loose_read_object_stream(out, source, oid))
194
0
      return 0;
195
196
0
  return open_istream_incore(out, odb, oid);
197
0
}
198
199
/****************************************************************
200
 * Users of streaming interface
201
 ****************************************************************/
202
203
int odb_read_stream_close(struct odb_read_stream *st)
204
0
{
205
0
  int r = st->close(st);
206
0
  free(st);
207
0
  return r;
208
0
}
209
210
ssize_t odb_read_stream_read(struct odb_read_stream *st, void *buf, size_t sz)
211
0
{
212
0
  return st->read(st, buf, sz);
213
0
}
214
215
struct odb_read_stream *odb_read_stream_open(struct object_database *odb,
216
               const struct object_id *oid,
217
               struct stream_filter *filter)
218
0
{
219
0
  struct odb_read_stream *st;
220
0
  const struct object_id *real = lookup_replace_object(odb->repo, oid);
221
0
  int ret = istream_source(&st, odb, real);
222
223
0
  if (ret)
224
0
    return NULL;
225
226
0
  if (filter) {
227
    /* Add "&& !is_null_stream_filter(filter)" for performance */
228
0
    struct odb_read_stream *nst = attach_stream_filter(st, filter);
229
0
    if (!nst) {
230
0
      odb_read_stream_close(st);
231
0
      return NULL;
232
0
    }
233
0
    st = nst;
234
0
  }
235
236
0
  return st;
237
0
}
238
239
int odb_stream_blob_to_fd(struct object_database *odb,
240
        int fd,
241
        const struct object_id *oid,
242
        struct stream_filter *filter,
243
        int can_seek)
244
0
{
245
0
  struct odb_read_stream *st;
246
0
  ssize_t kept = 0;
247
0
  int result = -1;
248
249
0
  st = odb_read_stream_open(odb, oid, filter);
250
0
  if (!st) {
251
0
    if (filter)
252
0
      free_stream_filter(filter);
253
0
    return result;
254
0
  }
255
0
  if (st->type != OBJ_BLOB)
256
0
    goto close_and_exit;
257
0
  for (;;) {
258
0
    char buf[1024 * 16];
259
0
    ssize_t wrote, holeto;
260
0
    ssize_t readlen = odb_read_stream_read(st, buf, sizeof(buf));
261
262
0
    if (readlen < 0)
263
0
      goto close_and_exit;
264
0
    if (!readlen)
265
0
      break;
266
0
    if (can_seek && sizeof(buf) == readlen) {
267
0
      for (holeto = 0; holeto < readlen; holeto++)
268
0
        if (buf[holeto])
269
0
          break;
270
0
      if (readlen == holeto) {
271
0
        kept += holeto;
272
0
        continue;
273
0
      }
274
0
    }
275
276
0
    if (kept && lseek(fd, kept, SEEK_CUR) == (off_t) -1)
277
0
      goto close_and_exit;
278
0
    else
279
0
      kept = 0;
280
0
    wrote = write_in_full(fd, buf, readlen);
281
282
0
    if (wrote < 0)
283
0
      goto close_and_exit;
284
0
  }
285
0
  if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
286
0
         xwrite(fd, "", 1) != 1))
287
0
    goto close_and_exit;
288
0
  result = 0;
289
290
0
 close_and_exit:
291
0
  odb_read_stream_close(st);
292
0
  return result;
293
0
}