1// ShareAttrDir.cpp
2
3#include <new>
4
5#include <stdlib.h>
6#include <string.h>
7
8#include <Node.h>
9
10#include "AttrDirInfo.h"
11#include "AutoDeleter.h"
12#include "ShareAttrDir.h"
13
14// compare_attributes
15//
16// NULL is considered the maximum
17static
18int
19compare_attributes(const Attribute* a, const Attribute* b)
20{
21	if (a == b)
22		return 0;
23	if (!a)
24		return 1;
25	if (!b)
26		return -1;
27
28	return strcmp(a->GetName(), b->GetName());
29}
30
31// compare_attributes
32static
33int
34compare_attributes(const void* _a, const void* _b)
35{
36	return compare_attributes(*(const Attribute**)_a, *(const Attribute**)_b);
37}
38
39// compare_iterators
40static
41int
42compare_iterators(const ShareAttrDirIterator* a, const ShareAttrDirIterator* b)
43{
44	return compare_attributes(a->GetCurrentAttribute(),
45		b->GetCurrentAttribute());
46}
47
48// compare_iterators
49static
50int
51compare_iterators(const void* _a, const void* _b)
52{
53	return compare_iterators(*(const ShareAttrDirIterator**)_a,
54		*(const ShareAttrDirIterator**)_b);
55}
56
57// constructor
58Attribute::Attribute(const char* name, const attr_info& info,
59	const void* data)
60	: fInfo(info)
61{
62	char* nameBuffer = fDataAndName;
63
64	// copy data, if any
65	if (data) {
66		nameBuffer += info.size;
67		memcpy(fDataAndName, data, info.size);
68
69		// store a negative size to indicate we also have the data
70		fInfo.size = -info.size;
71	}
72
73	// copy the name
74	strcpy(nameBuffer, name);
75}
76
77// destructor
78Attribute::~Attribute()
79{
80}
81
82// CreateAttribute
83status_t
84Attribute::CreateAttribute(const char* name, const attr_info& info,
85	const void* data, Attribute** attribute)
86{
87	if (!name || !attribute)
88		return B_BAD_VALUE;
89
90	// compute the size
91	int32 nameLen = strlen(name);
92	int32 size = sizeof(Attribute) + nameLen;
93	if (data)
94		size += info.size;
95
96	void* buffer = malloc(size);
97	if (!buffer)
98		return B_NO_MEMORY;
99
100	*attribute = new(buffer) Attribute(name, info, data);
101	return B_OK;
102}
103
104// DeleteAttribute
105void
106Attribute::DeleteAttribute(Attribute* attribute)
107{
108	if (attribute) {
109		attribute->~Attribute();
110		free(attribute);
111	}
112}
113
114// GetName
115const char*
116Attribute::GetName() const
117{
118	return (fInfo.size >= 0 ? fDataAndName : fDataAndName - fInfo.size);
119}
120
121// GetInfo
122void
123Attribute::GetInfo(attr_info* info) const
124{
125	if (info) {
126		info->type = fInfo.type;
127		info->size = GetSize();
128	}
129}
130
131// GetType
132uint32
133Attribute::GetType() const
134{
135	return fInfo.type;
136}
137
138// GetSize
139off_t
140Attribute::GetSize() const
141{
142	return (fInfo.size >= 0 ? fInfo.size : -fInfo.size);
143}
144
145// GetData
146const void*
147Attribute::GetData() const
148{
149	return (fInfo.size >= 0 ? NULL : fDataAndName);
150}
151
152
153// #pragma mark -
154
155// constructor
156ShareAttrDir::ShareAttrDir()
157	: fAttributes(),
158	  fRevision(-1),
159	  fUpToDate(false)
160{
161}
162
163// destructor
164ShareAttrDir::~ShareAttrDir()
165{
166	ClearAttrDir();
167}
168
169// Init
170status_t
171ShareAttrDir::Init(const AttrDirInfo& dirInfo)
172{
173	if (!dirInfo.isValid)
174		return B_BAD_VALUE;
175
176	// get the attributes
177	Attribute** attributes = NULL;
178	int32 count = 0;
179	status_t error = _GetAttributes(dirInfo, attributes, count);
180	if (error != B_OK)
181		return error;
182	ArrayDeleter<Attribute*> _(attributes);
183
184	// add the attributes
185	for (int32 i = 0; i < count; i++)
186		fAttributes.Insert(attributes[i]);
187
188	fRevision = dirInfo.revision;
189	fUpToDate = true;
190
191	return B_OK;
192}
193
194// Update
195status_t
196ShareAttrDir::Update(const AttrDirInfo& dirInfo,
197	DoublyLinkedList<ShareAttrDirIterator>* iterators)
198{
199	if (!dirInfo.isValid)
200		return B_BAD_VALUE;
201
202	if (fRevision >= dirInfo.revision)
203		return B_OK;
204
205	// allocate an array for the old attributes
206	int32 oldCount = fAttributes.Size();
207	Attribute** oldAttributes = new(std::nothrow) Attribute*[oldCount];
208	if (!oldAttributes)
209		return B_NO_MEMORY;
210	ArrayDeleter<Attribute*> _(oldAttributes);
211
212	// get the new attributes
213	Attribute** newAttributes = NULL;
214	int32 newCount = 0;
215	status_t error = _GetAttributes(dirInfo, newAttributes, newCount);
216	if (error != B_OK)
217		return error;
218	ArrayDeleter<Attribute*> _2(newAttributes);
219
220	// sort the iterators
221	int32 iteratorCount = (iterators ? iterators->Count() : 0);
222	if (iteratorCount > 0) {
223		// allocate an array
224		ShareAttrDirIterator** _iterators
225			= new(std::nothrow) ShareAttrDirIterator*[iteratorCount];
226		if (!_iterators)
227			return B_NO_MEMORY;
228		ArrayDeleter<ShareAttrDirIterator*> _3(_iterators);
229
230		// move the iterators
231		for (int32 i = 0; i < iteratorCount; i++) {
232			ShareAttrDirIterator* iterator = iterators->First();
233			_iterators[i] = iterator;
234			iterators->Remove(iterator);
235		}
236
237		// sort them
238		qsort(_iterators, iteratorCount, sizeof(ShareAttrDirIterator*),
239			compare_iterators);
240
241		// move them back into the list
242		for (int32 i = 0; i < iteratorCount; i++)
243			iterators->Insert(_iterators[i]);
244	}
245
246	// remove the old attributes
247	for (int32 i = 0; i < oldCount; i++) {
248		Attribute* attribute = fAttributes.GetFirst();
249		oldAttributes[i] = attribute;
250		fAttributes.Remove(attribute);
251	}
252
253	// add the new attributes
254	int32 oldIndex = 0;
255	int32 newIndex = 0;
256	ShareAttrDirIterator* iterator = (iterators ? iterators->First() : NULL);
257	while (oldIndex < oldCount || newIndex < newCount) {
258		Attribute* oldAttr = (oldCount > 0 ? oldAttributes[oldIndex] : NULL);
259		Attribute* newAttr = (newCount > 0 ? newAttributes[newIndex] : NULL);
260		int cmp = compare_attributes(oldAttr, newAttr);
261		if (cmp < 0) {
262			// oldAttr is obsolete: move all iterators pointing to it to the
263			// next new attribute
264			while (iterator && iterator->GetCurrentAttribute() == oldAttr) {
265				iterator->SetCurrentAttribute(newAttr);
266				iterator = iterators->GetNext(iterator);
267			}
268			oldIndex++;
269		} else if (cmp > 0) {
270			// newAttr is new
271			fAttributes.Insert(newAttr);
272			newIndex++;
273		} else {
274			// oldAttr == newAttr
275			fAttributes.Insert(newAttr);
276			oldIndex++;
277			newIndex++;
278
279			// move the attributes pointing to this attribute
280			while (iterator && iterator->GetCurrentAttribute() == oldAttr) {
281				iterator->SetCurrentAttribute(newAttr);
282				iterator = iterators->GetNext(iterator);
283			}
284		}
285	}
286
287	// delete the old attributes
288	for (int32 i = 0; i < oldCount; i++)
289		Attribute::DeleteAttribute(oldAttributes[i]);
290
291	fRevision = dirInfo.revision;
292	fUpToDate = true;
293
294	return B_OK;
295}
296
297// SetRevision
298void
299ShareAttrDir::SetRevision(int64 revision)
300{
301	fRevision = revision;
302}
303
304// GetRevision
305int64
306ShareAttrDir::GetRevision() const
307{
308	return fRevision;
309}
310
311// SetUpToDate
312void
313ShareAttrDir::SetUpToDate(bool upToDate)
314{
315	fUpToDate = upToDate;
316}
317
318// IsUpToDate
319bool
320ShareAttrDir::IsUpToDate() const
321{
322	return fUpToDate;
323}
324
325// ClearAttrDir
326void
327ShareAttrDir::ClearAttrDir()
328{
329	while (Attribute* attribute = GetFirstAttribute())
330		RemoveAttribute(attribute);
331}
332
333// AddAttribute
334status_t
335ShareAttrDir::AddAttribute(const char* name, const attr_info& info,
336	const void* data)
337{
338	if (!name || GetAttribute(name))
339		return B_BAD_VALUE;
340
341	// create the attribute
342	Attribute* attribute;
343	status_t error = Attribute::CreateAttribute(name, info, data, &attribute);
344	if (error != B_OK)
345		return error;
346
347	// add the attribute
348	fAttributes.Insert(attribute);
349
350	return B_OK;
351}
352
353// RemoveAttribute
354bool
355ShareAttrDir::RemoveAttribute(const char* name)
356{
357	if (!name)
358		return false;
359
360	for (SLList<Attribute>::Iterator it = fAttributes.GetIterator();
361		 it.HasNext();) {
362		Attribute* attribute = it.Next();
363		if (strcmp(attribute->GetName(), name) == 0) {
364			it.Remove();
365			Attribute::DeleteAttribute(attribute);
366			return true;
367		}
368	}
369
370	return false;
371}
372
373// RemoveAttribute
374void
375ShareAttrDir::RemoveAttribute(Attribute* attribute)
376{
377	if (!attribute)
378		return;
379
380	fAttributes.Remove(attribute);
381	Attribute::DeleteAttribute(attribute);
382}
383
384// GetAttribute
385Attribute*
386ShareAttrDir::GetAttribute(const char* name) const
387{
388	if (!name)
389		return NULL;
390
391	for (SLList<Attribute>::ConstIterator it = fAttributes.GetIterator();
392		 it.HasNext();) {
393		Attribute* attribute = it.Next();
394		if (strcmp(attribute->GetName(), name) == 0)
395			return attribute;
396	}
397
398	return NULL;
399}
400
401// GetFirstAttribute
402Attribute*
403ShareAttrDir::GetFirstAttribute() const
404{
405	return fAttributes.GetFirst();
406}
407
408// GetNextAttribute
409Attribute*
410ShareAttrDir::GetNextAttribute(Attribute* attribute) const
411{
412	return (attribute ? fAttributes.GetNext(attribute) : NULL);
413}
414
415// _GetAttributes
416status_t
417ShareAttrDir::_GetAttributes(const AttrDirInfo& dirInfo,
418	Attribute**& _attributes, int32& _count)
419{
420	if (!dirInfo.isValid)
421		return B_BAD_VALUE;
422
423	int32 count = dirInfo.attributeInfos.CountElements();
424	const AttributeInfo* attrInfos = dirInfo.attributeInfos.GetElements();
425
426	// allocate an attribute array
427	Attribute** attributes = NULL;
428	if (count > 0) {
429		attributes = new(std::nothrow) Attribute*[count];
430		if (!attributes)
431			return B_NO_MEMORY;
432		memset(attributes, 0, sizeof(Attribute*) * count);
433	}
434
435	status_t error = B_OK;
436	for (int32 i = 0; i < count; i++) {
437		const AttributeInfo& attrInfo = attrInfos[i];
438
439		// create the attribute
440		const void* data = attrInfo.data.GetData();
441		if (data && attrInfo.data.GetSize() != attrInfo.info.size)
442			data = NULL;
443		error = Attribute::CreateAttribute(attrInfo.name.GetString(),
444			attrInfo.info, data, attributes + i);
445		if (error != B_OK)
446			break;
447	}
448
449	// cleanup on error
450	if (error != B_OK) {
451		for (int32 i = 0; i < count; i++) {
452			if (Attribute* attribute = attributes[i])
453				Attribute::DeleteAttribute(attribute);
454		}
455		delete[] attributes;
456
457		return error;
458	}
459
460	// sort the attribute array
461	if (count > 0)
462		qsort(attributes, count, sizeof(Attribute*), compare_attributes);
463
464	_attributes = attributes;
465	_count = count;
466	return B_OK;
467}
468
469