1#ifndef CACHE_H
2#define CACHE_H
3/* Cache - a template cache class
4**
5** Copyright 2001 pinc Software. All Rights Reserved.
6*/
7
8
9#include <SupportDefs.h>
10
11#include <stdio.h>
12
13
14template<class T> class Cache
15{
16	public:
17		class Cacheable
18		{
19			public:
20				Cacheable()
21					:
22					prev(NULL),
23					next(NULL),
24					locked(0L),
25					isDirty(false)
26				{
27				}
28
29				virtual ~Cacheable()
30				{
31					// perform your "undirty" work here
32				}
33
34				virtual bool Equals(T) = 0;
35
36				Cacheable	*prev,*next;
37				int32		locked;
38				bool		isDirty;
39		};
40
41	public:
42		Cache(int32 max = 20)
43			:
44			fMaxInQueue(max),
45			fCount(0),
46			fHoldChanges(false),
47			fMostRecentlyUsed(NULL),
48			fLeastRecentlyUsed(NULL)
49		{
50		}
51
52		virtual ~Cache()
53		{
54			Clear(0,true);
55		}
56
57		void Clear(int32 targetCount = 0,bool force = false)
58		{
59			Cacheable *entry = fLeastRecentlyUsed;
60			while (entry)
61			{
62				Cacheable *prev = entry->prev;
63				if (entry->locked <= 0 || force)
64				{
65					if (entry->next)
66						entry->next->prev = prev;
67					if (prev)
68						prev->next = entry->next;
69
70					if (fLeastRecentlyUsed == entry)
71						fLeastRecentlyUsed = prev;
72					if (fMostRecentlyUsed == entry)
73						fMostRecentlyUsed = prev;
74
75					delete entry;
76
77					if (--fCount <= targetCount)
78						break;
79				}
80
81				entry = prev;
82			}
83		}
84
85		void SetHoldChanges(bool hold)
86		{
87			fHoldChanges = hold;
88			if (!hold)
89				Clear(fMaxInQueue);
90		}
91
92		status_t SetDirty(T data,bool dirty)
93		{
94			Cacheable *entry = Get(data);
95			if (!entry)
96				return B_ENTRY_NOT_FOUND;
97
98			entry->isDirty = dirty;
99			return B_OK;
100		}
101
102		status_t Lock(T data)
103		{
104			Cacheable *entry = Get(data);
105			if (!entry)
106				return B_ENTRY_NOT_FOUND;
107
108			entry->locked++;
109			return B_OK;
110		}
111
112		status_t Unlock(T data)
113		{
114			Cacheable *entry = Get(data);
115			if (!entry)
116				return B_ENTRY_NOT_FOUND;
117
118			entry->locked--;
119			return B_OK;
120		}
121
122		Cacheable *Get(T data)
123		{
124			Cacheable *entry = GetFromCache(data);
125			if (entry)
126			{
127				if (fMostRecentlyUsed == entry)
128					return entry;
129
130				// remove entry from cache (to insert it at top of the MRU list)
131				if (entry->prev)
132					entry->prev->next = entry->next;
133				if (!entry->next)
134				{
135					if (fLeastRecentlyUsed == entry)
136						fLeastRecentlyUsed = entry->prev;
137					else
138						fprintf(stderr,"Cache: fatal error, fLeastRecentlyUsed != entry\n");
139				}
140				else
141					entry->next->prev = entry->prev;
142			}
143			else
144			{
145				entry = NewCacheable(data);
146				if (entry)
147					fCount++;
148			}
149
150			if (entry)
151			{
152				// insert entry at the top of the MRU list
153				entry->next = fMostRecentlyUsed;
154				entry->prev = NULL;
155
156				if (fMostRecentlyUsed)
157					fMostRecentlyUsed->prev = entry;
158				else if (fLeastRecentlyUsed == NULL)
159					fLeastRecentlyUsed = entry;
160				else
161					fprintf(stderr,"Cache: fatal error, fLeastRecently != NULL!\n");
162				fMostRecentlyUsed = entry;
163
164				// remove old nodes from of the cache (if possible and necessary)
165				if (!fHoldChanges
166					&& fCount > fMaxInQueue
167					&& fLeastRecentlyUsed)
168					Clear(fMaxInQueue);
169			}
170			return entry;
171		}
172
173	protected:
174		Cacheable *GetFromCache(T data)
175		{
176			Cacheable *entry = fMostRecentlyUsed;
177			while (entry)
178			{
179				if (entry->Equals(data))
180					return entry;
181				entry = entry->next;
182			}
183			return NULL;
184		}
185
186		virtual Cacheable *NewCacheable(T data) = 0;
187
188		int32		fMaxInQueue, fCount;
189		bool		fHoldChanges;
190		Cacheable	*fMostRecentlyUsed;
191		Cacheable	*fLeastRecentlyUsed;
192};
193
194#endif	/* CACHE_H */
195