/src/cairo/src/cairo-cache.c
Line | Count | Source |
1 | | /* cairo - a vector graphics library with display and print output |
2 | | * |
3 | | * Copyright © 2004 Red Hat, Inc. |
4 | | * Copyright © 2005 Red Hat, Inc. |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it either under the terms of the GNU Lesser General Public |
8 | | * License version 2.1 as published by the Free Software Foundation |
9 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
10 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
11 | | * notice, a recipient may use your version of this file under either |
12 | | * the MPL or the LGPL. |
13 | | * |
14 | | * You should have received a copy of the LGPL along with this library |
15 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
16 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
17 | | * You should have received a copy of the MPL along with this library |
18 | | * in the file COPYING-MPL-1.1 |
19 | | * |
20 | | * The contents of this file are subject to the Mozilla Public License |
21 | | * Version 1.1 (the "License"); you may not use this file except in |
22 | | * compliance with the License. You may obtain a copy of the License at |
23 | | * http://www.mozilla.org/MPL/ |
24 | | * |
25 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
26 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
27 | | * the specific language governing rights and limitations. |
28 | | * |
29 | | * The Original Code is the cairo graphics library. |
30 | | * |
31 | | * The Initial Developer of the Original Code is Red Hat, Inc. |
32 | | * |
33 | | * Contributor(s): |
34 | | * Keith Packard <keithp@keithp.com> |
35 | | * Graydon Hoare <graydon@redhat.com> |
36 | | * Carl Worth <cworth@cworth.org> |
37 | | */ |
38 | | |
39 | | #include "cairoint.h" |
40 | | #include "cairo-error-private.h" |
41 | | |
42 | | static void |
43 | | _cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, |
44 | | unsigned long additional); |
45 | | |
46 | | static cairo_bool_t |
47 | | _cairo_cache_entry_is_non_zero (const void *entry) |
48 | 0 | { |
49 | 0 | return ((const cairo_cache_entry_t *) entry)->size; |
50 | 0 | } |
51 | | |
52 | | |
53 | | /** |
54 | | * _cairo_cache_init: |
55 | | * @cache: the #cairo_cache_t to initialise |
56 | | * @keys_equal: a function to return %TRUE if two keys are equal |
57 | | * @entry_destroy: destroy notifier for cache entries |
58 | | * @max_size: the maximum size for this cache |
59 | | * |
60 | | * Creates a new cache using the keys_equal() function to determine |
61 | | * the equality of entries. |
62 | | * |
63 | | * Data is provided to the cache in the form of user-derived version |
64 | | * of #cairo_cache_entry_t. A cache entry must be able to hold hash |
65 | | * code, a size, and the key/value pair being stored in the |
66 | | * cache. Sometimes only the key will be necessary, (as in |
67 | | * _cairo_cache_lookup()), and in these cases the value portion of the |
68 | | * entry need not be initialized. |
69 | | * |
70 | | * The units for max_size can be chosen by the caller, but should be |
71 | | * consistent with the units of the size field of cache entries. When |
72 | | * adding an entry with _cairo_cache_insert() if the total size of |
73 | | * entries in the cache would exceed max_size then entries will be |
74 | | * removed at random until the new entry would fit or the cache is |
75 | | * empty. Then the new entry is inserted. |
76 | | * |
77 | | * There are cases in which the automatic removal of entries is |
78 | | * undesired. If the cache entries have reference counts, then it is a |
79 | | * simple matter to use the reference counts to ensure that entries |
80 | | * continue to live even after being ejected from the cache. However, |
81 | | * in some cases the memory overhead of adding a reference count to |
82 | | * the entry would be objectionable. In such cases, the |
83 | | * _cairo_cache_freeze() and _cairo_cache_thaw() calls can be |
84 | | * used to establish a window during which no automatic removal of |
85 | | * entries will occur. |
86 | | * |
87 | | * Returns: the newly created #cairo_cache_t |
88 | | **/ |
89 | | cairo_status_t |
90 | | _cairo_cache_init (cairo_cache_t *cache, |
91 | | cairo_cache_keys_equal_func_t keys_equal, |
92 | | cairo_cache_predicate_func_t predicate, |
93 | | cairo_destroy_func_t entry_destroy, |
94 | | unsigned long max_size) |
95 | 2 | { |
96 | 2 | cache->hash_table = _cairo_hash_table_create (keys_equal); |
97 | 2 | if (unlikely (cache->hash_table == NULL)) |
98 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
99 | | |
100 | 2 | if (predicate == NULL) |
101 | 0 | predicate = _cairo_cache_entry_is_non_zero; |
102 | 2 | cache->predicate = predicate; |
103 | 2 | cache->entry_destroy = entry_destroy; |
104 | | |
105 | 2 | cache->max_size = max_size; |
106 | 2 | cache->size = 0; |
107 | | |
108 | 2 | cache->freeze_count = 0; |
109 | | |
110 | 2 | return CAIRO_STATUS_SUCCESS; |
111 | 2 | } |
112 | | |
113 | | static void |
114 | | _cairo_cache_pluck (void *entry, void *closure) |
115 | 0 | { |
116 | 0 | _cairo_cache_remove (closure, entry); |
117 | 0 | } |
118 | | |
119 | | /** |
120 | | * _cairo_cache_fini: |
121 | | * @cache: a cache to destroy |
122 | | * |
123 | | * Immediately destroys the given cache, freeing all resources |
124 | | * associated with it. As part of this process, the entry_destroy() |
125 | | * function, (as passed to _cairo_cache_init()), will be called for |
126 | | * each entry in the cache. |
127 | | **/ |
128 | | void |
129 | | _cairo_cache_fini (cairo_cache_t *cache) |
130 | 0 | { |
131 | 0 | _cairo_hash_table_foreach (cache->hash_table, |
132 | 0 | _cairo_cache_pluck, |
133 | 0 | cache); |
134 | 0 | assert (cache->size == 0); |
135 | 0 | _cairo_hash_table_destroy (cache->hash_table); |
136 | 0 | } |
137 | | |
138 | | /** |
139 | | * _cairo_cache_freeze: |
140 | | * @cache: a cache with some precious entries in it (or about to be |
141 | | * added) |
142 | | * |
143 | | * Disable the automatic ejection of entries from the cache. For as |
144 | | * long as the cache is "frozen", calls to _cairo_cache_insert() will |
145 | | * add new entries to the cache regardless of how large the cache |
146 | | * grows. See _cairo_cache_thaw(). |
147 | | * |
148 | | * Note: Multiple calls to _cairo_cache_freeze() will stack, in that |
149 | | * the cache will remain "frozen" until a corresponding number of |
150 | | * calls are made to _cairo_cache_thaw(). |
151 | | **/ |
152 | | void |
153 | | _cairo_cache_freeze (cairo_cache_t *cache) |
154 | 3.99k | { |
155 | 3.99k | assert (cache->freeze_count >= 0); |
156 | | |
157 | 3.99k | cache->freeze_count++; |
158 | 3.99k | } |
159 | | |
160 | | /** |
161 | | * _cairo_cache_thaw: |
162 | | * @cache: a cache, just after the entries in it have become less |
163 | | * precious |
164 | | * |
165 | | * Cancels the effects of _cairo_cache_freeze(). |
166 | | * |
167 | | * When a number of calls to _cairo_cache_thaw() is made corresponding |
168 | | * to the number of calls to _cairo_cache_freeze() the cache will no |
169 | | * longer be "frozen". If the cache had grown larger than max_size |
170 | | * while frozen, entries will immediately be ejected (by random) from |
171 | | * the cache until the cache is smaller than max_size. Also, the |
172 | | * automatic ejection of entries on _cairo_cache_insert() will resume. |
173 | | **/ |
174 | | void |
175 | | _cairo_cache_thaw (cairo_cache_t *cache) |
176 | 3.99k | { |
177 | 3.99k | assert (cache->freeze_count > 0); |
178 | | |
179 | 3.99k | if (--cache->freeze_count == 0) |
180 | 3.99k | _cairo_cache_shrink_to_accommodate (cache, 0); |
181 | 3.99k | } |
182 | | |
183 | | /** |
184 | | * _cairo_cache_lookup: |
185 | | * @cache: a cache |
186 | | * @key: the key of interest |
187 | | * @entry_return: pointer for return value |
188 | | * |
189 | | * Performs a lookup in @cache looking for an entry which has a key |
190 | | * that matches @key, (as determined by the keys_equal() function |
191 | | * passed to _cairo_cache_init()). |
192 | | * |
193 | | * Return value: %TRUE if there is an entry in the cache that matches |
194 | | * @key, (which will now be in *entry_return). %FALSE otherwise, (in |
195 | | * which case *entry_return will be %NULL). |
196 | | **/ |
197 | | void * |
198 | | _cairo_cache_lookup (cairo_cache_t *cache, |
199 | | cairo_cache_entry_t *key) |
200 | 0 | { |
201 | 0 | return _cairo_hash_table_lookup (cache->hash_table, |
202 | 0 | (cairo_hash_entry_t *) key); |
203 | 0 | } |
204 | | |
205 | | /** |
206 | | * _cairo_cache_remove_random: |
207 | | * @cache: a cache |
208 | | * |
209 | | * Remove a random entry from the cache. |
210 | | * |
211 | | * Return value: %TRUE if an entry was successfully removed. |
212 | | * %FALSE if there are no entries that can be removed. |
213 | | **/ |
214 | | static cairo_bool_t |
215 | | _cairo_cache_remove_random (cairo_cache_t *cache) |
216 | 0 | { |
217 | 0 | cairo_cache_entry_t *entry; |
218 | |
|
219 | 0 | entry = _cairo_hash_table_random_entry (cache->hash_table, |
220 | 0 | cache->predicate); |
221 | 0 | if (unlikely (entry == NULL)) |
222 | 0 | return FALSE; |
223 | | |
224 | 0 | _cairo_cache_remove (cache, entry); |
225 | |
|
226 | 0 | return TRUE; |
227 | 0 | } |
228 | | |
229 | | /** |
230 | | * _cairo_cache_shrink_to_accommodate: |
231 | | * @cache: a cache |
232 | | * @additional: additional size requested in bytes |
233 | | * |
234 | | * If cache is not frozen, eject entries randomly until the size of |
235 | | * the cache is at least @additional bytes less than |
236 | | * cache->max_size. That is, make enough room to accommodate a new |
237 | | * entry of size @additional. |
238 | | **/ |
239 | | static void |
240 | | _cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, |
241 | | unsigned long additional) |
242 | 3.99k | { |
243 | 3.99k | while (cache->size + additional > cache->max_size) { |
244 | 0 | if (! _cairo_cache_remove_random (cache)) |
245 | 0 | return; |
246 | 0 | } |
247 | 3.99k | } |
248 | | |
249 | | /** |
250 | | * _cairo_cache_insert: |
251 | | * @cache: a cache |
252 | | * @entry: an entry to be inserted |
253 | | * |
254 | | * Insert @entry into the cache. If an entry exists in the cache with |
255 | | * a matching key, then the old entry will be removed first, (and the |
256 | | * entry_destroy() callback will be called on it). |
257 | | * |
258 | | * Return value: %CAIRO_STATUS_SUCCESS if successful or |
259 | | * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available. |
260 | | **/ |
261 | | cairo_status_t |
262 | | _cairo_cache_insert (cairo_cache_t *cache, |
263 | | cairo_cache_entry_t *entry) |
264 | 4.01k | { |
265 | 4.01k | cairo_status_t status; |
266 | | |
267 | 4.01k | if (entry->size && ! cache->freeze_count) |
268 | 0 | _cairo_cache_shrink_to_accommodate (cache, entry->size); |
269 | | |
270 | 4.01k | status = _cairo_hash_table_insert (cache->hash_table, |
271 | 4.01k | (cairo_hash_entry_t *) entry); |
272 | 4.01k | if (unlikely (status)) |
273 | 0 | return status; |
274 | | |
275 | 4.01k | cache->size += entry->size; |
276 | | |
277 | 4.01k | return CAIRO_STATUS_SUCCESS; |
278 | 4.01k | } |
279 | | |
280 | | /** |
281 | | * _cairo_cache_remove: |
282 | | * @cache: a cache |
283 | | * @entry: an entry that exists in the cache |
284 | | * |
285 | | * Remove an existing entry from the cache. |
286 | | **/ |
287 | | void |
288 | | _cairo_cache_remove (cairo_cache_t *cache, |
289 | | cairo_cache_entry_t *entry) |
290 | 1 | { |
291 | 1 | cache->size -= entry->size; |
292 | | |
293 | 1 | _cairo_hash_table_remove (cache->hash_table, |
294 | 1 | (cairo_hash_entry_t *) entry); |
295 | | |
296 | 1 | if (cache->entry_destroy) |
297 | 0 | cache->entry_destroy (entry); |
298 | 1 | } |
299 | | |
300 | | /** |
301 | | * _cairo_cache_foreach: |
302 | | * @cache: a cache |
303 | | * @cache_callback: function to be called for each entry |
304 | | * @closure: additional argument to be passed to @cache_callback |
305 | | * |
306 | | * Call @cache_callback for each entry in the cache, in a |
307 | | * non-specified order. |
308 | | **/ |
309 | | void |
310 | | _cairo_cache_foreach (cairo_cache_t *cache, |
311 | | cairo_cache_callback_func_t cache_callback, |
312 | | void *closure) |
313 | 0 | { |
314 | 0 | _cairo_hash_table_foreach (cache->hash_table, |
315 | 0 | cache_callback, |
316 | 0 | closure); |
317 | 0 | } |
318 | | |
319 | | uintptr_t |
320 | | _cairo_hash_string (const char *c) |
321 | 2.05k | { |
322 | | /* This is the djb2 hash. */ |
323 | 2.05k | uintptr_t hash = _CAIRO_HASH_INIT_VALUE; |
324 | 2.05k | while (c && *c) |
325 | 0 | hash = ((hash << 5) + hash) + *c++; |
326 | 2.05k | return hash; |
327 | 2.05k | } |
328 | | |
329 | | uintptr_t |
330 | | _cairo_hash_bytes (uintptr_t hash, |
331 | | const void *ptr, |
332 | | unsigned int length) |
333 | 16.2k | { |
334 | 16.2k | const uint8_t *bytes = ptr; |
335 | | /* This is the djb2 hash. */ |
336 | 155k | while (length--) |
337 | 139k | hash = ((hash << 5) + hash) + *bytes++; |
338 | 16.2k | return hash; |
339 | 16.2k | } |
340 | | |
341 | | uintptr_t |
342 | | _cairo_hash_uintptr (uintptr_t hash, |
343 | | uintptr_t u) |
344 | 0 | { |
345 | 0 | return _cairo_hash_bytes (hash, &u, sizeof(u)); |
346 | 0 | } |