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