source: trunk/lib/lru_cache.c @ 168

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

merged remaining smb_deps items into regfi

began formatting API comments for use with doxygen

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