source: trunk/lib/lru_cache.c @ 141

Last change on this file since 141 was 136, checked in by tim, 16 years ago

fixed various integer issues and memory allocation issues

polished error message functions and added initial messages in a few places

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