source: trunk/lib/lru_cache.c @ 252

Last change on this file since 252 was 252, checked in by tim, 13 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.