1/*
2 * Copyright 2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Oliver Tappe <zooey@hirschkaefer.de>
7 *		Ingo Weinhold <ingo_weinhold@gmx.de>
8 */
9
10
11#include <package/PackageInfoSet.h>
12
13#include <new>
14
15#include <Referenceable.h>
16
17#include <AutoDeleter.h>
18
19#include <util/OpenHashTable.h>
20
21#include <package/PackageInfo.h>
22
23
24namespace BPackageKit {
25
26
27// #pragma mark - PackageInfo
28
29
30struct BPackageInfoSet::PackageInfo : public BPackageInfo {
31	PackageInfo*	hashNext;
32	PackageInfo*	listNext;
33
34	PackageInfo(const BPackageInfo& other)
35		:
36		BPackageInfo(other),
37		listNext(NULL)
38	{
39	}
40
41	void DeleteList()
42	{
43		PackageInfo* info = this;
44		while (info != NULL) {
45			PackageInfo* next = info->listNext;
46			delete info;
47			info = next;
48		}
49	}
50};
51
52
53// #pragma mark - PackageInfoHashDefinition
54
55
56struct BPackageInfoSet::PackageInfoHashDefinition {
57	typedef const char*		KeyType;
58	typedef	PackageInfo		ValueType;
59
60	size_t HashKey(const char* key) const
61	{
62		return BString::HashValue(key);
63	}
64
65	size_t Hash(const PackageInfo* value) const
66	{
67		return value->Name().HashValue();
68	}
69
70	bool Compare(const char* key, const PackageInfo* value) const
71	{
72		return value->Name() == key;
73	}
74
75	PackageInfo*& GetLink(PackageInfo* value) const
76	{
77		return value->hashNext;
78	}
79};
80
81
82// #pragma mark - PackageMap
83
84
85struct BPackageInfoSet::PackageMap : public BReferenceable,
86	public BOpenHashTable<PackageInfoHashDefinition> {
87
88	PackageMap()
89		:
90		fCount(0)
91	{
92	}
93
94	~PackageMap()
95	{
96		DeleteAllPackageInfos();
97	}
98
99	static PackageMap* Create()
100	{
101		PackageMap* map = new(std::nothrow) PackageMap;
102		if (map == NULL || map->Init() != B_OK) {
103			delete map;
104			return NULL;
105		}
106
107		return map;
108	}
109
110	PackageMap* Clone() const
111	{
112		PackageMap* newMap = Create();
113		if (newMap == NULL)
114			return NULL;
115		ObjectDeleter<PackageMap> newMapDeleter(newMap);
116
117		for (BPackageInfoSet::Iterator it(this); it.HasNext();) {
118			const BPackageInfo* info = it.Next();
119			if (newMap->AddNewPackageInfo(*info) != B_OK)
120				return NULL;
121		}
122
123		return newMapDeleter.Detach();
124	}
125
126	void AddPackageInfo(PackageInfo* info)
127	{
128		if (PackageInfo* oldInfo = Lookup(info->Name())) {
129			info->listNext = oldInfo->listNext;
130			oldInfo->listNext = info;
131		} else
132			Insert(info);
133
134		fCount++;
135	}
136
137	status_t AddNewPackageInfo(const BPackageInfo& oldInfo)
138	{
139		PackageInfo* info = new(std::nothrow) PackageInfo(oldInfo);
140		if (info == NULL)
141			return B_NO_MEMORY;
142		ObjectDeleter<PackageInfo> infoDeleter(info);
143
144		status_t error = info->InitCheck();
145		if (error != B_OK)
146			return error;
147
148		AddPackageInfo(infoDeleter.Detach());
149
150		return B_OK;
151	}
152
153	void DeleteAllPackageInfos()
154	{
155		PackageInfo* info = Clear(true);
156		while (info != NULL) {
157			PackageInfo* next = info->hashNext;
158			info->DeleteList();
159			info = next;
160		}
161	}
162
163	uint32 CountPackageInfos() const
164	{
165		return fCount;
166	}
167
168private:
169	uint32	fCount;
170};
171
172
173// #pragma mark - Iterator
174
175
176BPackageInfoSet::Iterator::Iterator(const PackageMap* map)
177	:
178	fMap(map),
179	fNextInfo(map != NULL ? map->GetIterator().Next() : NULL)
180{
181}
182
183
184bool
185BPackageInfoSet::Iterator::HasNext() const
186{
187	return fNextInfo != NULL;
188}
189
190
191const BPackageInfo*
192BPackageInfoSet::Iterator::Next()
193{
194	BPackageInfo* result = fNextInfo;
195
196	if (fNextInfo != NULL) {
197		if (fNextInfo->listNext != NULL) {
198			// get next in list
199			fNextInfo = fNextInfo->listNext;
200		} else {
201			// get next in hash table
202			PackageMap::Iterator iterator
203				= fMap->GetIterator(fNextInfo->Name());
204			iterator.Next();
205			fNextInfo = iterator.Next();
206		}
207	}
208
209	return result;
210}
211
212
213// #pragma mark - BPackageInfoSet
214
215
216BPackageInfoSet::BPackageInfoSet()
217	:
218	fPackageMap(NULL)
219{
220}
221
222
223BPackageInfoSet::~BPackageInfoSet()
224{
225	if (fPackageMap != NULL)
226		fPackageMap->ReleaseReference();
227}
228
229
230BPackageInfoSet::BPackageInfoSet(const BPackageInfoSet& other)
231	:
232	fPackageMap(other.fPackageMap)
233{
234	if (fPackageMap != NULL)
235		fPackageMap->AcquireReference();
236}
237
238
239status_t
240BPackageInfoSet::AddInfo(const BPackageInfo& info)
241{
242	if (!_CopyOnWrite())
243		return B_NO_MEMORY;
244
245	return fPackageMap->AddNewPackageInfo(info);
246}
247
248
249void
250BPackageInfoSet::MakeEmpty()
251{
252	if (fPackageMap == NULL || fPackageMap->CountPackageInfos() == 0)
253		return;
254
255	// If our map is shared, just set it to NULL.
256	if (fPackageMap->CountReferences() != 1) {
257		fPackageMap->ReleaseReference();
258		fPackageMap = NULL;
259		return;
260	}
261
262	// Our map is not shared -- make it empty.
263	fPackageMap->DeleteAllPackageInfos();
264}
265
266
267uint32
268BPackageInfoSet::CountInfos() const
269{
270	if (fPackageMap == NULL)
271		return 0;
272
273	return fPackageMap->CountPackageInfos();
274}
275
276
277BPackageInfoSet::Iterator
278BPackageInfoSet::GetIterator() const
279{
280	return Iterator(fPackageMap);
281}
282
283
284BPackageInfoSet&
285BPackageInfoSet::operator=(const BPackageInfoSet& other)
286{
287	if (other.fPackageMap == fPackageMap)
288		return *this;
289
290	if (fPackageMap != NULL)
291		fPackageMap->ReleaseReference();
292
293	fPackageMap = other.fPackageMap;
294
295	if (fPackageMap != NULL)
296		fPackageMap->AcquireReference();
297
298	return *this;
299}
300
301
302bool
303BPackageInfoSet::_CopyOnWrite()
304{
305	if (fPackageMap == NULL) {
306		fPackageMap = PackageMap::Create();
307		return fPackageMap != NULL;
308	}
309
310	if (fPackageMap->CountReferences() == 1)
311		return true;
312
313	PackageMap* newMap = fPackageMap->Clone();
314	if (newMap == NULL)
315		return false;
316
317	fPackageMap->ReleaseReference();
318	fPackageMap = newMap;
319	return true;
320}
321
322
323}	// namespace BPackageKit
324