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