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