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