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