xref: /PHP-5.5/ext/gd/gdcache.c (revision 8a90aad3)
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 
39 #include "php.h"
40 
41 /* This just seems unessacary */
42 #if PHP_WIN32
43 #define ENABLE_GD_TTF
44 #else
45 #include <php_config.h>
46 #endif
47 #if HAVE_LIBFREETYPE && !defined(HAVE_GD_BUNDLED)
48 
49 #include "gdcache.h"
50 
51 /*********************************************************/
52 /* implementation                                        */
53 /*********************************************************/
54 
55 
56 /* create a new cache */
57 gdCache_head_t *
gdCacheCreate(int size,gdCacheTestFn_t gdCacheTest,gdCacheFetchFn_t gdCacheFetch,gdCacheReleaseFn_t gdCacheRelease)58 gdCacheCreate(
59 	int					size,
60 	gdCacheTestFn_t		gdCacheTest,
61 	gdCacheFetchFn_t	gdCacheFetch,
62 	gdCacheReleaseFn_t	gdCacheRelease )
63 {
64 	gdCache_head_t *head;
65 
66 	head = (gdCache_head_t *)pemalloc(sizeof(gdCache_head_t), 1);
67 	head->mru = NULL;
68 	head->size = size;
69 	head->gdCacheTest = gdCacheTest;
70 	head->gdCacheFetch = gdCacheFetch;
71 	head->gdCacheRelease = gdCacheRelease;
72 	return head;
73 }
74 
75 void
gdCacheDelete(gdCache_head_t * head)76 gdCacheDelete( gdCache_head_t *head )
77 {
78 	gdCache_element_t *elem, *prev;
79 
80 	elem = head->mru;
81 	while(elem) {
82 		(*(head->gdCacheRelease))(elem->userdata);
83 		prev = elem;
84 		elem = elem->next;
85 		pefree((char *)prev, 1);
86 	}
87 	pefree((char *)head, 1);
88 }
89 
90 void *
gdCacheGet(gdCache_head_t * head,void * keydata)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 	if (elem == NULL) {
99 		return NULL;
100 
101 	}
102 
103 	while(elem) {
104 		if ((*(head->gdCacheTest))(elem->userdata, keydata)) {
105 			if (i) {  /* if not already most-recently-used */
106 				/* relink to top of list */
107 				prev->next = elem->next;
108 				elem->next = head->mru;
109 				head->mru = elem;
110 			}
111 			return elem->userdata;
112 		}
113 		prevprev = prev;
114 		prev = elem;
115 		elem = elem->next;
116 		i++;
117 	}
118 	userdata = (*(head->gdCacheFetch))(&(head->error), keydata);
119 	if (! userdata) {
120 		/* if there was an error in the fetch then don't cache */
121 		return NULL;
122 	}
123 	if (i < head->size) {  /* cache still growing - add new elem */
124 		elem = (gdCache_element_t *)pemalloc(sizeof(gdCache_element_t), 1);
125 	}
126 	else { /* cache full - replace least-recently-used */
127 		/* preveprev becomes new end of list */
128 		prevprev->next = NULL;
129 		elem = prev;
130 		(*(head->gdCacheRelease))(elem->userdata);
131 	}
132 	/* relink to top of list */
133 	elem->next = head->mru;
134 	head->mru = elem;
135 	elem->userdata = userdata;
136 	return userdata;
137 }
138 
139 
140 
141 /*********************************************************/
142 /* test stub                                             */
143 /*********************************************************/
144 
145 
146 #ifdef GDCACHE_TEST
147 
148 #include <stdio.h>
149 
150 typedef struct {
151 	int key;
152 	int value;
153 } key_value_t;
154 
155 static int
cacheTest(void * map,void * key)156 cacheTest( void *map, void *key )
157 {
158 	return (((key_value_t *)map)->key == *(int *)key);
159 }
160 
161 static void *
cacheFetch(char ** error,void * key)162 cacheFetch( char **error, void *key )
163 {
164 	key_value_t *map;
165 
166 	map = (key_value_t *)malloc(sizeof(key_value_t));
167 	if (map == NULL) {
168 		return NULL;
169 	}
170 	map->key = *(int *)key;
171 	map->value = 3;
172 
173 	*error = NULL;
174 	return (void *)map;
175 }
176 
177 static void
cacheRelease(void * map)178 cacheRelease( void *map)
179 {
180 	free( (char *)map );
181 }
182 
183 int
main(char * argv[],int argc)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
210 
211 #endif /* ENABLE_GD_TTF */
212