/*
 *	This file implements some sort of filename -> un_index conversion
 *	cache. Speeds up for real FS's (where the inode numbers are NOT
 *	junk) and slows down for the silly FS's where the inode numbers
 *	are junk.
 *
 *	12/12/93, kay roemer.
 */

# include "cache.h"

# include "unix.h"

# include <mint/file.h>


# define CACHE_ENTRIES	10

struct lookup_cache
{
	short	valid;
# define CACHE_VALID	0
# define CACHE_DIRTY	1
	short	dev;
	long	inode;
	long	stamp;
# define MK_STAMP(time, date)	((((long)(date)) << 16) | (time))
	long	un_index;
};

static struct lookup_cache f_cache[CACHE_ENTRIES];

long
un_cache_lookup (char *name, long *index)
{
	XATTR attr;
	short dirty_idx, i;
	long r, stamp, fd;
	static short last_deleted = 0;
	
	r = f_xattr (1, name, &attr);
	if (r)
	{
		DEBUG (("unix: un_cache_lookup: Fxattr(%s) -> %ld", name, r));
		return r;
	}
	
	dirty_idx = -1;
	stamp = MK_STAMP (attr.mtime, attr.mdate);
	for (i = 0; i < CACHE_ENTRIES; i++)
	{
		if (f_cache[i].valid == CACHE_DIRTY)
		{
			dirty_idx = i;
			continue;
		}
		
		if (f_cache[i].inode == attr.index
			&& f_cache[i].dev == attr.dev)
		{
			if (f_cache[i].stamp == stamp)
			{
				*index = f_cache[i].un_index;
				return 0;
			}
			else
			{
				f_cache[i].valid = CACHE_DIRTY;
				dirty_idx = i;
				break;
			}
		}
	}
	
	/* No matching entry found. We drop dirty_entry or some other entry
	 * in a round robin manner if no dirty entry is left.
	 */
	fd = f_open (name, O_RDWR);
	if (fd < 0)
	{
		DEBUG (("unix: un_cache_lookup: Fopen(%s) -> %ld", name, fd));
		return fd;
	}
	
	r = f_read (fd, sizeof (*index), (char *) index);
	f_close (fd);
	
	if (r >= 0 && r != sizeof (*index))
		r = EACCES;
	
	if (r < 0)
	{
		DEBUG (("unix: un_namei: Could not read idx from %s", name));
		return r;
	}
	
	if (dirty_idx == -1)
	{
		dirty_idx = last_deleted++;
		if (last_deleted >= CACHE_ENTRIES)
			last_deleted = 0;
	}
	
	f_cache[dirty_idx].dev = attr.dev;
	f_cache[dirty_idx].inode = attr.index;
	f_cache[dirty_idx].stamp = stamp;
	f_cache[dirty_idx].un_index = *index;
	f_cache[dirty_idx].valid = CACHE_VALID;
	
	return 0;
}

void
un_cache_remove (char *name)
{
	XATTR attr;
	long r;
	short i;
	
	r = f_xattr (1, name, &attr);
	if (r) return;
	
	for (i = 0; i < CACHE_ENTRIES; i++)
	{
		if (f_cache[i].valid == CACHE_VALID
			&& f_cache[i].inode == attr.index
			&& f_cache[i].dev == attr.dev)
		{
			f_cache[i].valid = CACHE_DIRTY;
			return;
		}
	}
}

void
un_cache_init (void)
{
	int i;
	
	for (i = 0; i < CACHE_ENTRIES; i++)
		f_cache[i].valid = CACHE_DIRTY;
}
