source: releases/1.0.0/lib/lru_cache.c@ 293

Last change on this file since 293 was 252, checked in by tim, 14 years ago

updated pyregfi to work with regfi changes
renamed some functions for more clarity
fixed some issues related to talloc_reference

  • Property svn:keywords set to Id
File size: 7.9 KB
Line 
1/*
2 * Copyright (C) 2008-2009 Timothy D. Morgan
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 * $Id: lru_cache.c 252 2011-05-08 17:33:49Z tim $
18 */
19
20/**
21 * @file
22 */
23
24#include "lru_cache.h"
25
26
27#define LRU_CACHE_DEBUG 0
28
29/* XXX: really should replace this with a real universal hash or other
30 * fast HMAC.
31 */
32static uint32_t lru_cache_compute_hash(uint32_t num_buckets,
33 uint32_t secret,
34 const void* buf,
35 uint32_t buf_len)
36{
37 uint32_t i;
38 uint32_t ret_val = 0x243f6a88;
39 unsigned char* s = (unsigned char*)&secret;
40 const unsigned char* b = (unsigned char*)buf;
41
42 for(i=0; i<buf_len; i++)
43 ret_val = (ret_val+(i^s[i%4])*b[i]) % num_buckets;
44
45 return ret_val;
46}
47
48/* Returns approximately floor(log_2(n)) (log base 2 of n, floored)
49 * If n == 0, returns 0
50 */
51static uint32_t lru_cache_floor_log2(uint32_t n)
52{
53 uint32_t ret_val;
54
55 for(ret_val=31; ret_val > 1; ret_val--)
56 if((n & (1 << ret_val)) != 0)
57 return ret_val;
58
59 return 0;
60}
61
62#if 0
63static void lru_cache_print(lru_cache* ht)
64{
65 uint32_t i;
66 lru_cache_element* cur;
67
68 printf("from newest to oldest:\n");
69 for(cur=ht->newest; cur != NULL; cur=cur->older)
70 {
71 /* write(STDOUT_FILENO, cur->index, cur->index_len);*/
72 printf("%p", (void*)cur);
73 printf("\n");
74 if(cur->older == ht->newest)
75 {
76 printf("??? Loop in LRU list!!");
77 break;
78 }
79 }
80 printf("\n");
81
82 printf("table:\n");
83 for(i=0; i<ht->num_buckets; i++)
84 {
85 printf("%.8X: ", i);
86 for(cur=ht->table[i]; cur != NULL; cur=cur->next)
87 {
88 /* write(STDOUT_FILENO, cur->index, cur->index_len);*/
89 printf("%p", (void*)cur);
90 printf("|");
91
92 if(cur->next == ht->table[i])
93 {
94 printf("??? Loop in table chain!!");
95 break;
96 }
97 }
98 printf("\n");
99 }
100}
101#endif
102
103
104lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret)
105{
106 return lru_cache_create_ctx(NULL, max_keys, secret, false);
107}
108
109
110lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
111 uint32_t secret, bool talloc_data)
112{
113 lru_cache* ret_val;
114
115 ret_val = talloc(talloc_ctx, lru_cache);
116 if(ret_val == NULL)
117 return NULL;
118
119 if(max_keys == 0)
120 ret_val->num_buckets = 1024;
121 else if(max_keys == 1)
122 ret_val->num_buckets = 1;
123 else
124 {
125 ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys);
126 if(ret_val->num_buckets < 1)
127 ret_val->num_buckets = 1;
128 }
129
130 ret_val->table = talloc_array(ret_val,
131 lru_cache_element*, ret_val->num_buckets);
132 if(ret_val->table == NULL)
133 {
134 talloc_free(ret_val);
135 return NULL;
136 }
137
138 ret_val->oldest = NULL;
139 ret_val->newest = NULL;
140 ret_val->max_keys = max_keys;
141 ret_val->secret = secret;
142 ret_val->talloc_data = talloc_data;
143 ret_val->num_keys = 0;
144 memset(ret_val->table, 0, ret_val->num_buckets*sizeof(lru_cache_element*));
145
146 return ret_val;
147}
148
149
150void lru_cache_destroy(lru_cache* ht)
151{
152 ht->secret = 0;
153 talloc_unlink(NULL, ht);
154}
155
156
157
158bool lru_cache_update(lru_cache* ht, const void* index,
159 uint32_t index_len, void* data)
160{
161 uint32_t hash, lru_hash;
162 lru_cache_element* cur;
163 lru_cache_element* last = NULL;
164 lru_cache_element* e = NULL;
165 void* tmp_index;
166
167 hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len);
168 for(cur = ht->table[hash]; cur != NULL && e == NULL; cur=cur->next)
169 {
170 if((index_len == cur->index_len)
171 && memcmp(cur->index, index, index_len) == 0)
172 { e = cur; }
173 }
174
175 if(e != NULL)
176 { /* We found the index, so we're going to overwrite the data.
177 * We also need to reposition the element to the newest position,
178 * so remove it from the list for now.
179 */
180 if(ht->talloc_data)
181 talloc_unlink(e, e->data);
182
183 if(e->newer == NULL)
184 ht->newest = e->older;
185 else
186 e->newer->older = e->older;
187
188 if(e->older == NULL)
189 ht->oldest = e->newer;
190 else
191 e->older->newer = e->newer;
192 }
193 else
194 { /* We didn't find an identical index. */
195
196 if((ht->max_keys != 0) && (ht->num_keys >= ht->max_keys))
197 { /* Eliminate the least recently used item, but reuse the element
198 * structure to minimize reallocation.
199 */
200 e = ht->oldest;
201 if(ht->newest == ht->oldest)
202 {
203 ht->newest = NULL;
204 ht->oldest = NULL;
205 }
206 else
207 {
208 ht->oldest = e->newer;
209 e->newer->older = NULL;
210 }
211 e->newer = NULL;
212 e->older = NULL;
213
214 last = NULL;
215 lru_hash = lru_cache_compute_hash(ht->num_buckets, ht->secret,
216 e->index, e->index_len);
217 for(cur = ht->table[lru_hash]; cur != e && cur != NULL;
218 last=cur, cur=cur->next)
219 { continue; }
220
221 if(last == NULL)
222 ht->table[lru_hash] = e->next;
223 else
224 last->next = e->next;
225 e->next = NULL;
226
227 if(ht->talloc_data)
228 talloc_unlink(e, e->data);
229
230 tmp_index = talloc_realloc_size(e, e->index, index_len);
231 if(tmp_index == NULL)
232 {
233 talloc_free(e);
234 return false;
235 }
236 else
237 e->index = tmp_index;
238 }
239 else
240 { /* Brand new element because we have room to spare. */
241
242 e = talloc(ht->table, lru_cache_element);
243 if(e == NULL)
244 return false;
245
246 e->index = talloc_size(e, index_len);
247 if(e->index == NULL)
248 {
249 talloc_free(e);
250 return false;
251 }
252
253 /* New entry, increment counters. */
254 ht->num_keys++;
255 }
256 memcpy(e->index, index, index_len);
257 e->index_len = index_len;
258
259 e->next = ht->table[hash];
260 ht->table[hash] = e;
261 }
262 if(ht->talloc_data)
263 data = talloc_reference(e, data);
264 e->data = data;
265
266 /* Finally, let's insert the element to the newest position in the LRU list.*/
267 if(ht->newest != NULL)
268 ht->newest->newer = e;
269 e->newer = NULL;
270 e->older = ht->newest;
271 ht->newest = e;
272 if(ht->oldest == NULL)
273 ht->oldest = e;
274
275 return true;
276}
277
278
279void* lru_cache_find(lru_cache* ht, const void* index,
280 uint32_t index_len)
281{
282 uint32_t hash;
283 lru_cache_element* cur;
284
285 hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len);
286 for(cur = ht->table[hash]; (cur != NULL); cur = cur->next)
287 {
288 if((index_len == cur->index_len)
289 && memcmp(cur->index, index, index_len) == 0)
290 { break; }
291 }
292
293 if(cur != NULL && cur->newer != NULL)
294 { /* Need to move this element up to the newest slot. */
295
296 cur->newer->older = cur->older;
297
298 if(cur->older == NULL)
299 ht->oldest = cur->newer;
300 else
301 cur->older->newer = cur->newer;
302
303 cur->newer = NULL;
304 cur->older = ht->newest;
305 ht->newest->newer = cur;
306 ht->newest = cur;
307 }
308
309 if(cur != NULL)
310 return cur->data;
311 else
312 return NULL;
313}
314
315
316
317bool lru_cache_remove(lru_cache* ht, const void* index,
318 uint32_t index_len)
319{
320 uint32_t hash;
321 lru_cache_element* cur;
322 lru_cache_element* last = NULL;
323
324 hash = lru_cache_compute_hash(ht->num_buckets, ht->secret,
325 index, index_len);
326 for(cur=ht->table[hash]; (cur != NULL);
327 last=cur, cur=cur->next)
328 {
329 if((index_len == cur->index_len)
330 && memcmp(cur->index, index, index_len) == 0)
331 { break; }
332 }
333
334 if(cur == NULL)
335 return false;
336
337 /* Detach from list */
338 if(cur->newer == NULL)
339 ht->newest = cur->older;
340 else
341 cur->newer->older = cur->older;
342
343 if(cur->older == NULL)
344 ht->oldest = cur->newer;
345 else
346 cur->older->newer = cur->newer;
347
348 /* Detach from hash table */
349 if(last == NULL)
350 ht->table[hash] = cur->next;
351 else
352 last->next = cur->next;
353
354 talloc_free(cur);
355
356 /* Removing entry, decrement counters. */
357 ht->num_keys--;
358
359 return true;
360}
Note: See TracBrowser for help on using the repository browser.