1 #include "sortedcache.h"
5 int git_sortedcache_new(
7 size_t item_path_offset
,
8 git_sortedcache_free_item_fn free_item
,
9 void *free_item_payload
,
10 git_vector_cmp item_cmp
,
16 pathlen
= path
? strlen(path
) : 0;
18 sc
= git__calloc(sizeof(git_sortedcache
) + pathlen
+ 1, 1);
19 GITERR_CHECK_ALLOC(sc
);
21 if (git_pool_init(&sc
->pool
, 1, 0) < 0 ||
22 git_vector_init(&sc
->items
, 4, item_cmp
) < 0 ||
23 (sc
->map
= git_strmap_alloc()) == NULL
)
26 if (git_rwlock_init(&sc
->lock
)) {
27 giterr_set(GITERR_OS
, "Failed to initialize lock");
31 sc
->item_path_offset
= item_path_offset
;
32 sc
->free_item
= free_item
;
33 sc
->free_item_payload
= free_item_payload
;
36 memcpy(sc
->path
, path
, pathlen
);
43 git_strmap_free(sc
->map
);
44 git_vector_free(&sc
->items
);
45 git_pool_clear(&sc
->pool
);
50 void git_sortedcache_incref(git_sortedcache
*sc
)
55 const char *git_sortedcache_path(git_sortedcache
*sc
)
60 static void sortedcache_clear(git_sortedcache
*sc
)
62 git_strmap_clear(sc
->map
);
68 git_vector_foreach(&sc
->items
, i
, item
) {
69 sc
->free_item(sc
->free_item_payload
, item
);
73 git_vector_clear(&sc
->items
);
75 git_pool_clear(&sc
->pool
);
78 static void sortedcache_free(git_sortedcache
*sc
)
80 /* acquire write lock to make sure everyone else is done */
81 if (git_sortedcache_wlock(sc
) < 0)
84 sortedcache_clear(sc
);
85 git_vector_free(&sc
->items
);
86 git_strmap_free(sc
->map
);
88 git_sortedcache_wunlock(sc
);
90 git_rwlock_free(&sc
->lock
);
94 void git_sortedcache_free(git_sortedcache
*sc
)
98 GIT_REFCOUNT_DEC(sc
, sortedcache_free
);
101 static int sortedcache_copy_item(void *payload
, void *tgt_item
, void *src_item
)
103 git_sortedcache
*sc
= payload
;
104 /* path will already have been copied by upsert */
105 memcpy(tgt_item
, src_item
, sc
->item_path_offset
);
109 /* copy a sorted cache */
110 int git_sortedcache_copy(
111 git_sortedcache
**out
,
112 git_sortedcache
*src
,
114 int (*copy_item
)(void *payload
, void *tgt_item
, void *src_item
),
118 git_sortedcache
*tgt
;
120 void *src_item
, *tgt_item
;
122 /* just use memcpy if no special copy fn is passed in */
124 copy_item
= sortedcache_copy_item
;
128 if ((error
= git_sortedcache_new(
129 &tgt
, src
->item_path_offset
,
130 src
->free_item
, src
->free_item_payload
,
131 src
->items
._cmp
, src
->path
)) < 0)
134 if (lock
&& git_sortedcache_rlock(src
) < 0) {
135 git_sortedcache_free(tgt
);
139 git_vector_foreach(&src
->items
, i
, src_item
) {
140 char *path
= ((char *)src_item
) + src
->item_path_offset
;
142 if ((error
= git_sortedcache_upsert(&tgt_item
, tgt
, path
)) < 0 ||
143 (error
= copy_item(payload
, tgt_item
, src_item
)) < 0)
148 git_sortedcache_runlock(src
);
150 git_sortedcache_free(tgt
);
152 *out
= !error
? tgt
: NULL
;
157 /* lock sortedcache while making modifications */
158 int git_sortedcache_wlock(git_sortedcache
*sc
)
160 GIT_UNUSED(sc
); /* prevent warning when compiled w/o threads */
162 if (git_rwlock_wrlock(&sc
->lock
) < 0) {
163 giterr_set(GITERR_OS
, "Unable to acquire write lock on cache");
169 /* unlock sorted cache when done with modifications */
170 void git_sortedcache_wunlock(git_sortedcache
*sc
)
172 git_vector_sort(&sc
->items
);
173 git_rwlock_wrunlock(&sc
->lock
);
176 /* lock sortedcache for read */
177 int git_sortedcache_rlock(git_sortedcache
*sc
)
179 GIT_UNUSED(sc
); /* prevent warning when compiled w/o threads */
181 if (git_rwlock_rdlock(&sc
->lock
) < 0) {
182 giterr_set(GITERR_OS
, "Unable to acquire read lock on cache");
188 /* unlock sorted cache when done reading */
189 void git_sortedcache_runlock(git_sortedcache
*sc
)
191 GIT_UNUSED(sc
); /* prevent warning when compiled w/o threads */
192 git_rwlock_rdunlock(&sc
->lock
);
195 /* if the file has changed, lock cache and load file contents into buf;
196 * returns <0 on error, >0 if file has not changed
198 int git_sortedcache_lockandload(git_sortedcache
*sc
, git_buf
*buf
)
202 if ((error
= git_sortedcache_wlock(sc
)) < 0)
205 if ((error
= git_futils_filestamp_check(&sc
->stamp
, sc
->path
)) <= 0)
208 if (!git__is_sizet(sc
->stamp
.size
)) {
209 giterr_set(GITERR_INVALID
, "Unable to load file larger than size_t");
214 if ((fd
= git_futils_open_ro(sc
->path
)) < 0) {
220 error
= git_futils_readbuffer_fd(buf
, fd
, (size_t)sc
->stamp
.size
);
227 return 1; /* return 1 -> file needs reload and was successfully loaded */
230 git_sortedcache_wunlock(sc
);
234 void git_sortedcache_updated(git_sortedcache
*sc
)
236 /* update filestamp to latest value */
237 if (git_futils_filestamp_check(&sc
->stamp
, sc
->path
) < 0)
241 /* release all items in sorted cache */
242 int git_sortedcache_clear(git_sortedcache
*sc
, bool wlock
)
244 if (wlock
&& git_sortedcache_wlock(sc
) < 0)
247 sortedcache_clear(sc
);
250 git_sortedcache_wunlock(sc
);
255 /* find and/or insert item, returning pointer to item data */
256 int git_sortedcache_upsert(void **out
, git_sortedcache
*sc
, const char *key
)
261 size_t keylen
, itemlen
;
264 pos
= git_strmap_lookup_index(sc
->map
, key
);
265 if (git_strmap_valid_index(sc
->map
, pos
)) {
266 item
= git_strmap_value_at(sc
->map
, pos
);
270 keylen
= strlen(key
);
271 itemlen
= sc
->item_path_offset
+ keylen
+ 1;
272 itemlen
= (itemlen
+ 7) & ~7;
274 if ((item
= git_pool_mallocz(&sc
->pool
, (uint32_t)itemlen
)) == NULL
) {
275 /* don't use GITERR_CHECK_ALLOC b/c of lock */
280 /* one strange thing is that even if the vector or hash table insert
281 * fail, there is no way to free the pool item so we just abandon it
284 item_key
= ((char *)item
) + sc
->item_path_offset
;
285 memcpy(item_key
, key
, keylen
);
287 pos
= kh_put(str
, sc
->map
, item_key
, &error
);
292 kh_key(sc
->map
, pos
) = item_key
;
293 kh_val(sc
->map
, pos
) = item
;
295 error
= git_vector_insert(&sc
->items
, item
);
297 git_strmap_delete_at(sc
->map
, pos
);
301 *out
= !error
? item
: NULL
;
305 /* lookup item by key */
306 void *git_sortedcache_lookup(const git_sortedcache
*sc
, const char *key
)
308 khiter_t pos
= git_strmap_lookup_index(sc
->map
, key
);
309 if (git_strmap_valid_index(sc
->map
, pos
))
310 return git_strmap_value_at(sc
->map
, pos
);
314 /* find out how many items are in the cache */
315 size_t git_sortedcache_entrycount(const git_sortedcache
*sc
)
317 return git_vector_length(&sc
->items
);
320 /* lookup item by index */
321 void *git_sortedcache_entry(git_sortedcache
*sc
, size_t pos
)
323 /* make sure the items are sorted so this gets the correct item */
324 if (!sc
->items
.sorted
)
325 git_vector_sort(&sc
->items
);
327 return git_vector_get(&sc
->items
, pos
);
330 /* helper struct so bsearch callback can know offset + key value for cmp */
331 struct sortedcache_magic_key
{
336 static int sortedcache_magic_cmp(const void *key
, const void *value
)
338 const struct sortedcache_magic_key
*magic
= key
;
339 const char *value_key
= ((const char *)value
) + magic
->offset
;
340 return strcmp(magic
->key
, value_key
);
343 /* lookup index of item by key */
344 int git_sortedcache_lookup_index(
345 size_t *out
, git_sortedcache
*sc
, const char *key
)
347 struct sortedcache_magic_key magic
;
349 magic
.offset
= sc
->item_path_offset
;
352 return git_vector_bsearch2(out
, &sc
->items
, sortedcache_magic_cmp
, &magic
);
355 /* remove entry from cache */
356 int git_sortedcache_remove(git_sortedcache
*sc
, size_t pos
)
361 /* because of pool allocation, this can't actually remove the item,
362 * but we can remove it from the items vector and the hash table.
365 if ((item
= git_vector_get(&sc
->items
, pos
)) == NULL
) {
366 giterr_set(GITERR_INVALID
, "Removing item out of range");
367 return GIT_ENOTFOUND
;
370 (void)git_vector_remove(&sc
->items
, pos
);
372 mappos
= git_strmap_lookup_index(sc
->map
, item
+ sc
->item_path_offset
);
373 git_strmap_delete_at(sc
->map
, mappos
);
376 sc
->free_item(sc
->free_item_payload
, item
);