1139825Simp#ifndef CACHE_H
2251709Sjeff#define CACHE_H
3148078Srwatson/* Cache - a template cache class
4148078Srwatson**
592654Sjeff** Copyright 2001 pinc Software. All Rights Reserved.
692654Sjeff** Released under the terms of the MIT license.
792654Sjeff*/
892654Sjeff
992654Sjeff
1092654Sjeff#include <SupportDefs.h>
1192654Sjeff
1292654Sjeff#include <stdio.h>
1392654Sjeff
1492654Sjeff
1592654Sjefftemplate<class T> class Cache
1692654Sjeff{
1792654Sjeff	public:
1892654Sjeff		class Cacheable
1992654Sjeff		{
2092654Sjeff			public:
2192654Sjeff				Cacheable()
2292654Sjeff					:
2392654Sjeff					prev(NULL),
2492654Sjeff					next(NULL),
2592654Sjeff					locked(0L),
2692654Sjeff					isDirty(false)
2792654Sjeff				{
2892654Sjeff				}
2992654Sjeff
3092654Sjeff				virtual ~Cacheable()
3192654Sjeff				{
3292654Sjeff					// perform your "undirty" work here
3392654Sjeff				}
3492654Sjeff
3592654Sjeff				virtual bool Equals(T) = 0;
3692654Sjeff
3792654Sjeff				Cacheable	*prev,*next;
3892654Sjeff				int32		locked;
39129906Sbmilekic				bool		isDirty;
4092654Sjeff		};
4192654Sjeff
42129906Sbmilekic	public:
4392654Sjeff		Cache(int32 max = 20)
4492654Sjeff			:
4592654Sjeff			fMaxInQueue(max),
4692654Sjeff			fCount(0),
4792654Sjeff			fHoldChanges(false),
48251709Sjeff			fMostRecentlyUsed(NULL),
49251709Sjeff			fLeastRecentlyUsed(NULL)
50251709Sjeff		{
5192654Sjeff		}
5292654Sjeff
5392654Sjeff		virtual ~Cache()
5492654Sjeff		{
5592654Sjeff			Clear(0,true);
5692654Sjeff		}
5792654Sjeff
5892654Sjeff		void Clear(int32 targetCount = 0,bool force = false)
5992654Sjeff		{
60129906Sbmilekic			Cacheable *entry = fLeastRecentlyUsed;
61129906Sbmilekic			while (entry)
62129906Sbmilekic			{
63129906Sbmilekic				Cacheable *prev = entry->prev;
64129906Sbmilekic				if (entry->locked <= 0 || force)
65129906Sbmilekic				{
66129906Sbmilekic					if (entry->next)
67169431Srwatson						entry->next->prev = prev;
68169431Srwatson					if (prev)
69169431Srwatson						prev->next = entry->next;
70169431Srwatson
71169431Srwatson					if (fLeastRecentlyUsed == entry)
72169431Srwatson						fLeastRecentlyUsed = prev;
73169431Srwatson					if (fMostRecentlyUsed == entry)
7492654Sjeff						fMostRecentlyUsed = prev;
7592654Sjeff
7692654Sjeff					delete entry;
7792654Sjeff
7892654Sjeff					if (--fCount <= targetCount)
7992654Sjeff						break;
8092654Sjeff				}
8192654Sjeff
8292654Sjeff				entry = prev;
8392654Sjeff			}
8492654Sjeff		}
8592654Sjeff
8692654Sjeff		void SetHoldChanges(bool hold)
8792654Sjeff		{
8892654Sjeff			fHoldChanges = hold;
8992654Sjeff			if (!hold)
9092654Sjeff				Clear(fMaxInQueue);
9192654Sjeff		}
9292654Sjeff
9392654Sjeff		status_t SetDirty(T data,bool dirty)
9492654Sjeff		{
9592654Sjeff			Cacheable *entry = Get(data);
9692654Sjeff			if (!entry)
9792654Sjeff				return B_ENTRY_NOT_FOUND;
9892654Sjeff
9992654Sjeff			entry->isDirty = dirty;
10092654Sjeff			return B_OK;
10192654Sjeff		}
10292654Sjeff
10392654Sjeff		status_t Lock(T data)
10492654Sjeff		{
10592654Sjeff			Cacheable *entry = Get(data);
10692654Sjeff			if (!entry)
10792654Sjeff				return B_ENTRY_NOT_FOUND;
10892654Sjeff
10992654Sjeff			entry->locked++;
110222163Salc			return B_OK;
11192654Sjeff		}
112249264Sglebius
113249264Sglebius		status_t Unlock(T data)
11492654Sjeff		{
11592654Sjeff			Cacheable *entry = Get(data);
11692654Sjeff			if (!entry)
11792654Sjeff				return B_ENTRY_NOT_FOUND;
11892654Sjeff
11992654Sjeff			entry->locked--;
12092654Sjeff			return B_OK;
12192654Sjeff		}
12292654Sjeff
12392654Sjeff		Cacheable *Get(T data)
12492654Sjeff		{
12592654Sjeff			Cacheable *entry = GetFromCache(data);
12692654Sjeff			if (entry)
127251709Sjeff			{
12892654Sjeff				if (fMostRecentlyUsed == entry)
12992654Sjeff					return entry;
13092654Sjeff
131200129Santoine				// remove entry from cache (to insert it at top of the MRU list)
13292654Sjeff				if (entry->prev)
13392654Sjeff					entry->prev->next = entry->next;
134200129Santoine				if (!entry->next)
13592654Sjeff				{
13692654Sjeff					if (fLeastRecentlyUsed == entry)
13792654Sjeff						fLeastRecentlyUsed = entry->prev;
13892654Sjeff					else
13992654Sjeff						fprintf(stderr,"Cache: fatal error, fLeastRecentlyUsed != entry\n");
14092654Sjeff				}
14192654Sjeff				else
14292654Sjeff					entry->next->prev = entry->prev;
14392654Sjeff			}
14492654Sjeff			else
14592654Sjeff			{
14692654Sjeff				entry = NewCacheable(data);
147205266Skmacy				if (entry)
148205266Skmacy					fCount++;
149205487Skmacy			}
150205487Skmacy
151205487Skmacy			if (entry)
152205298Skmacy			{
153205487Skmacy				// insert entry at the top of the MRU list
154205266Skmacy				entry->next = fMostRecentlyUsed;
155205266Skmacy				entry->prev = NULL;
15692654Sjeff
15792654Sjeff				if (fMostRecentlyUsed)
15892654Sjeff					fMostRecentlyUsed->prev = entry;
15992654Sjeff				else if (fLeastRecentlyUsed == NULL)
16092654Sjeff					fLeastRecentlyUsed = entry;
161120218Sjeff				else
162120218Sjeff					fprintf(stderr,"Cache: fatal error, fLeastRecently != NULL!\n");
163120218Sjeff				fMostRecentlyUsed = entry;
164205487Skmacy
16592654Sjeff				// remove old nodes from of the cache (if possible and necessary)
16692654Sjeff				if (!fHoldChanges
16792654Sjeff					&& fCount > fMaxInQueue
16892654Sjeff					&& fLeastRecentlyUsed)
16992654Sjeff					Clear(fMaxInQueue);
17092654Sjeff			}
171249313Sglebius			return entry;
172249313Sglebius		}
173205266Skmacy
17492654Sjeff	protected:
17592654Sjeff		Cacheable *GetFromCache(T data)
17692654Sjeff		{
17792654Sjeff			Cacheable *entry = fMostRecentlyUsed;
178129906Sbmilekic			while (entry)
179129906Sbmilekic			{
180129906Sbmilekic				if (entry->Equals(data))
181129906Sbmilekic					return entry;
182129906Sbmilekic				entry = entry->next;
183129906Sbmilekic			}
184252040Sjeff			return NULL;
185129906Sbmilekic		}
186129906Sbmilekic
187129906Sbmilekic		virtual Cacheable *NewCacheable(T data) = 0;
188129906Sbmilekic
189129906Sbmilekic		int32		fMaxInQueue, fCount;
190129906Sbmilekic		bool		fHoldChanges;
191129906Sbmilekic		Cacheable	*fMostRecentlyUsed;
192249313Sglebius		Cacheable	*fLeastRecentlyUsed;
193249313Sglebius};
194249313Sglebius
195252226Sjeff#endif	/* CACHE_H */
196249313Sglebius