lrucache.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. #include "stx.h"
  2. #include "common.h"
  3. /*****************************************
  4. * Basic types definitions
  5. */
  6. struct _stx_centry {
  7. void *key; /* key for doing lookups */
  8. void *data; /* data in the cache */
  9. size_t weight; /* "weight" of this entry */
  10. struct _stx_centry *next; /* next entry */
  11. struct _stx_centry **pthis;
  12. stx_clist_t lru_link; /* for putting this entry on LRU list */
  13. int ref_count; /* use count for this entry */
  14. int delete_pending; /* pending delete flag */
  15. };
  16. struct _stx_cache {
  17. size_t max_size; /* max size of cache */
  18. size_t cur_size; /* current size of cache */
  19. size_t max_weight; /* cache capacity */
  20. size_t cur_weight; /* current total "weight" of all entries */
  21. size_t hash_size; /* size of hash table */
  22. stx_cache_entry_t **table; /* hash table for this cache */
  23. stx_clist_t lru_list; /* least-recently-used list */
  24. /* Cache stats */
  25. unsigned long hits; /* num cache hits */
  26. unsigned long lookups; /* num cache lookups */
  27. unsigned long inserts; /* num inserts */
  28. unsigned long deletes; /* num deletes */
  29. /* Functions */
  30. unsigned long (*key_hash_fn)(const void *);
  31. long (*key_cmp_fn)(const void *, const void *);
  32. void (*cleanup_fn)(void *, void *);
  33. };
  34. #define STX_CACHE_ENTRY_PTR(_qp) \
  35. ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link)))
  36. /*****************************************
  37. * Cache methods
  38. */
  39. stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight,
  40. size_t hash_size,
  41. unsigned long (*key_hash_fn)(const void *key),
  42. long (*key_cmp_fn)(const void *key1,
  43. const void *key2),
  44. void (*cleanup_fn)(void *key, void *data))
  45. {
  46. stx_cache_t *newcache;
  47. newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t));
  48. if (newcache == NULL)
  49. return NULL;
  50. newcache->table = (stx_cache_entry_t **)calloc(hash_size,
  51. sizeof(stx_cache_entry_t *));
  52. if (newcache->table == NULL) {
  53. free(newcache);
  54. return NULL;
  55. }
  56. newcache->max_size = max_size;
  57. newcache->max_weight = max_weight;
  58. newcache->hash_size = hash_size;
  59. STX_CLIST_INIT_CLIST(&(newcache->lru_list));
  60. newcache->key_hash_fn = key_hash_fn;
  61. newcache->key_cmp_fn = key_cmp_fn;
  62. newcache->cleanup_fn = cleanup_fn;
  63. return newcache;
  64. }
  65. void stx_cache_empty(stx_cache_t *cache)
  66. {
  67. size_t i;
  68. stx_cache_entry_t *entry, *next_entry;
  69. for (i = 0; i < cache->hash_size; i++) {
  70. entry = cache->table[i];
  71. while (entry) {
  72. next_entry = entry->next;
  73. stx_cache_entry_delete(cache, entry);
  74. entry = next_entry;
  75. }
  76. }
  77. }
  78. void stx_cache_traverse(stx_cache_t *cache,
  79. void (*callback)(void *key, void *data))
  80. {
  81. size_t i;
  82. stx_cache_entry_t *entry;
  83. for (i = 0; i < cache->hash_size; i++) {
  84. for (entry = cache->table[i]; entry; entry = entry->next) {
  85. if (!entry->delete_pending)
  86. (*callback)(entry->key, entry->data);
  87. }
  88. }
  89. }
  90. void stx_cache_traverse_lru(stx_cache_t *cache,
  91. void (*callback)(void *key, void *data),
  92. unsigned int n)
  93. {
  94. stx_clist_t *q;
  95. stx_cache_entry_t *entry;
  96. for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n;
  97. q = q->next, n--) {
  98. entry = STX_CACHE_ENTRY_PTR(q);
  99. (*callback)(entry->key, entry->data);
  100. }
  101. }
  102. void stx_cache_traverse_mru(stx_cache_t *cache,
  103. void (*callback)(void *key, void *data),
  104. unsigned int n)
  105. {
  106. stx_clist_t *q;
  107. stx_cache_entry_t *entry;
  108. for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n;
  109. q = q->prev, n--) {
  110. entry = STX_CACHE_ENTRY_PTR(q);
  111. (*callback)(entry->key, entry->data);
  112. }
  113. }
  114. size_t stx_cache_getsize(stx_cache_t *cache)
  115. {
  116. return cache->cur_size;
  117. }
  118. size_t stx_cache_getweight(stx_cache_t *cache)
  119. {
  120. return cache->cur_weight;
  121. }
  122. void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info)
  123. {
  124. info->max_size = cache->max_size;
  125. info->max_weight = cache->max_weight;
  126. info->hash_size = cache->hash_size;
  127. info->cur_size = cache->cur_size;
  128. info->cur_weight = cache->cur_weight;
  129. info->hits = cache->hits;
  130. info->lookups = cache->lookups;
  131. info->inserts = cache->inserts;
  132. info->deletes = cache->deletes;
  133. }
  134. /*****************************************
  135. * Cache entry methods
  136. */
  137. stx_cache_entry_t *stx_cache_entry_create(void *key, void *data,
  138. size_t weight)
  139. {
  140. stx_cache_entry_t *newentry;
  141. newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t));
  142. if (newentry == NULL)
  143. return NULL;
  144. newentry->key = key;
  145. newentry->data = data;
  146. newentry->weight = weight;
  147. return newentry;
  148. }
  149. void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry)
  150. {
  151. entry->delete_pending = 1;
  152. if (entry->ref_count > 0)
  153. return;
  154. if (entry->pthis) {
  155. *entry->pthis = entry->next;
  156. if (entry->next)
  157. entry->next->pthis = entry->pthis;
  158. cache->cur_size--;
  159. cache->cur_weight -= entry->weight;
  160. cache->deletes++;
  161. STX_CLIST_REMOVE_LINK(&(entry->lru_link));
  162. }
  163. if (cache->cleanup_fn)
  164. cache->cleanup_fn(entry->key, entry->data);
  165. entry->pthis = NULL;
  166. entry->key = NULL;
  167. entry->data = NULL;
  168. free(entry);
  169. }
  170. stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key)
  171. {
  172. unsigned long bucket;
  173. stx_cache_entry_t *entry;
  174. cache->lookups++;
  175. bucket = cache->key_hash_fn(key) % cache->hash_size;
  176. for (entry = cache->table[bucket]; entry; entry = entry->next) {
  177. if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0)
  178. break;
  179. }
  180. if (entry) {
  181. cache->hits++;
  182. if (entry->ref_count == 0)
  183. STX_CLIST_REMOVE_LINK(&(entry->lru_link));
  184. entry->ref_count++;
  185. }
  186. return entry;
  187. }
  188. void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry)
  189. {
  190. if (entry->ref_count == 0)
  191. return;
  192. entry->ref_count--;
  193. if (entry->ref_count == 0) {
  194. STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list));
  195. if (entry->delete_pending)
  196. stx_cache_entry_delete(cache, entry);
  197. }
  198. }
  199. int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry)
  200. {
  201. stx_cache_entry_t *old_entry;
  202. unsigned long bucket;
  203. /*
  204. * If cache capacity is exceeded, try to remove LRU entries till there is
  205. * enough room or LRU list is empty.
  206. */
  207. while (cache->cur_weight + entry->weight > cache->max_weight) {
  208. old_entry = stx_cache_entry_getlru(cache);
  209. if (!old_entry) {
  210. /* cache capacity is exceeded and all entries are in use */
  211. return -1;
  212. }
  213. stx_cache_entry_delete(cache, old_entry);
  214. }
  215. /* If cache size is exceeded, remove LRU entry */
  216. if (cache->cur_size >= cache->max_size) {
  217. old_entry = stx_cache_entry_getlru(cache);
  218. if (!old_entry) {
  219. /* cache size is exceeded and all entries are in use */
  220. return -1;
  221. }
  222. stx_cache_entry_delete(cache, old_entry);
  223. }
  224. /* Don't add duplicate entries in the cache */
  225. bucket = cache->key_hash_fn(entry->key) % cache->hash_size;
  226. for (old_entry = cache->table[bucket]; old_entry;
  227. old_entry = old_entry->next) {
  228. if (!old_entry->delete_pending &&
  229. cache->key_cmp_fn(entry->key, old_entry->key) == 0)
  230. break;
  231. }
  232. if (old_entry)
  233. stx_cache_entry_delete(cache, old_entry);
  234. /* Insert in the hash table */
  235. entry->next = cache->table[bucket];
  236. cache->table[bucket] = entry;
  237. entry->pthis = &cache->table[bucket];
  238. if (entry->next)
  239. entry->next->pthis = &entry->next;
  240. entry->ref_count++;
  241. cache->inserts++;
  242. cache->cur_size++;
  243. cache->cur_weight += entry->weight;
  244. return 0;
  245. }
  246. stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache)
  247. {
  248. if (STX_CLIST_IS_EMPTY(&(cache->lru_list)))
  249. return NULL;
  250. return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list)));
  251. }
  252. int stx_cache_entry_sizeof(void)
  253. {
  254. return (int)sizeof(stx_cache_entry_t);
  255. }
  256. void *stx_cache_entry_getdata(stx_cache_entry_t *entry)
  257. {
  258. return entry->data;
  259. }
  260. void *stx_cache_entry_getkey(stx_cache_entry_t *entry)
  261. {
  262. return entry->key;
  263. }
  264. size_t stx_cache_entry_getweight(stx_cache_entry_t *entry)
  265. {
  266. return entry->weight;
  267. }