Blame src/gdcache.c

Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#	include "config.h"
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#include "gd.h"
Packit ed3af9
#include "gdhelpers.h"
Packit ed3af9
Packit ed3af9
#ifdef HAVE_LIBTTF
Packit ed3af9
#	define NEED_CACHE 1
Packit ed3af9
#else
Packit ed3af9
#ifdef HAVE_LIBFREETYPE
Packit ed3af9
#	define NEED_CACHE 1
Packit ed3af9
#endif
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#ifdef NEED_CACHE
Packit ed3af9
Packit ed3af9
/*
Packit ed3af9
 * gdcache.c
Packit ed3af9
 *
Packit ed3af9
 * Caches of pointers to user structs in which the least-recently-used
Packit ed3af9
 * element is replaced in the event of a cache miss after the cache has
Packit ed3af9
 * reached a given size.
Packit ed3af9
 *
Packit ed3af9
 * John Ellson  (ellson@graphviz.org)  Oct 31, 1997
Packit ed3af9
 *
Packit ed3af9
 * Test this with:
Packit ed3af9
 *               gcc -o gdcache -g -Wall -DTEST gdcache.c
Packit ed3af9
 *
Packit ed3af9
 * The cache is implemented by a singly-linked list of elements
Packit ed3af9
 * each containing a pointer to a user struct that is being managed by
Packit ed3af9
 * the cache.
Packit ed3af9
 *
Packit ed3af9
 * The head structure has a pointer to the most-recently-used
Packit ed3af9
 * element, and elements are moved to this position in the list each
Packit ed3af9
 * time they are used.  The head also contains pointers to three
Packit ed3af9
 * user defined functions:
Packit ed3af9
 *              - a function to test if a cached userdata matches some keydata
Packit ed3af9
 *              - a function to provide a new userdata struct to the cache
Packit ed3af9
 *          if there has been a cache miss.
Packit ed3af9
 *              - a function to release a userdata struct when it is
Packit ed3af9
 *          no longer being managed by the cache
Packit ed3af9
 *
Packit ed3af9
 * In the event of a cache miss the cache is allowed to grow up to
Packit ed3af9
 * a specified maximum size.  After the maximum size is reached then
Packit ed3af9
 * the least-recently-used element is discarded to make room for the
Packit ed3af9
 * new.  The most-recently-returned value is always left at the
Packit ed3af9
 * beginning of the list after retrieval.
Packit ed3af9
 *
Packit ed3af9
 * In the current implementation the cache is traversed by a linear
Packit ed3af9
 * search from most-recent to least-recent.  This linear search
Packit ed3af9
 * probably limits the usefulness of this implementation to cache
Packit ed3af9
 * sizes of a few tens of elements.
Packit ed3af9
 */
Packit ed3af9
Packit ed3af9
#include "gdcache.h"
Packit ed3af9
Packit ed3af9
/*********************************************************/
Packit ed3af9
/* implementation                                        */
Packit ed3af9
/*********************************************************/
Packit ed3af9
Packit ed3af9
/* create a new cache */
Packit ed3af9
gdCache_head_t *gdCacheCreate(int size,
Packit ed3af9
                              gdCacheTestFn_t gdCacheTest,
Packit ed3af9
                              gdCacheFetchFn_t gdCacheFetch,
Packit ed3af9
                              gdCacheReleaseFn_t gdCacheRelease)
Packit ed3af9
{
Packit ed3af9
	gdCache_head_t *head;
Packit ed3af9
Packit ed3af9
	head = (gdCache_head_t *)gdMalloc(sizeof(gdCache_head_t));
Packit ed3af9
	if(!head) {
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	head->mru = NULL;
Packit ed3af9
	head->size = size;
Packit ed3af9
	head->gdCacheTest = gdCacheTest;
Packit ed3af9
	head->gdCacheFetch = gdCacheFetch;
Packit ed3af9
	head->gdCacheRelease = gdCacheRelease;
Packit ed3af9
Packit ed3af9
	return head;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
void gdCacheDelete(gdCache_head_t *head)
Packit ed3af9
{
Packit ed3af9
	gdCache_element_t *elem, *prev;
Packit ed3af9
Packit ed3af9
	elem = head->mru;
Packit ed3af9
	while(elem) {
Packit ed3af9
		(*(head->gdCacheRelease))(elem->userdata);
Packit ed3af9
		prev = elem;
Packit ed3af9
		elem = elem->next;
Packit ed3af9
		gdFree((char *)prev);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	gdFree((char *)head);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
void * gdCacheGet(gdCache_head_t *head, void *keydata)
Packit ed3af9
{
Packit ed3af9
	int i = 0;
Packit ed3af9
	gdCache_element_t *elem, *prev = NULL, *prevprev = NULL;
Packit ed3af9
	void *userdata;
Packit ed3af9
Packit ed3af9
	elem = head->mru;
Packit ed3af9
	while(elem) {
Packit ed3af9
		if((*(head->gdCacheTest))(elem->userdata, keydata)) {
Packit ed3af9
			if(i) {
Packit ed3af9
				/* if not already most-recently-used */
Packit ed3af9
				/* relink to top of list */
Packit ed3af9
				prev->next = elem->next;
Packit ed3af9
				elem->next = head->mru;
Packit ed3af9
				head->mru = elem;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			return elem->userdata;
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		prevprev = prev;
Packit ed3af9
		prev = elem;
Packit ed3af9
		elem = elem->next;
Packit ed3af9
		i++;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	userdata = (*(head->gdCacheFetch))(&(head->error), keydata);
Packit ed3af9
	if(!userdata) {
Packit ed3af9
		/* if there was an error in the fetch then don't cache */
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if(i < head->size) {
Packit ed3af9
		/* cache still growing - add new elem */
Packit ed3af9
		elem = (gdCache_element_t *)gdMalloc(sizeof(gdCache_element_t));
Packit ed3af9
		if(!elem) {
Packit ed3af9
			(*(head->gdCacheRelease)) (userdata);
Packit ed3af9
			return NULL;
Packit ed3af9
		}
Packit ed3af9
	} else {
Packit ed3af9
		/* cache full - replace least-recently-used */
Packit ed3af9
		/* preveprev becomes new end of list */
Packit ed3af9
		prevprev->next = NULL;
Packit ed3af9
		elem = prev;
Packit ed3af9
		(*(head->gdCacheRelease))(elem->userdata);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* relink to top of list */
Packit ed3af9
	elem->next = head->mru;
Packit ed3af9
	head->mru = elem;
Packit ed3af9
	elem->userdata = userdata;
Packit ed3af9
Packit ed3af9
	return userdata;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/*********************************************************/
Packit ed3af9
/* test stub                                             */
Packit ed3af9
/*********************************************************/
Packit ed3af9
Packit ed3af9
#ifdef TEST
Packit ed3af9
Packit ed3af9
#include <stdio.h>
Packit ed3af9
Packit ed3af9
typedef struct {
Packit ed3af9
	int key;
Packit ed3af9
	int value;
Packit ed3af9
}
Packit ed3af9
key_value_t;
Packit ed3af9
Packit ed3af9
static int cacheTest(void *map, void *key)
Packit ed3af9
{
Packit ed3af9
	return (((key_value_t *)map)->key == *(int *)key);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static void *cacheFetch(char **error, void *key)
Packit ed3af9
{
Packit ed3af9
	key_value_t *map;
Packit ed3af9
Packit ed3af9
	map = (key_value_t *)gdMalloc(sizeof(key_value_t));
Packit ed3af9
	if (!map) {
Packit ed3af9
		*error = "gdMalloc failed";
Packit ed3af9
		return NULL;
Packit ed3af9
	}
Packit ed3af9
	map->key = *(int *)key;
Packit ed3af9
	map->value = 3;
Packit ed3af9
Packit ed3af9
	*error = NULL;
Packit ed3af9
Packit ed3af9
	return (void *)map;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static void cacheRelease(void *map)
Packit ed3af9
{
Packit ed3af9
	gdFree((char *)map);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
int main(char *argv[], int argc)
Packit ed3af9
{
Packit ed3af9
	gdCache_head_t *cacheTable;
Packit ed3af9
	int elem, key;
Packit ed3af9
Packit ed3af9
	cacheTable = gdCacheCreate(3, cacheTest, cacheFetch, cacheRelease);
Packit ed3af9
	if(!cacheTable) {
Packit ed3af9
		exit(1);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	key = 20;
Packit ed3af9
	elem = *(int *)gdCacheGet(cacheTable, &key);
Packit ed3af9
	key = 30;
Packit ed3af9
	elem = *(int *)gdCacheGet(cacheTable, &key);
Packit ed3af9
	key = 40;
Packit ed3af9
	elem = *(int *)gdCacheGet(cacheTable, &key);
Packit ed3af9
	key = 50;
Packit ed3af9
	elem = *(int *)gdCacheGet(cacheTable, &key);
Packit ed3af9
	key = 30;
Packit ed3af9
	elem = *(int *)gdCacheGet(cacheTable, &key);
Packit ed3af9
	key = 30;
Packit ed3af9
	elem = *(int *)gdCacheGet(cacheTable, &key);
Packit ed3af9
Packit ed3af9
	gdCacheDelete(cacheTable);
Packit ed3af9
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
#endif /* TEST */
Packit ed3af9
#endif /* NEED_CACHE */