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 oi->whence = OI_CACHED;
56 }
57
58 static int odb_source_inmemory_read_object_info(struct odb_source *source,
59 const struct object_id *oid,
60 struct object_info *oi,
61 enum object_info_flags flags UNUSED)
62 {
63 struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
64 const struct inmemory_object *object;
65
66 object = find_cached_object(inmemory, oid);
67 if (!object)
68 return -1;
69
70 populate_object_info(inmemory, oi, object);
71 return 0;
72 }
73
74 struct odb_read_stream_inmemory {
75 struct odb_read_stream base;
76 const unsigned char *buf;
77 size_t offset;
78 };
79
80 static ssize_t odb_read_stream_inmemory_read(struct odb_read_stream *stream,
81 char *buf, size_t buf_len)
82 {
83 struct odb_read_stream_inmemory *inmemory =
84 container_of(stream, struct odb_read_stream_inmemory, base);
85 size_t bytes = buf_len;
86
87 if (buf_len > inmemory->base.size - inmemory->offset)
88 bytes = inmemory->base.size - inmemory->offset;
89
90 memcpy(buf, inmemory->buf + inmemory->offset, bytes);
91 inmemory->offset += bytes;
92
93 return bytes;
94 }
95
96 static int odb_read_stream_inmemory_close(struct odb_read_stream *stream UNUSED)
97 {
98 return 0;
99 }
100
101 static int odb_source_inmemory_read_object_stream(struct odb_read_stream **out,
102 struct odb_source *source,
103 const struct object_id *oid)
104 {
105 struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
106 struct odb_read_stream_inmemory *stream;
107 const struct inmemory_object *object;
108
109 object = find_cached_object(inmemory, oid);
110 if (!object)
111 return -1;
112
113 CALLOC_ARRAY(stream, 1);
114 stream->base.read = odb_read_stream_inmemory_read;
115 stream->base.close = odb_read_stream_inmemory_close;
116 stream->base.size = object->size;
117 stream->base.type = object->type;
118 stream->buf = object->buf;
119
120 *out = &stream->base;
121 return 0;
122 }
123
124 struct odb_source_inmemory_for_each_object_data {
125 struct odb_source_inmemory *inmemory;
126 const struct object_info *request;
127 odb_for_each_object_cb cb;
128 void *cb_data;
129 };
130
131 static int odb_source_inmemory_for_each_object_cb(const struct object_id *oid,
132 void *node_data, void *cb_data)
133 {
134 struct odb_source_inmemory_for_each_object_data *data = cb_data;
135 struct inmemory_object *object = node_data;
136
137 if (data->request) {
138 struct object_info oi = *data->request;
139 populate_object_info(data->inmemory, &oi, object);
140 return data->cb(oid, &oi, data->cb_data);
141 } else {
142 return data->cb(oid, NULL, data->cb_data);
143 }
144 }
145
146 static int odb_source_inmemory_for_each_object(struct odb_source *source,
147 const struct object_info *request,
148 odb_for_each_object_cb cb,
149 void *cb_data,
150 const struct odb_for_each_object_options *opts)
151 {
152 struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
153 struct odb_source_inmemory_for_each_object_data payload = {
154 .inmemory = inmemory,
155 .request = request,
156 .cb = cb,
157 .cb_data = cb_data,
158 };
159 struct object_id null_oid = { 0 };
160
161 if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) ||
162 (opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local))
163 return 0;
164 if (!inmemory->objects)
165 return 0;
166
167 return oidtree_each(inmemory->objects,
168 opts->prefix ? opts->prefix : &null_oid, opts->prefix_hex_len,
169 odb_source_inmemory_for_each_object_cb, &payload);
170 }
171
172 struct find_abbrev_len_data {
173 const struct object_id *oid;
174 unsigned len;
175 };
176
177 static int find_abbrev_len_cb(const struct object_id *oid,
178 struct object_info *oi UNUSED,
179 void *cb_data)
180 {
181 struct find_abbrev_len_data *data = cb_data;
182 unsigned len = oid_common_prefix_hexlen(oid, data->oid);
183 if (len != hash_algos[oid->algo].hexsz && len >= data->len)
184 data->len = len + 1;
185 return 0;
186 }
187
188 static int odb_source_inmemory_find_abbrev_len(struct odb_source *source,
189 const struct object_id *oid,
190 unsigned min_len,
191 unsigned *out)
192 {
193 struct odb_for_each_object_options opts = {
194 .prefix = oid,
195 .prefix_hex_len = min_len,
196 };
197 struct find_abbrev_len_data data = {
198 .oid = oid,
199 .len = min_len,
200 };
201 int ret;
202
203 ret = odb_source_inmemory_for_each_object(source, NULL, find_abbrev_len_cb,
204 &data, &opts);
205 *out = data.len;
206
207 return ret;
208 }
209
210 static int count_objects_cb(const struct object_id *oid UNUSED,
211 struct object_info *oi UNUSED,
212 void *cb_data)
213 {
214 unsigned long *counter = cb_data;
215 (*counter)++;
216 return 0;
217 }
218
219 static int odb_source_inmemory_count_objects(struct odb_source *source,
220 enum odb_count_objects_flags flags UNUSED,
221 unsigned long *out)
222 {
223 struct odb_for_each_object_options opts = { 0 };
224 *out = 0;
225 return odb_source_inmemory_for_each_object(source, NULL, count_objects_cb,
226 out, &opts);
227 }
228
229 static int odb_source_inmemory_write_object(struct odb_source *source,
230 const void *buf, unsigned long len,
231 enum object_type type,
232 struct object_id *oid,
233 struct object_id *compat_oid UNUSED,
234 enum odb_write_object_flags flags UNUSED)
235 {
236 struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
237 struct inmemory_object *object;
238
239 hash_object_file(source->odb->repo->hash_algo, buf, len, type, oid);
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 ret = odb_source_inmemory_write_object(source, data, len, OBJ_BLOB, oid,
288 NULL, 0);
289 if (ret < 0)
290 goto out;
291
292 out:
293 free(data);
294 return ret;
295 }
296
297 static int odb_source_inmemory_freshen_object(struct odb_source *source,
298 const struct object_id *oid)
299 {
300 struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
301 if (find_cached_object(inmemory, oid))
302 return 1;
303 return 0;
304 }
305
306 static int odb_source_inmemory_begin_transaction(struct odb_source *source UNUSED,
307 struct odb_transaction **out UNUSED)
308 {
309 return error("in-memory source does not support transactions");
310 }
311
312 static int odb_source_inmemory_read_alternates(struct odb_source *source UNUSED,
313 struct strvec *out UNUSED)
314 {
315 return 0;
316 }
317
318 static int odb_source_inmemory_write_alternate(struct odb_source *source UNUSED,
319 const char *alternate UNUSED)
320 {
321 return error("in-memory source does not support alternates");
322 }
323
324 static void odb_source_inmemory_close(struct odb_source *source UNUSED)
325 {
326 }
327
328 static void odb_source_inmemory_reprepare(struct odb_source *source UNUSED)
329 {
330 }
331
332 static int inmemory_object_free(const struct object_id *oid UNUSED,
333 void *node_data,
334 void *cb_data UNUSED)
335 {
336 struct inmemory_object *object = node_data;
337 free((void *) object->buf);
338 free(object);
339 return 0;
340 }
341
342 static void odb_source_inmemory_free(struct odb_source *source)
343 {
344 struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
345
346 if (inmemory->objects) {
347 struct object_id null_oid = { 0 };
348
349 oidtree_each(inmemory->objects, &null_oid, 0,
350 inmemory_object_free, NULL);
351 oidtree_clear(inmemory->objects);
352 free(inmemory->objects);
353 }
354
355 free(inmemory->base.path);
356 free(inmemory);
357 }
358
359 struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb)
360 {
361 struct odb_source_inmemory *source;
362
363 CALLOC_ARRAY(source, 1);
364 odb_source_init(&source->base, odb, ODB_SOURCE_INMEMORY, "source", false);
365
366 source->base.free = odb_source_inmemory_free;
367 source->base.close = odb_source_inmemory_close;
368 source->base.reprepare = odb_source_inmemory_reprepare;
369 source->base.read_object_info = odb_source_inmemory_read_object_info;
370 source->base.read_object_stream = odb_source_inmemory_read_object_stream;
371 source->base.for_each_object = odb_source_inmemory_for_each_object;
372 source->base.find_abbrev_len = odb_source_inmemory_find_abbrev_len;
373 source->base.count_objects = odb_source_inmemory_count_objects;
374 source->base.write_object = odb_source_inmemory_write_object;
375 source->base.write_object_stream = odb_source_inmemory_write_object_stream;
376 source->base.freshen_object = odb_source_inmemory_freshen_object;
377 source->base.begin_transaction = odb_source_inmemory_begin_transaction;
378 source->base.read_alternates = odb_source_inmemory_read_alternates;
379 source->base.write_alternate = odb_source_inmemory_write_alternate;
380
381 return source;
382 }