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