gdcache.c 4.9 KB

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