HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component data.Cache_fifo by barry
expand copy to clipboardexpand
//a FIFO cache

data CacheEntry {
	char key[]
	byte value[]
	int size

	int index

	CacheEntry next
	CacheEntry prev

	CacheEntry ageNext
	CacheEntry agePrev
}

data CacheBucket {
	CacheEntry list
}

const int BUCKET_COUNT = 50
const int MAX_KEY_HASH = 10 //characters

component provides Cache {

	CacheBucket buckets[]
	CacheEntry ageList
	CacheEntry ageListEnd
	Mutex lock = new Mutex()

	int maxSize
	int curSize

	int hash(char key[])
		{
		int result = key[0]
		for (int i = 0; i < key.arrayLength && i < MAX_KEY_HASH; i++)
			{
			result = result * key[i]
			}
		
		return result % BUCKET_COUNT
		}

	Cache:Cache(int max)
		{
		maxSize = max
		buckets = new CacheBucket[BUCKET_COUNT]
		for (int i = 0; i < buckets.arrayLength; i++)
			{
			buckets[i] = new CacheBucket()
			}
		}
	
	void Cache:setSize(int max)
		{
		maxSize = max
		evict(curSize)
		}
	
	CacheEntry find(char key[])
		{
		CacheEntry w = buckets[hash(key)].list

		while (w != null)
			{
			if (w.key == key)
				{
				return w
				}
			
			w = w.next
			}
		
		return null
		}
	
	void deleteItem(CacheEntry c)
		{
		curSize -= c.size

		if (ageList === c)
			{
			ageList = c.ageNext
			if (ageList != null)
				ageList.agePrev = null
				else
				ageListEnd = null
			}
			else if (ageListEnd === c)
			{
			ageListEnd = c.agePrev
			if (ageListEnd != null)
				ageListEnd.ageNext = null
				else
				ageList = null
			}
			else
			{
			c.agePrev.ageNext = c.ageNext
			c.ageNext.agePrev = c.agePrev
			}
		
		c.ageNext = null
		c.agePrev = null

		if (buckets[c.index].list === c)
			{
			buckets[c.index].list = c.next
			if (buckets[c.index].list != null) buckets[c.index].list.prev = null
			}
			else
			{
			c.prev.next = c.next
			if (c.next != null) c.next.prev = c.prev
			}
		
		c.next = null
		c.prev = null
		}
	
	void evict(int size)
		{
		while (size > maxSize)
			{
			CacheEntry c = ageListEnd

			size -= c.size
			deleteItem(c)
			}
		}

	void Cache:put(char key[], byte value[])
		{
		int size = value.arrayLength
		mutex(lock)
			{
			if (size > maxSize) throw new Exception("item with key '$(key)' is larger than maximum cache size")
			
			//search for existing
			CacheEntry w = find(key)
			if (w != null)
				{
				deleteItem(w)
				}

			//eviction
			evict(curSize + size)

			//addition
			int index = hash(key)

			CacheEntry nce = new CacheEntry(key, value, size, index)

			CacheBucket bucket = buckets[index]
			nce.next = bucket.list
			if (bucket.list != null) bucket.list.prev = nce
			bucket.list = nce

			nce.ageNext = ageList
			if (ageList != null) ageList.agePrev = nce
			if (ageListEnd == null) ageListEnd = ageList
			ageList = nce

			curSize += size
			}
		}
	
	byte[] Cache:get(char key[])
		{
		mutex(lock)
			{
			CacheEntry w = find(key)

			if (w != null)
				{
				return w.value
				}
			
			return null
			}
		}
	
	void Cache:delete(char key[])
		{
		mutex(lock)
			{
			CacheEntry w = find(key)
			if (w != null) deleteItem(w)
			}
		}
}
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n data.Cache_fifo -m "reason for update" -u yourUsername
Version 1 (this version) by barry
Notes for this version: Adds a FIFO cache implementation.