Line Flow Count Block(s) Source
1 - /*
2 - * Copyright (C) the libgit2 contributors. All rights reserved.
3 - *
4 - * This file is part of libgit2, distributed under the GNU GPL v2 with
5 - * a Linking Exception. For full terms see the included COPYING file.
6 - */
7 -
8 - #include "sortedcache.h"
9 -
10 2658 2 int git_sortedcache_new(
11 - git_sortedcache **out,
12 - size_t item_path_offset,
13 - git_sortedcache_free_item_fn free_item,
14 - void *free_item_payload,
15 - git_vector_cmp item_cmp,
16 - const char *path)
17 - {
18 - git_sortedcache *sc;
19 - size_t pathlen, alloclen;
20 -
21 2658 2-4 pathlen = path ? strlen(path) : 0;
22 -
23 2658 5-11 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_sortedcache), pathlen);
24 2658 12-18 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
25 2658 19 sc = git__calloc(1, alloclen);
26 2658 20,21 GIT_ERROR_CHECK_ALLOC(sc);
27 -
28 2658 22,23,25 if (git_pool_init(&sc->pool, 1) < 0 ||
29 2658 24,27 git_vector_init(&sc->items, 4, item_cmp) < 0 ||
30 2658 26 git_strmap_new(&sc->map) < 0)
31 - goto fail;
32 -
33 2658 28,29 if (git_rwlock_init(&sc->lock)) {
34 ##### 30 git_error_set(GIT_ERROR_OS, "failed to initialize lock");
35 ##### 35 goto fail;
36 - }
37 -
38 2658 31 sc->item_path_offset = item_path_offset;
39 2658 31 sc->free_item = free_item;
40 2658 31 sc->free_item_payload = free_item_payload;
41 2658 31 GIT_REFCOUNT_INC(sc);
42 2658 32 if (pathlen)
43 2656 33 memcpy(sc->path, path, pathlen);
44 -
45 2658 34 *out = sc;
46 2658 34 return 0;
47 -
48 - fail:
49 ##### 36 git_strmap_free(sc->map);
50 ##### 37 git_vector_free(&sc->items);
51 ##### 38 git_pool_clear(&sc->pool);
52 ##### 39 git__free(sc);
53 ##### 40 return -1;
54 - }
55 -
56 ##### 2 void git_sortedcache_incref(git_sortedcache *sc)
57 - {
58 ##### 2 GIT_REFCOUNT_INC(sc);
59 ##### 3 }
60 -
61 64 2 const char *git_sortedcache_path(git_sortedcache *sc)
62 - {
63 64 2 return sc->path;
64 - }
65 -
66 16607 2 static void sortedcache_clear(git_sortedcache *sc)
67 - {
68 16607 2 git_strmap_clear(sc->map);
69 -
70 16607 3 if (sc->free_item) {
71 - size_t i;
72 - void *item;
73 -
74 17 4,6-8 git_vector_foreach(&sc->items, i, item) {
75 12 5 sc->free_item(sc->free_item_payload, item);
76 - }
77 - }
78 -
79 16607 9 git_vector_clear(&sc->items);
80 -
81 16607 10 git_pool_clear(&sc->pool);
82 16607 11 }
83 -
84 2658 2 static void sortedcache_free(git_sortedcache *sc)
85 - {
86 - /* acquire write lock to make sure everyone else is done */
87 2658 2,3 if (git_sortedcache_wlock(sc) < 0)
88 2658 4,11 return;
89 -
90 2658 5 sortedcache_clear(sc);
91 2658 6 git_vector_free(&sc->items);
92 2658 7 git_strmap_free(sc->map);
93 -
94 2658 8 git_sortedcache_wunlock(sc);
95 -
96 2658 9 git_rwlock_free(&sc->lock);
97 2658 10 git__free(sc);
98 - }
99 -
100 2658 2 void git_sortedcache_free(git_sortedcache *sc)
101 - {
102 2658 2 if (!sc)
103 2658 3,8 return;
104 2658 4-7 GIT_REFCOUNT_DEC(sc, sortedcache_free);
105 - }
106 -
107 2548 2 static int sortedcache_copy_item(void *payload, void *tgt_item, void *src_item)
108 - {
109 2548 2 git_sortedcache *sc = payload;
110 - /* path will already have been copied by upsert */
111 2548 2 memcpy(tgt_item, src_item, sc->item_path_offset);
112 2548 2 return 0;
113 - }
114 -
115 - /* copy a sorted cache */
116 488 2 int git_sortedcache_copy(
117 - git_sortedcache **out,
118 - git_sortedcache *src,
119 - bool lock,
120 - int (*copy_item)(void *payload, void *tgt_item, void *src_item),
121 - void *payload)
122 - {
123 488 2 int error = 0;
124 - git_sortedcache *tgt;
125 - size_t i;
126 - void *src_item, *tgt_item;
127 -
128 - /* just use memcpy if no special copy fn is passed in */
129 488 2 if (!copy_item) {
130 488 3 copy_item = sortedcache_copy_item;
131 488 3 payload = src;
132 - }
133 -
134 488 4,5 if ((error = git_sortedcache_new(
135 - &tgt, src->item_path_offset,
136 - src->free_item, src->free_item_payload,
137 488 4 src->items._cmp, src->path)) < 0)
138 ##### 6 return error;
139 -
140 488 7-9 if (lock && git_sortedcache_rlock(src) < 0) {
141 ##### 10 git_sortedcache_free(tgt);
142 ##### 11 return -1;
143 - }
144 -
145 3035 12,17-19 git_vector_foreach(&src->items, i, src_item) {
146 2547 13 char *path = ((char *)src_item) + src->item_path_offset;
147 -
148 2551 13-16 if ((error = git_sortedcache_upsert(&tgt_item, tgt, path)) < 0 ||
149 2495 15 (error = copy_item(payload, tgt_item, src_item)) < 0)
150 - break;
151 - }
152 -
153 488 20 if (lock)
154 488 21 git_sortedcache_runlock(src);
155 488 22 if (error)
156 ##### 23 git_sortedcache_free(tgt);
157 -
158 488 24-26 *out = !error ? tgt : NULL;
159 -
160 488 27 return error;
161 - }
162 -
163 - /* lock sortedcache while making modifications */
164 34479 2 int git_sortedcache_wlock(git_sortedcache *sc)
165 - {
166 - GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
167 -
168 34481 2,3 if (git_rwlock_wrlock(&sc->lock) < 0) {
169 ##### 4 git_error_set(GIT_ERROR_OS, "unable to acquire write lock on cache");
170 ##### 5 return -1;
171 - }
172 34481 6 return 0;
173 - }
174 -
175 - /* unlock sorted cache when done with modifications */
176 34481 2 void git_sortedcache_wunlock(git_sortedcache *sc)
177 - {
178 34481 2 git_vector_sort(&sc->items);
179 34483 3 git_rwlock_wrunlock(&sc->lock);
180 34483 4 }
181 -
182 - /* lock sortedcache for read */
183 16636 2 int git_sortedcache_rlock(git_sortedcache *sc)
184 - {
185 - GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
186 -
187 16636 2,3 if (git_rwlock_rdlock(&sc->lock) < 0) {
188 ##### 4 git_error_set(GIT_ERROR_OS, "unable to acquire read lock on cache");
189 ##### 5 return -1;
190 - }
191 16636 6 return 0;
192 - }
193 -
194 - /* unlock sorted cache when done reading */
195 16635 2 void git_sortedcache_runlock(git_sortedcache *sc)
196 - {
197 - GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
198 16635 2 git_rwlock_rdunlock(&sc->lock);
199 16635 3 }
200 -
201 - /* if the file has changed, lock cache and load file contents into buf;
202 - * returns <0 on error, >0 if file has not changed
203 - */
204 17633 2 int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf)
205 - {
206 - int error, fd;
207 - struct stat st;
208 -
209 17633 2,3 if ((error = git_sortedcache_wlock(sc)) < 0)
210 ##### 4 return error;
211 -
212 17634 5,6 if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0)
213 17073 7 goto unlock;
214 -
215 561 8,9 if ((fd = git_futils_open_ro(sc->path)) < 0) {
216 ##### 10 error = fd;
217 ##### 10 goto unlock;
218 - }
219 -
220 561 11,12 if (p_fstat(fd, &st) < 0) {
221 ##### 13 git_error_set(GIT_ERROR_OS, "failed to stat file");
222 ##### 14 error = -1;
223 ##### 14 (void)p_close(fd);
224 ##### 26 goto unlock;
225 - }
226 -
227 561 15,16 if (!git__is_sizet(st.st_size)) {
228 ##### 17 git_error_set(GIT_ERROR_INVALID, "unable to load file larger than size_t");
229 ##### 18 error = -1;
230 ##### 18 (void)p_close(fd);
231 ##### 19 goto unlock;
232 - }
233 -
234 561 20 if (buf)
235 561 21 error = git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size);
236 -
237 561 22 (void)p_close(fd);
238 -
239 561 23 if (error < 0)
240 ##### 24 goto unlock;
241 -
242 561 25 return 1; /* return 1 -> file needs reload and was successfully loaded */
243 -
244 - unlock:
245 17073 27 git_sortedcache_wunlock(sc);
246 17073 28 return error;
247 - }
248 -
249 38 2 void git_sortedcache_updated(git_sortedcache *sc)
250 - {
251 - /* update filestamp to latest value */
252 38 2 git_futils_filestamp_check(&sc->stamp, sc->path);
253 38 3 }
254 -
255 - /* release all items in sorted cache */
256 13949 2 int git_sortedcache_clear(git_sortedcache *sc, bool wlock)
257 - {
258 13949 2-4 if (wlock && git_sortedcache_wlock(sc) < 0)
259 ##### 5 return -1;
260 -
261 13949 6 sortedcache_clear(sc);
262 -
263 13949 7 if (wlock)
264 13388 8 git_sortedcache_wunlock(sc);
265 -
266 13949 9 return 0;
267 - }
268 -
269 - /* find and/or insert item, returning pointer to item data */
270 12182 2 int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key)
271 - {
272 - size_t keylen, itemlen;
273 12182 2 int error = 0;
274 - char *item_key;
275 - void *item;
276 -
277 13055 2,3 if ((item = git_strmap_get(sc->map, key)) != NULL)
278 160 4 goto done;
279 -
280 12895 5 keylen = strlen(key);
281 12895 5 itemlen = sc->item_path_offset + keylen + 1;
282 12895 5 itemlen = (itemlen + 7) & ~7;
283 -
284 12895 5,6 if ((item = git_pool_mallocz(&sc->pool, itemlen)) == NULL) {
285 - /* don't use GIT_ERROR_CHECK_ALLOC b/c of lock */
286 ##### 7 error = -1;
287 ##### 7 goto done;
288 - }
289 -
290 - /* one strange thing is that even if the vector or hash table insert
291 - * fail, there is no way to free the pool item so we just abandon it
292 - */
293 -
294 12669 8 item_key = ((char *)item) + sc->item_path_offset;
295 12669 8 memcpy(item_key, key, keylen);
296 -
297 12902 8,9 if ((error = git_strmap_set(sc->map, item_key, item)) < 0)
298 ##### 10 goto done;
299 -
300 12902 11,12 if ((error = git_vector_insert(&sc->items, item)) < 0)
301 ##### 13 git_strmap_delete(sc->map, item_key);
302 -
303 - done:
304 12993 14 if (out)
305 12984 15-18 *out = !error ? item : NULL;
306 12993 19 return error;
307 - }
308 -
309 - /* lookup item by key */
310 19409 2 void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key)
311 - {
312 19409 2 return git_strmap_get(sc->map, key);
313 - }
314 -
315 - /* find out how many items are in the cache */
316 14169 2 size_t git_sortedcache_entrycount(const git_sortedcache *sc)
317 - {
318 14169 2 return git_vector_length(&sc->items);
319 - }
320 -
321 - /* lookup item by index */
322 11468 2 void *git_sortedcache_entry(git_sortedcache *sc, size_t pos)
323 - {
324 - /* make sure the items are sorted so this gets the correct item */
325 11468 2 if (!git_vector_is_sorted(&sc->items))
326 216 3 git_vector_sort(&sc->items);
327 -
328 11468 4 return git_vector_get(&sc->items, pos);
329 - }
330 -
331 - /* helper struct so bsearch callback can know offset + key value for cmp */
332 - struct sortedcache_magic_key {
333 - size_t offset;
334 - const char *key;
335 - };
336 -
337 277 2 static int sortedcache_magic_cmp(const void *key, const void *value)
338 - {
339 277 2 const struct sortedcache_magic_key *magic = key;
340 277 2 const char *value_key = ((const char *)value) + magic->offset;
341 277 2 return strcmp(magic->key, value_key);
342 - }
343 -
344 - /* lookup index of item by key */
345 167 2 int git_sortedcache_lookup_index(
346 - size_t *out, git_sortedcache *sc, const char *key)
347 - {
348 - struct sortedcache_magic_key magic;
349 -
350 167 2 magic.offset = sc->item_path_offset;
351 167 2 magic.key = key;
352 -
353 167 2 return git_vector_bsearch2(out, &sc->items, sortedcache_magic_cmp, &magic);
354 - }
355 -
356 - /* remove entry from cache */
357 23 2 int git_sortedcache_remove(git_sortedcache *sc, size_t pos)
358 - {
359 - char *item;
360 -
361 - /*
362 - * Because of pool allocation, this can't actually remove the item,
363 - * but we can remove it from the items vector and the hash table.
364 - */
365 -
366 23 2,3 if ((item = git_vector_get(&sc->items, pos)) == NULL) {
367 ##### 4 git_error_set(GIT_ERROR_INVALID, "removing item out of range");
368 ##### 5 return GIT_ENOTFOUND;
369 - }
370 -
371 23 6 (void)git_vector_remove(&sc->items, pos);
372 -
373 23 7 git_strmap_delete(sc->map, item + sc->item_path_offset);
374 -
375 23 8 if (sc->free_item)
376 3 9 sc->free_item(sc->free_item_payload, item);
377 -
378 23 10 return 0;
379 - }
380 -