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