/*

File FunctionCache.m

A cache remembers function values, f(x) = y, so that function evaluations can avoid lengthy recomputations.  Each cache has a finite capacity.  When a new value is added to the cache, the cache discards the value that has gone unused for the longest period of time.  It uses a callback function to alert the application that a value is being discarded.  In this way memory can be freed and reclaimed for other uses.

*/

#import <appkit/appkit.h>
#import <stdlib.h>

#import "FunctionCache.h"


/* 行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行  */


static void		freeObject	(void *);


/* 行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行  */


@implementation FunctionCache


- initKey: (const char *) key
		value: (const char *) value
		capacity: (unsigned) theCapacity
		freeKey: (freeFunction) theFreeKey
		freeValue: (freeFunction) theFreeValue;
{
	[super  init];
	
	cache = [[HashTable  alloc]
	
					initKeyDesc: key
					valueDesc: "!"
					capacity: theCapacity ];
	
	capacity = theCapacity;
	freeKey = theFreeKey;
	freeValue = theFreeValue;
	worst = best = NULL;
	
	return self;
}


- initKeyType: (int) key  valueType: (int) value  capacity: (unsigned) theCapacity;
{
	freeFunction	theFreeKey, theFreeValue;
	char			* type;
	
	switch (key)
	{
		case ISASTRING:		type = "*";		theFreeKey = free;			break;
		case ISAOBJECT:		type = "@";		theFreeKey = freeObject;	break;
		default:			type = NULL;	theFreeKey = NULL;			break;
	}
	
	switch (value)
	{
		case ISASTRING:		theFreeValue = free;		break;
		case ISAOBJECT:		theFreeValue = freeObject;	break;
		default:			theFreeValue = NULL;		break;
	}
	
	[self	initKey: type
			value: NULL
			capacity: theCapacity
			freeKey: theFreeKey
			freeValue: theFreeValue ];
			
	return self;
}


- free
{
	[self  setCapacity: 0];
	[cache  free];
	
	return [super  free];
}


- setCapacity: (unsigned) theCapacity
{
	while ([cache  count] > theCapacity) [self  remove: worst->key];
	capacity = theCapacity;
	
	return self;
}


/* 行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行  */


- add: (void *) key  value: (void *) value;
{
	cacheElement	* new;
	
	new = (cacheElement *) malloc(sizeof(cacheElement));
	new->key = key;
	new->value = value;
	new->previous = NULL;
	new->next = best;
	if (best != NULL) best->previous = new;
	
	[self  release: [self  detach: [cache  insertKey: key  value: new]]];
	
	best = new;
	if (worst == NULL) worst = new;
	if ([cache  count] > capacity) [self  remove: worst->key];
	
	return self;
}


- remove: (void *) key;
{
	[self  release: [self  detach: [cache  removeKey: key]]];
	return self;
}


- (void *) find: (void *) key;
{
	cacheElement	* cell;
	
	if ((cell = (cacheElement *) [cache  valueForKey: key]) != NULL)
	{
		[self  detach: cell];
		cell->next = best;
		if (best != NULL) best->previous = cell;
		best = cell;
		if (worst == NULL) worst = cell;
	}
	
	return (cell == NULL) ? NULL:cell->value;
}


- (cacheElement *) detach: (cacheElement *) cell
{
	if (cell != NULL)
	{
		if (cell->previous) cell->previous->next = cell->next;
		if (cell->next) cell->next->previous = cell->previous;
		if (best == cell) best = cell->next;
		if (worst == cell) worst = cell->previous;
		cell->previous = cell->next = NULL;
	}
	
	return cell;
}


- release: (cacheElement *) cell
{
	if (cell != NULL)
	{
		if (freeKey != NULL) (* freeKey)(cell->key);
		if (freeValue != NULL) (* freeValue)(cell->value);
		free(cell);
	}
	
	return self;
}


@end


/* 行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行行  */


static void	freeObject	(object)

void		* object;

{
	[(id) object  free];
}