1/*
2 * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
5
6#include <TypeConstants.h>
7
8#include "DebugSupport.h"
9#include "Entry.h"
10#include "IndexImpl.h"
11#include "NameIndex.h"
12#include "ramfs.h"
13#include "Volume.h"
14
15// NameIndexPrimaryKey
16class NameIndexPrimaryKey {
17public:
18	NameIndexPrimaryKey(const Entry *entry,
19						const char *name = NULL)
20		: entry(entry), name(name ? name : entry->GetName()) {}
21	NameIndexPrimaryKey(const char *name)
22		: entry(NULL), name(name) {}
23
24	const Entry	*entry;
25	const char	*name;
26};
27
28// NameIndexGetPrimaryKey
29class NameIndexGetPrimaryKey {
30public:
31	inline NameIndexPrimaryKey operator()(const Entry *a)
32	{
33		return NameIndexPrimaryKey(a);
34	}
35
36	inline NameIndexPrimaryKey operator()(const Entry *a) const
37	{
38		return NameIndexPrimaryKey(a);
39	}
40};
41
42
43// NameIndexPrimaryKeyCompare
44class NameIndexPrimaryKeyCompare
45{
46public:
47	inline int operator()(const NameIndexPrimaryKey &a,
48						  const NameIndexPrimaryKey &b) const
49	{
50		if (a.entry != NULL && a.entry == b.entry)
51			return 0;
52		return strcmp(a.name, b.name);
53	}
54};
55
56
57// EntryTree
58
59typedef TwoKeyAVLTree<Entry*, NameIndexPrimaryKey, NameIndexPrimaryKeyCompare,
60					  NameIndexGetPrimaryKey>
61		_EntryTree;
62
63class NameIndex::EntryTree : public _EntryTree {};
64
65
66// NameIndexEntryIterator
67class NameIndexEntryIterator : public AbstractIndexEntryIterator,
68	public EntryListener {
69public:
70	NameIndexEntryIterator();
71	virtual ~NameIndexEntryIterator();
72
73	virtual Entry *GetCurrent();
74	virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
75	virtual Entry *GetPrevious();
76	virtual Entry *GetNext();
77
78	virtual status_t Suspend();
79	virtual status_t Resume();
80
81	bool SetTo(NameIndex *index, const char *name, bool ignoreValue = false);
82
83	virtual void EntryRemoved(Entry *entry);
84
85private:
86	friend class NameIndex;
87
88	typedef AbstractIndexEntryIterator BaseClass;
89
90private:
91	NameIndex						*fIndex;
92	NameIndex::EntryTree::Iterator	fIterator;
93	bool							fSuspended;
94	bool							fIsNext;
95};
96
97
98// NameIndex
99
100// constructor
101NameIndex::NameIndex(Volume *volume)
102	: Index(volume, "name", B_STRING_TYPE, false),
103	  fEntries(new(nothrow) EntryTree)
104{
105	if (fInitStatus == B_OK && !fEntries)
106		fInitStatus = B_NO_MEMORY;
107	if (fInitStatus == B_OK) {
108		fInitStatus = fVolume->AddEntryListener(this,
109			NULL, ENTRY_LISTEN_ANY_ENTRY | ENTRY_LISTEN_ALL);
110	}
111}
112
113// destructor
114NameIndex::~NameIndex()
115{
116	if (fVolume)
117		fVolume->RemoveEntryListener(this, NULL);
118	if (fEntries)
119		delete fEntries;
120	// Actually we would need to maintain a list of iterators and unset the
121	// still existing iterators here. But since the name index is deleted
122	// when the volume is unmounted, there shouldn't be any iterators left
123	// anymore.
124}
125
126// CountEntries
127int32
128NameIndex::CountEntries() const
129{
130	return fEntries->CountItems();
131}
132
133// Changed
134status_t
135NameIndex::Changed(Entry *entry, const char *oldName)
136{
137	status_t error = B_BAD_VALUE;
138	if (entry && oldName) {
139		EntryTree::Iterator it;
140		Entry **foundEntry
141			= fEntries->Find(NameIndexPrimaryKey(entry, oldName), entry, &it);
142		if (foundEntry && *foundEntry == entry) {
143			it.Remove();
144			error = fEntries->Insert(entry);
145
146			// udpate live queries
147			_UpdateLiveQueries(entry, oldName, entry->GetName());
148		}
149	}
150	return error;
151}
152
153// EntryAdded
154void
155NameIndex::EntryAdded(Entry *entry)
156{
157	if (entry) {
158		fEntries->Insert(entry);
159
160		// udpate live queries
161		_UpdateLiveQueries(entry, NULL, entry->GetName());
162	}
163}
164
165// EntryRemoved
166void
167NameIndex::EntryRemoved(Entry *entry)
168{
169	if (entry) {
170		fEntries->Remove(entry, entry);
171
172		// udpate live queries
173		_UpdateLiveQueries(entry, entry->GetName(), NULL);
174	}
175}
176
177// InternalGetIterator
178AbstractIndexEntryIterator *
179NameIndex::InternalGetIterator()
180{
181	NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator;
182	if (iterator) {
183		if (!iterator->SetTo(this, NULL, true)) {
184			delete iterator;
185			iterator = NULL;
186		}
187	}
188	return iterator;
189}
190
191// InternalFind
192AbstractIndexEntryIterator *
193NameIndex::InternalFind(const uint8 *key, size_t length)
194{
195	if (!key || length == 0)
196		return NULL;
197
198	// if the key is not null-terminated, copy it
199	uint8 clonedKey[kMaxIndexKeyLength];
200	if (key[length - 1] != '\0') {
201		if (length >= kMaxIndexKeyLength)
202			length = kMaxIndexKeyLength - 1;
203
204		memcpy(clonedKey, key, length);
205		clonedKey[length] = '\0';
206		length++;
207		key = clonedKey;
208	}
209
210	NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator;
211	if (iterator) {
212		if (!iterator->SetTo(this, (const char *)key)) {
213			delete iterator;
214			iterator = NULL;
215		}
216	}
217	return iterator;
218}
219
220// _UpdateLiveQueries
221void
222NameIndex::_UpdateLiveQueries(Entry* entry, const char* oldName,
223	const char* newName)
224{
225	fVolume->UpdateLiveQueries(entry, entry->GetNode(), GetName(),
226		GetType(), (const uint8*)oldName, (oldName ? strlen(oldName) : 0),
227		(const uint8*)newName, (newName ? strlen(newName) : 0));
228}
229
230
231// NameIndexEntryIterator
232
233// constructor
234NameIndexEntryIterator::NameIndexEntryIterator()
235	: AbstractIndexEntryIterator(),
236	  fIndex(NULL),
237	  fIterator(),
238	  fSuspended(false),
239	  fIsNext(false)
240{
241}
242
243// destructor
244NameIndexEntryIterator::~NameIndexEntryIterator()
245{
246	SetTo(NULL, NULL);
247}
248
249// GetCurrent
250Entry *
251NameIndexEntryIterator::GetCurrent()
252{
253	return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
254}
255
256// GetCurrent
257Entry *
258NameIndexEntryIterator::GetCurrent(uint8 *buffer, size_t *keyLength)
259{
260	Entry *entry = GetCurrent();
261	if (entry) {
262		strncpy((char*)buffer, entry->GetName(), kMaxIndexKeyLength);
263		*keyLength = strlen(entry->GetName());
264	}
265	return entry;
266}
267
268// GetPrevious
269Entry *
270NameIndexEntryIterator::GetPrevious()
271{
272	if (fSuspended)
273		return NULL;
274	if (!(fIterator.GetCurrent() && fIsNext))
275		fIterator.GetPrevious();
276	fIsNext = false;
277	return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
278}
279
280// GetNext
281Entry *
282NameIndexEntryIterator::GetNext()
283{
284	if (fSuspended)
285		return NULL;
286	if (!(fIterator.GetCurrent() && fIsNext))
287		fIterator.GetNext();
288	fIsNext = false;
289	return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL);
290}
291
292// Suspend
293status_t
294NameIndexEntryIterator::Suspend()
295{
296	status_t error = (!fSuspended ? B_OK : B_BAD_VALUE);
297	if (error == B_OK) {
298		if (fIterator.GetCurrent()) {
299			error = fIndex->GetVolume()->AddEntryListener(this,
300				*fIterator.GetCurrent(), ENTRY_LISTEN_REMOVED);
301		}
302		if (error == B_OK)
303			fSuspended = true;
304	}
305	return error;
306}
307
308// Resume
309status_t
310NameIndexEntryIterator::Resume()
311{
312	status_t error = (fSuspended ? B_OK : B_BAD_VALUE);
313	if (error == B_OK) {
314		if (fIterator.GetCurrent()) {
315			error = fIndex->GetVolume()->RemoveEntryListener(this,
316				*fIterator.GetCurrent());
317		}
318		if (error == B_OK)
319			fSuspended = false;
320	}
321	return error;
322}
323
324// SetTo
325bool
326NameIndexEntryIterator::SetTo(NameIndex *index, const char *name,
327							  bool ignoreValue)
328{
329	Resume();
330	fIndex = index;
331	fSuspended = false;
332	fIsNext = false;
333	if (fIndex) {
334		if (ignoreValue) {
335			fIndex->fEntries->GetIterator(&fIterator);
336			return fIterator.GetCurrent();
337		}
338		return fIndex->fEntries->FindFirst(name, &fIterator);
339	}
340	return false;
341}
342
343// EntryRemoved
344void
345NameIndexEntryIterator::EntryRemoved(Entry */*entry*/)
346{
347	Resume();
348	fIsNext = GetNext();
349	Suspend();
350}
351
352