/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 | } |