Coverage Report

Created: 2026-11-12 06:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/odb/source-inmemory.c
Line
Count
Source
1
#include "git-compat-util.h"
2
#include "object-file.h"
3
#include "odb.h"
4
#include "fsck.h"
5
#include "odb/source-inmemory.h"
6
#include "odb/streaming.h"
7
#include "oidtree.h"
8
#include "repository.h"
9
10
struct inmemory_object {
11
  enum object_type type;
12
  const void *buf;
13
  unsigned long size;
14
};
15
16
static const struct inmemory_object *find_cached_object(struct odb_source_inmemory *source,
17
              const struct object_id *oid)
18
0
{
19
0
  static const struct inmemory_object empty_tree = {
20
0
    .type = OBJ_TREE,
21
0
    .buf = "",
22
0
  };
23
0
  const struct inmemory_object *object;
24
25
0
  if (source->objects) {
26
0
    object = oidtree_get(source->objects, oid);
27
0
    if (object)
28
0
      return object;
29
0
  }
30
31
0
  if (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree))
32
0
    return &empty_tree;
33
34
0
  return NULL;
35
0
}
36
37
static void populate_object_info(struct odb_source_inmemory *source,
38
         struct object_info *oi,
39
         const struct inmemory_object *object)
40
0
{
41
0
  if (!oi)
42
0
    return;
43
44
0
  if (oi->typep)
45
0
    *(oi->typep) = object->type;
46
0
  if (oi->sizep)
47
0
    *(oi->sizep) = object->size;
48
0
  if (oi->disk_sizep)
49
0
    *(oi->disk_sizep) = 0;
50
0
  if (oi->delta_base_oid)
51
0
    oidclr(oi->delta_base_oid, source->base.odb->repo->hash_algo);
52
0
  if (oi->contentp)
53
0
    *oi->contentp = xmemdupz(object->buf, object->size);
54
0
  if (oi->mtimep)
55
0
    *oi->mtimep = 0;
56
0
  if (oi->source_infop)
57
0
    oi->source_infop->source = &source->base;
58
0
}
59
60
static enum odb_read_status odb_source_inmemory_read_object_info(struct odb_source *source,
61
                 const struct object_id *oid,
62
                 struct object_info *oi,
63
                 enum object_info_flags flags UNUSED,
64
                 struct strbuf *errmsg UNUSED)
65
0
{
66
0
  struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
67
0
  const struct inmemory_object *object;
68
69
0
  object = find_cached_object(inmemory, oid);
70
0
  if (!object)
71
0
    return ODB_READ_NOT_FOUND;
72
73
0
  populate_object_info(inmemory, oi, object);
74
0
  return 0;
75
0
}
76
77
struct odb_read_stream_inmemory {
78
  struct odb_stream base;
79
  const unsigned char *buf;
80
  size_t offset;
81
};
82
83
static ssize_t odb_read_stream_inmemory_read(struct odb_stream *stream,
84
               char *buf, size_t buf_len)
85
0
{
86
0
  struct odb_read_stream_inmemory *inmemory =
87
0
    container_of(stream, struct odb_read_stream_inmemory, base);
88
0
  size_t bytes = buf_len;
89
90
0
  if (buf_len > inmemory->base.size - inmemory->offset)
91
0
    bytes = inmemory->base.size - inmemory->offset;
92
93
0
  memcpy(buf, inmemory->buf + inmemory->offset, bytes);
94
0
  inmemory->offset += bytes;
95
96
0
  return bytes;
97
0
}
98
99
static int odb_read_stream_inmemory_close(struct odb_stream *stream UNUSED)
100
0
{
101
0
  return 0;
102
0
}
103
104
static int odb_source_inmemory_read_object_stream(struct odb_stream **out,
105
              struct odb_source *source,
106
              const struct object_id *oid)
107
0
{
108
0
  struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
109
0
  struct odb_read_stream_inmemory *stream;
110
0
  const struct inmemory_object *object;
111
112
0
  object = find_cached_object(inmemory, oid);
113
0
  if (!object)
114
0
    return -1;
115
116
0
  CALLOC_ARRAY(stream, 1);
117
0
  stream->base.read = odb_read_stream_inmemory_read;
118
0
  stream->base.close = odb_read_stream_inmemory_close;
119
0
  stream->base.size = object->size;
120
0
  stream->base.type = object->type;
121
0
  stream->buf = object->buf;
122
123
0
  *out = &stream->base;
124
0
  return 0;
125
0
}
126
127
struct odb_source_inmemory_for_each_object_data {
128
  struct odb_source_inmemory *inmemory;
129
  const struct object_info *request;
130
  odb_for_each_object_cb cb;
131
  void *cb_data;
132
};
133
134
static int odb_source_inmemory_for_each_object_cb(const struct object_id *oid,
135
              void *node_data, void *cb_data)
136
0
{
137
0
  struct odb_source_inmemory_for_each_object_data *data = cb_data;
138
0
  struct inmemory_object *object = node_data;
139
140
0
  if (data->request) {
141
0
    struct object_info oi = *data->request;
142
0
    populate_object_info(data->inmemory, &oi, object);
143
0
    return data->cb(oid, &oi, data->cb_data);
144
0
  } else {
145
0
    return data->cb(oid, NULL, data->cb_data);
146
0
  }
147
0
}
148
149
static int odb_source_inmemory_for_each_object(struct odb_source *source,
150
                 const struct object_info *request,
151
                 odb_for_each_object_cb cb,
152
                 void *cb_data,
153
                 const struct odb_for_each_object_options *opts)
154
0
{
155
0
  struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
156
0
  struct odb_source_inmemory_for_each_object_data payload = {
157
0
    .inmemory = inmemory,
158
0
    .request = request,
159
0
    .cb = cb,
160
0
    .cb_data = cb_data,
161
0
  };
162
0
  struct object_id null_oid = { 0 };
163
164
  /*
165
   * In-memory objects are neither promisor objects nor do they live in
166
   * the local object directory, so there is nothing to iterate over.
167
   */
168
0
  if (opts->flags & (ODB_FOR_EACH_OBJECT_PROMISOR_ONLY |
169
0
         ODB_FOR_EACH_OBJECT_LOCAL_ONLY))
170
0
    return 0;
171
0
  if (!inmemory->objects)
172
0
    return 0;
173
174
0
  return oidtree_each(inmemory->objects,
175
0
          opts->prefix ? opts->prefix : &null_oid, opts->prefix_hex_len,
176
0
          odb_source_inmemory_for_each_object_cb, &payload);
177
0
}
178
179
struct find_abbrev_len_data {
180
  const struct object_id *oid;
181
  unsigned len;
182
};
183
184
static int find_abbrev_len_cb(const struct object_id *oid,
185
            struct object_info *oi UNUSED,
186
            void *cb_data)
187
0
{
188
0
  struct find_abbrev_len_data *data = cb_data;
189
0
  unsigned len = oid_common_prefix_hexlen(oid, data->oid);
190
0
  if (len != hash_algos[oid->algo].hexsz && len >= data->len)
191
0
    data->len = len + 1;
192
0
  return 0;
193
0
}
194
195
static int odb_source_inmemory_find_abbrev_len(struct odb_source *source,
196
                 const struct object_id *oid,
197
                 unsigned min_len,
198
                 unsigned *out)
199
0
{
200
0
  struct odb_for_each_object_options opts = {
201
0
    .prefix = oid,
202
0
    .prefix_hex_len = min_len,
203
0
  };
204
0
  struct find_abbrev_len_data data = {
205
0
    .oid = oid,
206
0
    .len = min_len,
207
0
  };
208
0
  int ret;
209
210
0
  ret = odb_source_inmemory_for_each_object(source, NULL, find_abbrev_len_cb,
211
0
              &data, &opts);
212
0
  *out = data.len;
213
214
0
  return ret;
215
0
}
216
217
static int count_objects_cb(const struct object_id *oid UNUSED,
218
          struct object_info *oi UNUSED,
219
          void *cb_data)
220
0
{
221
0
  unsigned long *counter = cb_data;
222
0
  (*counter)++;
223
0
  return 0;
224
0
}
225
226
static int odb_source_inmemory_count_objects(struct odb_source *source,
227
               enum odb_count_objects_flags flags UNUSED,
228
               unsigned long *out)
229
0
{
230
0
  struct odb_for_each_object_options opts = { 0 };
231
0
  *out = 0;
232
0
  return odb_source_inmemory_for_each_object(source, NULL, count_objects_cb,
233
0
               out, &opts);
234
0
}
235
236
static int odb_source_inmemory_write_object(struct odb_source *source,
237
              const void *buf, size_t len,
238
              enum object_type type,
239
              const struct object_id *oid,
240
              const struct object_id *compat_oid UNUSED,
241
              const time_t *mtime UNUSED,
242
              enum odb_write_object_flags flags UNUSED)
243
0
{
244
0
  struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
245
0
  struct inmemory_object *object;
246
247
0
  if (!inmemory->objects) {
248
0
    CALLOC_ARRAY(inmemory->objects, 1);
249
0
    oidtree_init(inmemory->objects);
250
0
  } else if (oidtree_contains(inmemory->objects, oid)) {
251
0
    return 0;
252
0
  }
253
254
0
  CALLOC_ARRAY(object, 1);
255
0
  object->size = len;
256
0
  object->type = type;
257
0
  object->buf = xmemdupz(buf, len);
258
259
0
  oidtree_insert(inmemory->objects, oid, object);
260
261
0
  return 0;
262
0
}
263
264
static int odb_source_inmemory_write_object_stream(struct odb_source *source,
265
               struct odb_stream *stream,
266
               struct object_id *oid)
267
0
{
268
0
  char buf[16384];
269
0
  size_t total_read = 0;
270
0
  char *data;
271
0
  int ret;
272
273
0
  CALLOC_ARRAY(data, stream->size);
274
0
  while (1) {
275
0
    ssize_t bytes_read;
276
277
0
    bytes_read = odb_stream_read(stream, buf, sizeof(buf));
278
0
    if (bytes_read < 0) {
279
0
      ret = error("failed to read object stream");
280
0
      goto out;
281
0
    }
282
0
    if (!bytes_read)
283
0
      break;
284
285
0
    if (total_read + bytes_read > stream->size) {
286
0
      ret = error("object stream yielded more bytes than expected");
287
0
      goto out;
288
0
    }
289
290
0
    memcpy(data + total_read, buf, bytes_read);
291
0
    total_read += bytes_read;
292
0
  }
293
294
0
  if (total_read != stream->size) {
295
0
    ret = error("object stream yielded less bytes than expected");
296
0
    goto out;
297
0
  }
298
299
0
  hash_object_file(source->odb->repo->hash_algo, data, total_read,
300
0
       stream->type, oid);
301
302
0
  ret = odb_source_inmemory_write_object(source, data, stream->size,
303
0
                 stream->type, oid, NULL, NULL, 0);
304
0
  if (ret < 0)
305
0
    goto out;
306
307
0
out:
308
0
  free(data);
309
0
  return ret;
310
0
}
311
312
static int odb_source_inmemory_freshen_object(struct odb_source *source,
313
                const struct object_id *oid,
314
                const time_t *mtime UNUSED)
315
0
{
316
0
  struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
317
0
  if (find_cached_object(inmemory, oid))
318
0
    return 1;
319
0
  return 0;
320
0
}
321
322
static int odb_source_inmemory_begin_transaction(struct odb_source *source UNUSED,
323
             struct odb_transaction **out UNUSED,
324
             enum odb_transaction_flags flags UNUSED)
325
0
{
326
0
  return error("in-memory source does not support transactions");
327
0
}
328
329
static void odb_source_inmemory_close(struct odb_source *source UNUSED)
330
0
{
331
0
}
332
333
static void odb_source_inmemory_prepare(struct odb_source *source UNUSED,
334
          enum odb_prepare_flags flags UNUSED)
335
0
{
336
0
}
337
338
static int inmemory_object_free(const struct object_id *oid UNUSED,
339
        void *node_data,
340
        void *cb_data UNUSED)
341
0
{
342
0
  struct inmemory_object *object = node_data;
343
0
  free((void *) object->buf);
344
0
  free(object);
345
0
  return 0;
346
0
}
347
348
static void odb_source_inmemory_free(struct odb_source *source)
349
0
{
350
0
  struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
351
352
0
  if (inmemory->objects) {
353
0
    struct object_id null_oid = { 0 };
354
355
0
    oidtree_each(inmemory->objects, &null_oid, 0,
356
0
           inmemory_object_free, NULL);
357
0
    oidtree_clear(inmemory->objects);
358
0
    free(inmemory->objects);
359
0
  }
360
361
0
  free(inmemory->base.path);
362
0
  free(inmemory);
363
0
}
364
365
static int odb_source_inmemory_fsck(struct odb_source *source UNUSED,
366
            struct odb_fsck_options *opts UNUSED)
367
0
{
368
0
  return 0;
369
0
}
370
371
struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb)
372
0
{
373
0
  struct odb_source_inmemory *source;
374
375
0
  CALLOC_ARRAY(source, 1);
376
0
  odb_source_init(&source->base, odb, ODB_SOURCE_INMEMORY, "source");
377
378
0
  source->base.free = odb_source_inmemory_free;
379
0
  source->base.close = odb_source_inmemory_close;
380
0
  source->base.prepare = odb_source_inmemory_prepare;
381
0
  source->base.fsck = odb_source_inmemory_fsck;
382
0
  source->base.read_object_info = odb_source_inmemory_read_object_info;
383
0
  source->base.read_object_stream = odb_source_inmemory_read_object_stream;
384
0
  source->base.for_each_object = odb_source_inmemory_for_each_object;
385
0
  source->base.find_abbrev_len = odb_source_inmemory_find_abbrev_len;
386
0
  source->base.count_objects = odb_source_inmemory_count_objects;
387
0
  source->base.write_object = odb_source_inmemory_write_object;
388
0
  source->base.write_object_stream = odb_source_inmemory_write_object_stream;
389
0
  source->base.freshen_object = odb_source_inmemory_freshen_object;
390
0
  source->base.begin_transaction = odb_source_inmemory_begin_transaction;
391
392
0
  return source;
393
0
}