xref: /haiku/src/kits/support/PointerList.cpp (revision 55b40aa53a835472ec7952b138ae4256203d02e4)
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 void
142 AbstractPointerListHelper::Swap(void **items, int32 i, int32 j)
143 {
144 	void *swap = items[i];
145 	items[i] = items[j];
146 	items[j] = swap;
147 }
148 
149 
150 int32
151 AbstractPointerListHelper::BinarySearchIndex(const void *key, const BList *list)
152 {
153 	int32 index;
154 	const void **items = static_cast<const void**>(list->Items());
155 	BinarySearch(key, items, list->CountItems(), index);
156 	return index;
157 }
158 
159 
160 void *
161 AbstractPointerListHelper::BinarySearch(const void *key, const BList *list)
162 {
163 	int32 index;
164 	const void **items = static_cast<const void**>(list->Items());
165 	return BinarySearch(key, items, list->CountItems(), index);
166 }
167 
168 
169 void
170 AbstractPointerListHelper::SortItems(BList *list)
171 {
172 	void **items = static_cast<void**>(list->Items());
173 	QuickSort(items, 0, list->CountItems()-1);
174 }
175 
176 
177 void
178 AbstractPointerListHelper::HSortItems(BList *list)
179 {
180 	void **items = static_cast<void**>(list->Items());
181 	int32 numItems = list->CountItems();
182 	if (numItems > 1) {
183 		// swap last with first item
184 		Swap(items, 0, numItems-1);
185 	}
186 	// sort all items but last item
187 	QuickSort(items, 0, numItems-2);
188 }
189 
190 
191 void *
192 AbstractPointerListHelper::BinarySearch(const void *key, const void **items, int32 numItems, int32 &index)
193 {
194 #if USE_STL
195 	const void** end = &items[numItems];
196 	const void** found = lower_bound(items, end, key, comparator(this));
197 	index = found - items;
198 	if (index != numItems && Compare(key, *found) == 0) {
199 		return const_cast<void*>(*found);
200 	} else {
201 		index = -(index + 1);
202 		return NULL;
203 	}
204 #else
205 	int32 low = 0;
206 	int32 high = numItems-1;
207 	int result = 0;
208 	index = 0;
209 
210 	while (low <= high) {
211 		index = (low + high) / 2;
212 		const void *item = items[index];
213 		result = Compare(key, item);
214 		if (result < 0) {
215 			// key < item
216 			high = index - 1;
217 		} else if (result > 0) {
218 			// key > item
219 			low = index + 1;
220 		} else {
221 			// key == item
222 			return const_cast<void *>(item);
223 		}
224 	}
225 	// item not found
226 	if (result > 0) {
227 		// key > last item (= items[index])
228 		// insert position for key is after last item
229 		index ++;
230 	}
231 
232 	index = -(index+1);
233 	return NULL;
234 #endif
235 }
236 
237 #if !USE_STL
238 int32
239 AbstractPointerListHelper::ChoosePivot(void **items, int32 low, int32 high)
240 {
241 	if (kPivotThreshold <= kQuickSortThreshold
242 		|| high - low > kPivotThreshold) {
243 		assert(high - low > kPivotThreshold);
244 		// choose the middle item of three items
245  		int32 mid = (low + high) / 2;
246 
247 		void* first = items[low];
248 		void* middle = items[mid];
249 		void* last = items[high];
250 
251 		if (Compare(first, middle) <= 0) {
252 			// first <= middle
253 			if (Compare(middle, last) <= 0) {
254 				// first <= middle <= last
255 				return mid;
256 			}
257 			// first <= middle and last < middle
258 			if (Compare(first, last) <= 0) {
259 				// first <= last < middle
260 				return high;
261 			}
262 			// last < first <= middle
263 			return low;
264 		}
265 		// middle < first
266 		if (Compare(first, last) <= 0) {
267 			// middle < first <= last
268 			return low;
269 		}
270 		// middle < first and last < first
271 		if (Compare(middle, last) <= 0) {
272 			// middle <= last < first
273 			return high;
274 		}
275 		// last < middle < first
276 		return mid;
277 	} else {
278 		// choose the middle element to avoid O(n^2) for an already sorted list
279 		return (low + high) / 2;
280 	}
281 }
282 
283 
284 int32
285 AbstractPointerListHelper::Partition(void **items, int32 low, int32 high, bool &isSorted)
286 {
287 	assert(low < high);
288 	int32 left  = low;
289 	int32 right = high;
290 	int32 pivot = ChoosePivot(items, low, high);
291 	void *pivotItem = items[pivot];
292 
293 	// Optimization: Check if all items are equal. We get this almost for free.
294 	// Searching the first item that does not belong to the left list has to
295 	// be done anyway.
296 	int32 result;
297 	isSorted = true;
298 	// Search first item in left part that does not belong to this part
299 	// (where item > pivotItem)
300 	while (left < right && (result = Compare(items[left], pivotItem)) <= 0) {
301 		left ++;
302 		if (result != 0) {
303 			isSorted = false;
304 			break;
305 		}
306 	}
307 	if (isSorted && left == right && Compare(items[right], pivotItem) == 0) {
308 		return low;
309 	}
310 	// End of optimization
311 	isSorted = false;
312 
313 	// pivot element has to be first element in list
314 	if (low != pivot)
315 		Swap(items, low, pivot);
316 
317 	// now partion the array in a left part where item <= pivotItem
318 	// and a right part where item > pivotItem
319 	do {
320 		// search first item in left part that does not belong to this part
321 		// (where item > pivotItem)
322 		while (left < right && Compare(items[left], pivotItem) <= 0) {
323 			left ++;
324 		}
325 		// search first item (from right to left) in right part that does not belong
326 		// to this part (where item <= pivotItem). This holds at least for pivot
327 		// element at top of list! No array bounds check needed!
328 		while (Compare(items[right], pivotItem) > 0) {
329 			right --;
330 		}
331 		if (left < right) {
332 			// now swap the items to the proper part
333 			Swap(items, left, right);
334 		}
335 	} while (left < right);
336 	// place pivotItem between left and right part
337 	items[low] = items[right];
338 	items[right] = pivotItem;
339 	return right;
340 }
341 
342 
343 void
344 AbstractPointerListHelper::InsertionSort(void **items, int32 numItems)
345 {
346 	for (int32 i = 1; i < numItems; i ++) {
347 		// treat list[0 .. i-1] as sorted
348 		void* item = items[i];
349 		// insert item at right place in list[0..i]
350 		int32 j = i;
351 		void* prev = items[j-1];
352 		while (Compare(prev, item) > 0) {
353 			items[j] = prev;
354 			j --;
355 			if (j <= 0) break;
356 			prev = items[j-1];
357 		}
358 		items[j] = item;
359 	}
360 }
361 
362 
363 void
364 AbstractPointerListHelper::InsertionSort(void **items, int32 low, int32 high)
365 {
366 	InsertionSort(&items[low], high - low + 1);
367 }
368 #endif
369 
370 void
371 AbstractPointerListHelper::QuickSort(void **items, int32 low, int32 high)
372 {
373 #if USE_STL
374 	if (low <= high) {
375 		sort(&items[low], &items[high+1], comparator(this));
376 	}
377 #else
378 	if (low < high) {
379 		if (high - low < kQuickSortThreshold) {
380 			InsertionSort(items, low, high);
381 		} else {
382 			bool isSorted;
383 			int pivot = Partition(items, low, high, isSorted);
384 			if (isSorted) return;
385 
386 			QuickSort(items, low, pivot - 1);
387 			QuickSort(items, pivot + 1, high);
388 		}
389 	}
390 #endif
391 }
392 
393 
394 class PointerListHelper : public AbstractPointerListHelper {
395 public:
396 	PointerListHelper(_PointerList_::GenericCompareFunction compareFunc)
397 		: fCompareFunc(compareFunc)
398 	{
399 		// nothing to do
400 	}
401 
402 	int Compare(const void *a, const void *b)
403 	{
404 		return fCompareFunc(a, b);
405 	}
406 
407 private:
408 	_PointerList_::GenericCompareFunction fCompareFunc;
409 };
410 
411 
412 class PointerListHelperWithState : public AbstractPointerListHelper
413 {
414 public:
415 	PointerListHelperWithState(
416 		_PointerList_::GenericCompareFunctionWithState compareFunc,
417 		void* state)
418 		: fCompareFunc(compareFunc)
419 		, fState(state)
420 	{
421 		// nothing to do
422 	}
423 
424 	int Compare(const void *a, const void *b)
425 	{
426 		return fCompareFunc(a, b, fState);
427 	}
428 
429 private:
430 	_PointerList_::GenericCompareFunctionWithState fCompareFunc;
431 	void* fState;
432 };
433 
434 
435 class PointerListHelperUsePredicate : public AbstractPointerListHelper
436 {
437 public:
438 	PointerListHelperUsePredicate(
439 		_PointerList_::UnaryPredicateGlue predicate)
440 		: fPredicate(predicate)
441 	{
442 		// nothing to do
443 	}
444 
445 	int Compare(const void *arg, const void *item)
446 	{
447 		// need to adapt arguments and return value
448 		return -fPredicate(item, const_cast<void *>(arg));
449 	}
450 
451 private:
452 	_PointerList_::UnaryPredicateGlue fPredicate;
453 };
454 
455 
456 // Implementation of class _PointerList_
457 
458 _PointerList_::_PointerList_(int32 itemsPerBlock, bool own)
459 	:
460 	BList(itemsPerBlock),
461 	owning(own)
462 {
463 
464 }
465 
466 
467 _PointerList_::_PointerList_(const _PointerList_ &list)
468 	:
469 	BList(list),
470 	owning(list.owning)
471 {
472 }
473 
474 
475 _PointerList_::~_PointerList_()
476 {
477 	// This is empty by design, the "owning" variable
478 	// is used by the BObjectList subclass
479 }
480 
481 
482 // Note: function pointers must not be NULL!!!
483 
484 void *
485 _PointerList_::EachElement(GenericEachFunction function, void *arg)
486 {
487 	int32 numItems = CountItems();
488 	void *result = NULL;
489 
490 	for (int32 index = 0; index < numItems; index++) {
491 		result = function(ItemAtFast(index), arg);
492 		if (result != NULL)
493 			break;
494 	}
495 
496 	return result;
497 }
498 
499 
500 void
501 _PointerList_::SortItems(GenericCompareFunction compareFunc)
502 {
503 	PointerListHelper helper(compareFunc);
504 	helper.SortItems(this);
505 }
506 
507 
508 void
509 _PointerList_::SortItems(GenericCompareFunctionWithState compareFunc, void *state)
510 {
511 	PointerListHelperWithState helper(compareFunc, state);
512 	helper.SortItems(this);
513 }
514 
515 
516 void
517 _PointerList_::HSortItems(GenericCompareFunction compareFunc)
518 {
519 	PointerListHelper helper(compareFunc);
520 	helper.HSortItems(this);
521 }
522 
523 
524 void
525 _PointerList_::HSortItems(GenericCompareFunctionWithState compareFunc, void *state)
526 {
527 	PointerListHelperWithState helper(compareFunc, state);
528 	helper.HSortItems(this);
529 }
530 
531 
532 void *
533 _PointerList_::BinarySearch(const void *key, GenericCompareFunction compareFunc) const
534 {
535 	PointerListHelper helper(compareFunc);
536 	return helper.BinarySearch(key, this);
537 }
538 
539 
540 void *
541 _PointerList_::BinarySearch(const void *key,
542 			GenericCompareFunctionWithState compareFunc, void *state) const
543 {
544 	PointerListHelperWithState helper(compareFunc, state);
545 	return helper.BinarySearch(key, this);
546 }
547 
548 
549 int32
550 _PointerList_::BinarySearchIndex(const void *key, GenericCompareFunction compareFunc) const
551 {
552 	PointerListHelper helper(compareFunc);
553 	return helper.BinarySearchIndex(key, this);
554 }
555 
556 
557 int32
558 _PointerList_::BinarySearchIndex(const void *key,
559 			GenericCompareFunctionWithState compareFunc, void *state) const
560 {
561 	PointerListHelperWithState helper(compareFunc, state);
562 	return helper.BinarySearchIndex(key, this);
563 }
564 
565 
566 int32
567 _PointerList_::BinarySearchIndexByPredicate(const void *key, UnaryPredicateGlue predicate) const
568 {
569 	PointerListHelperUsePredicate helper(predicate);
570 	return helper.BinarySearchIndex(key, this);
571 }
572 
573 bool
574 _PointerList_::ReplaceItem(int32 index, void *newItem)
575 {
576 	if (index < 0 || index >= CountItems())
577 		return false;
578 
579 	void **items = static_cast<void **>(Items());
580 	items[index] = newItem;
581 
582 	return true;
583 }
584 
585