gdcache.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #include "gd.h"
  2. #include "gdhelpers.h"
  3. #ifdef HAVE_LIBFREETYPE
  4. #define NEED_CACHE 1
  5. #endif
  6. #ifdef NEED_CACHE
  7. /*
  8. * gdcache.c
  9. *
  10. * Caches of pointers to user structs in which the least-recently-used
  11. * element is replaced in the event of a cache miss after the cache has
  12. * reached a given size.
  13. *
  14. * John Ellson (ellson@graphviz.org) Oct 31, 1997
  15. *
  16. * Test this with:
  17. * gcc -o gdcache -g -Wall -DTEST gdcache.c
  18. *
  19. * The cache is implemented by a singly-linked list of elements
  20. * each containing a pointer to a user struct that is being managed by
  21. * the cache.
  22. *
  23. * The head structure has a pointer to the most-recently-used
  24. * element, and elements are moved to this position in the list each
  25. * time they are used. The head also contains pointers to three
  26. * user defined functions:
  27. * - a function to test if a cached userdata matches some keydata
  28. * - a function to provide a new userdata struct to the cache
  29. * if there has been a cache miss.
  30. * - a function to release a userdata struct when it is
  31. * no longer being managed by the cache
  32. *
  33. * In the event of a cache miss the cache is allowed to grow up to
  34. * a specified maximum size. After the maximum size is reached then
  35. * the least-recently-used element is discarded to make room for the
  36. * new. The most-recently-returned value is always left at the
  37. * beginning of the list after retrieval.
  38. *
  39. * In the current implementation the cache is traversed by a linear
  40. * search from most-recent to least-recent. This linear search
  41. * probably limits the usefulness of this implementation to cache
  42. * sizes of a few tens of elements.
  43. */
  44. #include "gdcache.h"
  45. /*********************************************************/
  46. /* implementation */
  47. /*********************************************************/
  48. /* create a new cache */
  49. gdCache_head_t *
  50. gdCacheCreate (
  51. int size,
  52. gdCacheTestFn_t gdCacheTest,
  53. gdCacheFetchFn_t gdCacheFetch,
  54. gdCacheReleaseFn_t gdCacheRelease)
  55. {
  56. gdCache_head_t *head;
  57. head = (gdCache_head_t *) gdPMalloc(sizeof (gdCache_head_t));
  58. head->mru = NULL;
  59. head->size = size;
  60. head->gdCacheTest = gdCacheTest;
  61. head->gdCacheFetch = gdCacheFetch;
  62. head->gdCacheRelease = gdCacheRelease;
  63. return head;
  64. }
  65. void
  66. gdCacheDelete (gdCache_head_t * head)
  67. {
  68. gdCache_element_t *elem, *prev;
  69. elem = head->mru;
  70. while (elem)
  71. {
  72. (*(head->gdCacheRelease)) (elem->userdata);
  73. prev = elem;
  74. elem = elem->next;
  75. gdPFree ((char *) prev);
  76. }
  77. gdPFree ((char *) head);
  78. }
  79. void *
  80. gdCacheGet (gdCache_head_t * head, void *keydata)
  81. {
  82. int i = 0;
  83. gdCache_element_t *elem, *prev = NULL, *prevprev = NULL;
  84. void *userdata;
  85. elem = head->mru;
  86. while (elem)
  87. {
  88. if ((*(head->gdCacheTest)) (elem->userdata, keydata))
  89. {
  90. if (i)
  91. { /* if not already most-recently-used */
  92. /* relink to top of list */
  93. prev->next = elem->next;
  94. elem->next = head->mru;
  95. head->mru = elem;
  96. }
  97. return elem->userdata;
  98. }
  99. prevprev = prev;
  100. prev = elem;
  101. elem = elem->next;
  102. i++;
  103. }
  104. userdata = (*(head->gdCacheFetch)) (&(head->error), keydata);
  105. if (!userdata)
  106. {
  107. /* if there was an error in the fetch then don't cache */
  108. return NULL;
  109. }
  110. if (i < head->size)
  111. { /* cache still growing - add new elem */
  112. elem = (gdCache_element_t *) gdPMalloc(sizeof (gdCache_element_t));
  113. }
  114. else
  115. { /* cache full - replace least-recently-used */
  116. /* preveprev becomes new end of list */
  117. prevprev->next = NULL;
  118. elem = prev;
  119. (*(head->gdCacheRelease)) (elem->userdata);
  120. }
  121. /* relink to top of list */
  122. elem->next = head->mru;
  123. head->mru = elem;
  124. elem->userdata = userdata;
  125. return userdata;
  126. }
  127. /*********************************************************/
  128. /* test stub */
  129. /*********************************************************/
  130. #ifdef TEST
  131. #include <stdio.h>
  132. typedef struct
  133. {
  134. int key;
  135. int value;
  136. }
  137. key_value_t;
  138. static int
  139. cacheTest (void *map, void *key)
  140. {
  141. return (((key_value_t *) map)->key == *(int *) key);
  142. }
  143. static void *
  144. cacheFetch (char **error, void *key)
  145. {
  146. key_value_t *map;
  147. map = (key_value_t *) gdMalloc (sizeof (key_value_t));
  148. map->key = *(int *) key;
  149. map->value = 3;
  150. *error = NULL;
  151. return (void *) map;
  152. }
  153. static void
  154. cacheRelease (void *map)
  155. {
  156. gdFree ((char *) map);
  157. }
  158. int
  159. main (char *argv[], int argc)
  160. {
  161. gdCache_head_t *cacheTable;
  162. int elem, key;
  163. cacheTable = gdCacheCreate (3, cacheTest, cacheFetch, cacheRelease);
  164. key = 20;
  165. elem = *(int *) gdCacheGet (cacheTable, &key);
  166. key = 30;
  167. elem = *(int *) gdCacheGet (cacheTable, &key);
  168. key = 40;
  169. elem = *(int *) gdCacheGet (cacheTable, &key);
  170. key = 50;
  171. elem = *(int *) gdCacheGet (cacheTable, &key);
  172. key = 30;
  173. elem = *(int *) gdCacheGet (cacheTable, &key);
  174. key = 30;
  175. elem = *(int *) gdCacheGet (cacheTable, &key);
  176. gdCacheDelete (cacheTable);
  177. return 0;
  178. }
  179. #endif /* TEST */
  180. #endif /* NEED_CACHE */