2 * Copyright (c) 2019, 2020 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include "got_compat.h"
27 #include "got_object.h"
28 #include "got_error.h"
30 #include "got_lib_delta.h"
31 #include "got_lib_inflate.h"
32 #include "got_lib_object.h"
33 #include "got_lib_delta_cache.h"
36 #define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
39 struct got_delta_cache_element
{
40 TAILQ_ENTRY(got_delta_cache_element
) entry
;
41 off_t delta_data_offset
;
46 TAILQ_HEAD(got_delta_cache_head
, got_delta_cache_element
);
48 struct got_delta_cache
{
49 struct got_delta_cache_head entries
;
60 struct got_delta_cache
*
61 got_delta_cache_alloc(int maxelem
, size_t maxelemsize
)
63 struct got_delta_cache
*cache
;
65 cache
= calloc(1, sizeof(*cache
));
69 TAILQ_INIT(&cache
->entries
);
70 cache
->maxelem
= maxelem
;
71 cache
->maxelemsize
= maxelemsize
;
76 got_delta_cache_free(struct got_delta_cache
*cache
)
78 struct got_delta_cache_element
*entry
;
80 #ifdef GOT_OBJ_CACHE_DEBUG
81 fprintf(stderr
, "%s: delta cache: %d elements, %d searches, %d hits, "
82 "%d missed, %d evicted, %d too large\n", getprogname(), cache
->nelem
,
83 cache
->cache_search
, cache
->cache_hit
, cache
->cache_miss
,
84 cache
->cache_evict
, cache
->cache_toolarge
);
86 while (!TAILQ_EMPTY(&cache
->entries
)) {
87 entry
= TAILQ_FIRST(&cache
->entries
);
88 TAILQ_REMOVE(&cache
->entries
, entry
, entry
);
89 free(entry
->delta_data
);
95 #ifndef GOT_NO_OBJ_CACHE
97 remove_least_used_element(struct got_delta_cache
*cache
)
99 struct got_delta_cache_element
*entry
;
101 if (cache
->nelem
== 0)
104 entry
= TAILQ_LAST(&cache
->entries
, got_delta_cache_head
);
105 TAILQ_REMOVE(&cache
->entries
, entry
, entry
);
106 free(entry
->delta_data
);
109 cache
->cache_evict
++;
113 const struct got_error
*
114 got_delta_cache_add(struct got_delta_cache
*cache
,
115 off_t delta_data_offset
, uint8_t *delta_data
, size_t delta_len
)
117 #ifdef GOT_NO_OBJ_CACHE
118 return got_error(GOT_ERR_NO_SPACE
);
120 struct got_delta_cache_element
*entry
;
122 if (delta_len
> cache
->maxelemsize
) {
123 cache
->cache_toolarge
++;
124 return got_error(GOT_ERR_NO_SPACE
);
127 if (cache
->nelem
>= cache
->maxelem
)
128 remove_least_used_element(cache
);
130 entry
= calloc(1, sizeof(*entry
));
132 return got_error_from_errno("calloc");
134 entry
->delta_data_offset
= delta_data_offset
;
135 entry
->delta_data
= delta_data
;
136 entry
->delta_len
= delta_len
;
138 TAILQ_INSERT_HEAD(&cache
->entries
, entry
, entry
);
145 got_delta_cache_get(uint8_t **delta_data
, size_t *delta_len
,
146 struct got_delta_cache
*cache
, off_t delta_data_offset
)
148 struct got_delta_cache_element
*entry
;
150 cache
->cache_search
++;
153 TAILQ_FOREACH(entry
, &cache
->entries
, entry
) {
154 if (entry
->delta_data_offset
!= delta_data_offset
)
157 if (entry
!= TAILQ_FIRST(&cache
->entries
)) {
158 TAILQ_REMOVE(&cache
->entries
, entry
, entry
);
159 TAILQ_INSERT_HEAD(&cache
->entries
, entry
, entry
);
161 *delta_data
= entry
->delta_data
;
162 *delta_len
= entry
->delta_len
;