xref: /haiku/src/kits/support/PointerList.cpp (revision 39241fe22890fb958b6ba32d6ab9526da98be187)
1 /*
2 ** Copyright 2003-2004, Stefano Ceccherini (burton666@libero.it). All rights reserved.
3 **           2004, Michael Pfeiffer (laplace@users.sourceforge.net).
4 ** Distributed under the terms of the Haiku License.
5 **
6 ** History
7 ** 2003-2004  Initial implementation by Stefano Ceccerini.
8 ** 2004/08/03 Testing, bug fixing and implementation of quick sort, refactoring
9 **            by Michael Pfeiffer.
10 */
11 
12 // TODO:
13 //   - Rewrite to use STL
14 //   - Include ObjectList.h
15 //   - Test if building with jam works
16 
17 // Note: Method Owning() is inlined in header file ObjectList.h
18 
19 #include <stl.h>
20 
21 #include <assert.h>
22 #include <List.h>
23 
24 // If USE_STL is 1 binary search and sort are used from STL.
25 // The implementation of _PointerList_ should be completely rewritten to
26 // use STL in a more efficent way!
27 #define USE_STL 1
28 
29 // Declaration of class _PointerList_ is inlined to be independent of the
30 // header file ObjectList.h
31 
32 #ifndef __OBJECT_LIST__
33 class _PointerList_ : public BList {
34 public:
35 	_PointerList_(const _PointerList_ &list);
36 	_PointerList_(int32 itemsPerBlock = 20, bool owning = false);
37 	~_PointerList_();
38 
39 	typedef void *(* GenericEachFunction)(void *item, void *arg);
40 	typedef int (* GenericCompareFunction)(const void *key, const void *item);
41 	typedef int (* GenericCompareFunctionWithState)(const void *key, const void *item,
42 		void *state);
43 	typedef int (* UnaryPredicateGlue)(const void *item, void *key);
44 
45 	void *EachElement(GenericEachFunction, void *arg);
46 	void SortItems(GenericCompareFunction);
47 	void SortItems(GenericCompareFunctionWithState, void *state);
48 	void HSortItems(GenericCompareFunction);
49 	void HSortItems(GenericCompareFunctionWithState, void *state);
50 
51 	void *BinarySearch(const void *key, GenericCompareFunction) const;
52 	void *BinarySearch(const void *key, GenericCompareFunctionWithState, void *state) const;
53 
54 	int32 BinarySearchIndex(const void *key, GenericCompareFunction) const;
55 	int32 BinarySearchIndex(const void *key, GenericCompareFunctionWithState, void *state) const;
56 	int32 BinarySearchIndexByPredicate(const void *arg, UnaryPredicateGlue) const;
57 
58 	bool Owning() const;
59 	bool ReplaceItem(int32, void *);
60 
61 protected:
62 	bool owning;
63 
64 };
65 #endif
66 
67 struct comparator;
68 
69 class AbstractPointerListHelper {
70 public:
71 	AbstractPointerListHelper() {};
72 
73 	/**
74 		Returns the index of the item that matches key or
75 	    a negative number. Then -(index+1) is the insert position
76 	    of the item not in list.
77 	*/
78 	int32 BinarySearchIndex(const void *key, const BList *list);
79 	/**
80 		Returns the item that matches key or NULL if the item could
81 		not be found in the list.
82 	*/
83 	void* BinarySearch(const void *key, const BList *list);
84 	/**
85 		Sorts the items in list.
86 	*/
87 	void SortItems(BList *list);
88 	/**
89 		Removes the first item in list and appends it at the bottom of
90 		the list and sorts all items but the last item.
91 	*/
92 	void HSortItems(BList *list);
93 
94 #if USE_STL
95 	friend struct comparator;
96 #endif
97 
98 private:
99 	enum {
100 		// Use insertion sort if number of elements in list is less than
101 		// kQuickSortThreshold.
102 		kQuickSortThreshold = 11,
103 		// Use simple pivot element computation if number of elements in
104 		// list is less than kPivotThreshold.
105 		kPivotThreshold     = 5,
106 	};
107 
108 	// Methods that do the actual work:
109 	inline void Swap(void **items, int32 i, int32 j);
110 
111 	void* BinarySearch(const void *key, const void **items, int32 numItems, int32 &index);
112 #if !USE_STL
113 	void InsertionSort(void **items, int32 numItems);
114 	inline void InsertionSort(void **items, int32 low, int32 high);
115 	int32 ChoosePivot(void **items, int32 low, int32 high);
116 	int32 Partition(void **items, int32 low, int32 high, bool &isSorted);
117 #endif
118 	void QuickSort(void **items, int32 low, int32 high);
119 
120 	// Method to be implemented by sub classes
121 	int virtual Compare(const void *key, const void* item) = 0;
122 };
123 
124 #if USE_STL
125 struct comparator : public binary_function<const void*, const void*, bool>
126 {
127 	comparator(AbstractPointerListHelper* helper) : helper(helper) {}
128 
129 	bool operator()(const void* a, const void* b) {
130 		return helper->Compare(b, a) > 0;
131 	}
132 
133 	AbstractPointerListHelper* helper;
134 };
135 #endif
136 
137 void
138 AbstractPointerListHelper::Swap(void **items, int32 i, int32 j)
139 {
140 	void *swap = items[i];
141 	items[i] = items[j];
142 	items[j] = swap;
143 }
144 
145 
146 int32
147 AbstractPointerListHelper::BinarySearchIndex(const void *key, const BList *list)
148 {
149 	int32 index;
150 	const void **items = static_cast<const void**>(list->Items());
151 	BinarySearch(key, items, list->CountItems(), index);
152 	return index;
153 }
154 
155 
156 void *
157 AbstractPointerListHelper::BinarySearch(const void *key, const BList *list)
158 {
159 	int32 index;
160 	const void **items = static_cast<const void**>(list->Items());
161 	return BinarySearch(key, items, list->CountItems(), index);
162 }
163 
164 
165 void
166 AbstractPointerListHelper::SortItems(BList *list)
167 {
168 	void **items = static_cast<void**>(list->Items());
169 	QuickSort(items, 0, list->CountItems()-1);
170 }
171 
172 
173 void
174 AbstractPointerListHelper::HSortItems(BList *list)
175 {
176 	void **items = static_cast<void**>(list->Items());
177 	int32 numItems = list->CountItems();
178 	if (numItems > 1) {
179 		// swap last with first item
180 		Swap(items, 0, numItems-1);
181 	}
182 	// sort all items but last item
183 	QuickSort(items, 0, numItems-2);
184 }
185 
186 
187 void *
188 AbstractPointerListHelper::BinarySearch(const void *key, const void **items, int32 numItems, int32 &index)
189 {
190 #if USE_STL
191 	const void** end = &items[numItems];
192 	const void** found = lower_bound(items, end, key, comparator(this));
193 	index = found - items;
194 	if (index != numItems && Compare(key, *found) == 0) {
195 		return const_cast<void*>(*found);
196 	} else {
197 		index = -(index + 1);
198 		return NULL;
199 	}
200 #else
201 	int32 low = 0;
202 	int32 high = numItems-1;
203 	int result = 0;
204 	index = 0;
205 
206 	while (low <= high) {
207 		index = (low + high) / 2;
208 		const void *item = items[index];
209 		result = Compare(key, item);
210 		if (result < 0) {
211 			// key < item
212 			high = index - 1;
213 		} else if (result > 0) {
214 			// key > item
215 			low = index + 1;
216 		} else {
217 			// key == item
218 			return const_cast<void *>(item);
219 		}
220 	}
221 	// item not found
222 	if (result > 0) {
223 		// key > last item (= items[index])
224 		// insert position for key is after last item
225 		index ++;
226 	}
227 
228 	index = -(index+1);
229 	return NULL;
230 #endif
231 }
232 
233 #if !USE_STL
234 int32
235 AbstractPointerListHelper::ChoosePivot(void **items, int32 low, int32 high)
236 {
237 	if (kPivotThreshold <= kQuickSortThreshold
238 		|| high - low > kPivotThreshold) {
239 		assert(high - low > kPivotThreshold);
240 		// choose the middle item of three items
241  		int32 mid = (low + high) / 2;
242 
243 		void* first = items[low];
244 		void* middle = items[mid];
245 		void* last = items[high];
246 
247 		if (Compare(first, middle) <= 0) {
248 			// first <= middle
249 			if (Compare(middle, last) <= 0) {
250 				// first <= middle <= last
251 				return mid;
252 			}
253 			// first <= middle and last < middle
254 			if (Compare(first, last) <= 0) {
255 				// first <= last < middle
256 				return high;
257 			}
258 			// last < first <= middle
259 			return low;
260 		}
261 		// middle < first
262 		if (Compare(first, last) <= 0) {
263 			// middle < first <= last
264 			return low;
265 		}
266 		// middle < first and last < first
267 		if (Compare(middle, last) <= 0) {
268 			// middle <= last < first
269 			return high;
270 		}
271 		// last < middle < first
272 		return mid;
273 	} else {
274 		// choose the middle element to avoid O(n^2) for an already sorted list
275 		return (low + high) / 2;
276 	}
277 }
278 
279 
280 int32
281 AbstractPointerListHelper::Partition(void **items, int32 low, int32 high, bool &isSorted)
282 {
283 	assert(low < high);
284 	int32 left  = low;
285 	int32 right = high;
286 	int32 pivot = ChoosePivot(items, low, high);
287 	void *pivotItem = items[pivot];
288 
289 	// Optimization: Check if all items are equal. We get this almost for free.
290 	// Searching the first item that does not belong to the left list has to
291 	// be done anyway.
292 	int32 result;
293 	isSorted = true;
294 	// Search first item in left part that does not belong to this part
295 	// (where item > pivotItem)
296 	while (left < right && (result = Compare(items[left], pivotItem)) <= 0) {
297 		left ++;
298 		if (result != 0) {
299 			isSorted = false;
300 			break;
301 		}
302 	}
303 	if (isSorted && left == right && Compare(items[right], pivotItem) == 0) {
304 		return low;
305 	}
306 	// End of optimization
307 	isSorted = false;
308 
309 	// pivot element has to be first element in list
310 	if (low != pivot)
311 		Swap(items, low, pivot);
312 
313 	// now partion the array in a left part where item <= pivotItem
314 	// and a right part where item > pivotItem
315 	do {
316 		// search first item in left part that does not belong to this part
317 		// (where item > pivotItem)
318 		while (left < right && Compare(items[left], pivotItem) <= 0) {
319 			left ++;
320 		}
321 		// search first item (from right to left) in right part that does not belong
322 		// to this part (where item <= pivotItem). This holds at least for pivot
323 		// element at top of list! No array bounds check needed!
324 		while (Compare(items[right], pivotItem) > 0) {
325 			right --;
326 		}
327 		if (left < right) {
328 			// now swap the items to the proper part
329 			Swap(items, left, right);
330 		}
331 	} while (left < right);
332 	// place pivotItem between left and right part
333 	items[low] = items[right];
334 	items[right] = pivotItem;
335 	return right;
336 }
337 
338 
339 void
340 AbstractPointerListHelper::InsertionSort(void **items, int32 numItems)
341 {
342 	for (int32 i = 1; i < numItems; i ++) {
343 		// treat list[0 .. i-1] as sorted
344 		void* item = items[i];
345 		// insert item at right place in list[0..i]
346 		int32 j = i;
347 		void* prev = items[j-1];
348 		while (Compare(prev, item) > 0) {
349 			items[j] = prev;
350 			j --;
351 			if (j <= 0) break;
352 			prev = items[j-1];
353 		}
354 		items[j] = item;
355 	}
356 }
357 
358 
359 void
360 AbstractPointerListHelper::InsertionSort(void **items, int32 low, int32 high)
361 {
362 	InsertionSort(&items[low], high - low + 1);
363 }
364 #endif
365 
366 void
367 AbstractPointerListHelper::QuickSort(void **items, int32 low, int32 high)
368 {
369 #if USE_STL
370 	if (low <= high) {
371 		sort(&items[low], &items[high+1], comparator(this));
372 	}
373 #else
374 	if (low < high) {
375 		if (high - low < kQuickSortThreshold) {
376 			InsertionSort(items, low, high);
377 		} else {
378 			bool isSorted;
379 			int pivot = Partition(items, low, high, isSorted);
380 			if (isSorted) return;
381 
382 			QuickSort(items, low, pivot - 1);
383 			QuickSort(items, pivot + 1, high);
384 		}
385 	}
386 #endif
387 }
388 
389 
390 class PointerListHelper : public AbstractPointerListHelper {
391 public:
392 	PointerListHelper(_PointerList_::GenericCompareFunction compareFunc)
393 		: fCompareFunc(compareFunc)
394 	{
395 		// nothing to do
396 	}
397 
398 	int Compare(const void *a, const void *b)
399 	{
400 		return fCompareFunc(a, b);
401 	}
402 
403 private:
404 	_PointerList_::GenericCompareFunction fCompareFunc;
405 };
406 
407 
408 class PointerListHelperWithState : public AbstractPointerListHelper
409 {
410 public:
411 	PointerListHelperWithState(
412 		_PointerList_::GenericCompareFunctionWithState compareFunc,
413 		void* state)
414 		: fCompareFunc(compareFunc)
415 		, fState(state)
416 	{
417 		// nothing to do
418 	}
419 
420 	int Compare(const void *a, const void *b)
421 	{
422 		return fCompareFunc(a, b, fState);
423 	}
424 
425 private:
426 	_PointerList_::GenericCompareFunctionWithState fCompareFunc;
427 	void* fState;
428 };
429 
430 
431 class PointerListHelperUsePredicate : public AbstractPointerListHelper
432 {
433 public:
434 	PointerListHelperUsePredicate(
435 		_PointerList_::UnaryPredicateGlue predicate)
436 		: fPredicate(predicate)
437 	{
438 		// nothing to do
439 	}
440 
441 	int Compare(const void *arg, const void *item)
442 	{
443 		// need to adapt arguments and return value
444 		return -fPredicate(item, const_cast<void *>(arg));
445 	}
446 
447 private:
448 	_PointerList_::UnaryPredicateGlue fPredicate;
449 };
450 
451 
452 // Implementation of class _PointerList_
453 
454 _PointerList_::_PointerList_(int32 itemsPerBlock, bool own)
455 	:
456 	BList(itemsPerBlock),
457 	owning(own)
458 {
459 
460 }
461 
462 
463 _PointerList_::_PointerList_(const _PointerList_ &list)
464 	:
465 	BList(list),
466 	owning(list.owning)
467 {
468 }
469 
470 
471 _PointerList_::~_PointerList_()
472 {
473 	// This is empty by design, the "owning" variable
474 	// is used by the BObjectList subclass
475 }
476 
477 
478 // Note: function pointers must not be NULL!!!
479 
480 void *
481 _PointerList_::EachElement(GenericEachFunction function, void *arg)
482 {
483 	int32 numItems = CountItems();
484 	void *result = NULL;
485 
486 	for (int32 index = 0; index < numItems; index++) {
487 		result = function(ItemAtFast(index), arg);
488 		if (result != NULL)
489 			break;
490 	}
491 
492 	return result;
493 }
494 
495 
496 void
497 _PointerList_::SortItems(GenericCompareFunction compareFunc)
498 {
499 	PointerListHelper helper(compareFunc);
500 	helper.SortItems(this);
501 }
502 
503 
504 void
505 _PointerList_::SortItems(GenericCompareFunctionWithState compareFunc, void *state)
506 {
507 	PointerListHelperWithState helper(compareFunc, state);
508 	helper.SortItems(this);
509 }
510 
511 
512 void
513 _PointerList_::HSortItems(GenericCompareFunction compareFunc)
514 {
515 	PointerListHelper helper(compareFunc);
516 	helper.HSortItems(this);
517 }
518 
519 
520 void
521 _PointerList_::HSortItems(GenericCompareFunctionWithState compareFunc, void *state)
522 {
523 	PointerListHelperWithState helper(compareFunc, state);
524 	helper.HSortItems(this);
525 }
526 
527 
528 void *
529 _PointerList_::BinarySearch(const void *key, GenericCompareFunction compareFunc) const
530 {
531 	PointerListHelper helper(compareFunc);
532 	return helper.BinarySearch(key, this);
533 }
534 
535 
536 void *
537 _PointerList_::BinarySearch(const void *key,
538 			GenericCompareFunctionWithState compareFunc, void *state) const
539 {
540 	PointerListHelperWithState helper(compareFunc, state);
541 	return helper.BinarySearch(key, this);
542 }
543 
544 
545 int32
546 _PointerList_::BinarySearchIndex(const void *key, GenericCompareFunction compareFunc) const
547 {
548 	PointerListHelper helper(compareFunc);
549 	return helper.BinarySearchIndex(key, this);
550 }
551 
552 
553 int32
554 _PointerList_::BinarySearchIndex(const void *key,
555 			GenericCompareFunctionWithState compareFunc, void *state) const
556 {
557 	PointerListHelperWithState helper(compareFunc, state);
558 	return helper.BinarySearchIndex(key, this);
559 }
560 
561 
562 int32
563 _PointerList_::BinarySearchIndexByPredicate(const void *key, UnaryPredicateGlue predicate) const
564 {
565 	PointerListHelperUsePredicate helper(predicate);
566 	return helper.BinarySearchIndex(key, this);
567 }
568 
569 bool
570 _PointerList_::ReplaceItem(int32 index, void *newItem)
571 {
572 	if (index < 0 || index >= CountItems())
573 		return false;
574 
575 	void **items = static_cast<void **>(Items());
576 	items[index] = newItem;
577 
578 	return true;
579 }
580 
581