File indexing completed on 2024-05-05 04:38:44
0001 /* 0002 SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #ifndef EMBEDDED_FREE_TREE 0008 #define EMBEDDED_FREE_TREE 0009 0010 #include "kdevvarlengtharray.h" 0011 0012 #include <QPair> 0013 0014 #include <cstdlib> 0015 #include <iostream> 0016 #include <limits> 0017 0018 //Uncomment this to search for tree-inconsistencies, however it's very expensive 0019 // #define DEBUG_FREEITEM_COUNT debugFreeItemCount(); verifyTreeConsistent(*m_centralFreeItem, 0, m_itemCount); 0020 #define DEBUG_FREEITEM_COUNT 0021 0022 /** 0023 * This file implements algorithms that allow managing a sorted list of items, and managing "free" items 0024 * for reuse efficiently in that list. Among those free items a tree is built, and they are traversed 0025 * on insertion/removal to manage free items in the tree. 0026 * 0027 * There is specific needs on the embedded items: 0028 * - They must be markable "invalid", so after they are deleted they can stay in their place as invalid items. 0029 * - While they are invalid, they still must be able to hold 2 integers, needed for managing the tree of free items. 0030 * - One integer is needed for each list to hold a pointer to the central free item. 0031 * 0032 * Only these functions must be used to manipulate the lists, from the beginning up. First create an empty list 0033 * and initialize centralFreeItem with -1, and then you start adding items. 0034 * 0035 * Since the list is sorted, and each item can be contained only once, these lists actually represent a set. 0036 * 0037 * EmbeddedTreeAlgorithms implements an efficient "contains" function that uses binary search within the list. 0038 */ 0039 0040 namespace KDevelop { 0041 ///Responsible for handling the items in the list 0042 ///This is an example. ItemHandler::rightChild(..) and ItemHandler::leftChild(..) must be values that must be able to hold the count of positive 0043 ///values that will be the maximum size of the list, and additionally -1. 0044 // template<class Data> 0045 // class ExampleItemHandler { 0046 // public: 0047 // ExampleItemHandler(const Data& data) : m_data(data) { 0048 // } 0049 // int ItemHandler::rightChild() const { 0050 // Q_ASSERT(0); 0051 // } 0052 // int ItemHandler::leftChild() const { 0053 // Q_ASSERT(0); 0054 // } 0055 // void ItemHandler::setLeftChild(int child) { 0056 // Q_ASSERT(0); 0057 // } 0058 // void setRightChild(int child) { 0059 // Q_ASSERT(0); 0060 // } 0061 // bool operator<(const StandardItemHandler& rhs) const { 0062 // Q_ASSERT(0); 0063 // } 0064 // //Copies this item into the given one 0065 // void copyTo(Data& data) const { 0066 // data = m_data; 0067 // } 0068 // 0069 // static void createFreeItem(Data& data) { 0070 // data = Data(); 0071 // } 0072 // 0073 // bool isFree() const { 0074 // Q_ASSERT(0); 0075 // } 0076 // 0077 // const Data& data() { 0078 // } 0079 // 0080 // private: 0081 // const Data& m_data; 0082 // }; 0083 0084 /** 0085 * Use this for several constant algorithms on sorted lists with free-trees 0086 * */ 0087 template<class Data, class ItemHandler> 0088 class EmbeddedTreeAlgorithms 0089 { 0090 0091 public: 0092 0093 EmbeddedTreeAlgorithms(const Data* items, uint itemCount, const int& centralFreeItem) : m_items(items) 0094 , m_itemCount(itemCount) 0095 , m_centralFreeItem(¢ralFreeItem) 0096 { 0097 } 0098 ~EmbeddedTreeAlgorithms() 0099 { 0100 } 0101 0102 ///Efficiently checks whether the item is contained in the set. 0103 ///If it is contained, returns the index. Else, returns -1. 0104 0105 int indexOf(const Data& data) 0106 { 0107 return indexOf(data, 0, m_itemCount); 0108 } 0109 0110 ///Searches the given item within the specified bounds. 0111 int indexOf(const Data& data, uint start, uint end) 0112 { 0113 while (1) { 0114 if (start >= end) 0115 return -1; 0116 0117 int center = (start + end) / 2; 0118 0119 //Skip free items, since they cannot be used for ordering 0120 for (; center < ( int )end;) { 0121 if (!ItemHandler::isFree(m_items[center])) 0122 break; 0123 ++center; 0124 } 0125 0126 if (center == ( int )end) { 0127 end = (start + end) / 2; //No non-free items found in second half, so continue search in the other 0128 } else { 0129 if (ItemHandler::equals(data, m_items[center])) { 0130 return center; 0131 } else if (data < m_items[center]) { 0132 end = (start + end) / 2; 0133 } else { 0134 //Continue search in second half 0135 start = center + 1; 0136 } 0137 } 0138 } 0139 } 0140 0141 ///Returns the first valid index that has a data-value larger or equal to @p data. 0142 ///Returns -1 if nothing is found. 0143 int lowerBound(const Data& data, int start, int end) 0144 { 0145 int currentBound = -1; 0146 while (1) { 0147 if (start >= end) 0148 return currentBound; 0149 0150 int center = (start + end) / 2; 0151 0152 //Skip free items, since they cannot be used for ordering 0153 for (; center < end;) { 0154 if (!ItemHandler::isFree(m_items[center])) 0155 break; 0156 ++center; 0157 } 0158 0159 if (center == end) { 0160 end = (start + end) / 2; //No non-free items found in second half, so continue search in the other 0161 } else { 0162 if (ItemHandler::equals(data, m_items[center])) { 0163 return center; 0164 } else if (data < m_items[center]) { 0165 currentBound = center; 0166 end = (start + end) / 2; 0167 } else { 0168 //Continue search in second half 0169 start = center + 1; 0170 } 0171 } 0172 } 0173 } 0174 0175 uint countFreeItems() const 0176 { 0177 return countFreeItemsInternal(*m_centralFreeItem); 0178 } 0179 uint countFreeItemsNaive() const 0180 { 0181 uint ret = 0; 0182 for (uint a = 0; a < m_itemCount; ++a) { 0183 if (ItemHandler::isFree(m_items[a])) 0184 ++ret; 0185 } 0186 0187 return ret; 0188 } 0189 0190 void verifyOrder() 0191 { 0192 Data last; 0193 0194 for (uint a = 0; a < m_itemCount; ++a) { 0195 if (!ItemHandler::isFree(m_items[a])) { 0196 if (!ItemHandler::isFree(last)) 0197 Q_ASSERT(last < m_items[a]); 0198 last = m_items[a]; 0199 } 0200 } 0201 } 0202 0203 void verifyTreeConsistent() 0204 { 0205 verifyTreeConsistentInternal(*m_centralFreeItem, 0, m_itemCount); 0206 Q_ASSERT(countFreeItems() == countFreeItemsNaive()); 0207 } 0208 0209 private: 0210 void verifyTreeConsistentInternal(int position, int lowerBound, int upperBound) 0211 { 0212 if (position == -1) 0213 return; 0214 Q_ASSERT(lowerBound <= position && position < upperBound); 0215 verifyTreeConsistentInternal(ItemHandler::leftChild(m_items[position]), lowerBound, position); 0216 verifyTreeConsistentInternal(ItemHandler::rightChild(m_items[position]), position + 1, upperBound); 0217 } 0218 0219 uint countFreeItemsInternal(int item) const 0220 { 0221 if (item == -1) 0222 return 0; 0223 0224 return 1 + countFreeItemsInternal(ItemHandler::leftChild(m_items[item])) + countFreeItemsInternal(ItemHandler::rightChild( 0225 m_items[ 0226 item])); 0227 } 0228 0229 const Data* m_items; 0230 uint m_itemCount; 0231 const int* m_centralFreeItem; 0232 }; 0233 0234 /**Use this to add items. 0235 * The added item must not be in the set yet! 0236 * General usage: 0237 * - Construct the object 0238 * - Check if newItemCount() equals the previous item-count. If not, construct 0239 * a new list as large as newItemCount, and call object.transferData to transfer the data 0240 * into the new list. The new size must match the returned newItemCount. 0241 * - Either call object.apply(), or let it be called automatically by the destructor. 0242 * @tparam increaseFraction By what fraction the list is increased when it needs to. For example the size will be increased by 1/5 if it's 5. 0243 * @tparam rebuildIfInsertionMoreExpensive The structure is rebuilt completely when an insertion needs a moving around of more than rebuildIfInsertionMoreExpensive times 0244 the count of items needed to be moved in worst case in a fresh tree. 0245 * After rebuilding the tree, the free space is evenly distributed, and thus insertions require much less moving. 0246 * */ 0247 template<class Data, class ItemHandler, int increaseFraction = 5, int rebuildIfInsertionMoreExpensive = 20> 0248 class EmbeddedTreeAddItem 0249 { 0250 0251 public: 0252 0253 EmbeddedTreeAddItem(Data* items, uint itemCount, int& centralFreeItem, const Data& add) : m_add(add) 0254 , m_items(items) 0255 , m_itemCount(itemCount) 0256 , m_centralFreeItem(¢ralFreeItem) 0257 , m_applied(false) 0258 , m_needResize(false) 0259 { 0260 m_needResize = !apply(); 0261 } 0262 ~EmbeddedTreeAddItem() 0263 { 0264 if (!m_applied) 0265 apply(true); 0266 } 0267 0268 ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then 0269 ///the data needs to be transferred into a new list using transferData 0270 uint newItemCount() const 0271 { 0272 if (!m_applied) { 0273 if (*m_centralFreeItem == -1) { 0274 uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); 0275 uint newItemCount = realItemCount + (realItemCount / increaseFraction); 0276 if (newItemCount <= m_itemCount) 0277 newItemCount = m_itemCount + 1; 0278 0279 return newItemCount; 0280 } else if (m_needResize) { 0281 uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); 0282 uint newItemCount = realItemCount + (realItemCount / increaseFraction); 0283 0284 return newItemCount; 0285 } 0286 } 0287 return m_itemCount; 0288 } 0289 0290 ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() 0291 void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) 0292 { 0293 DEBUG_FREEITEM_COUNT 0294 0295 uint currentRealCount = m_itemCount - countFreeItems(*m_centralFreeItem); 0296 // Q_ASSERT(currentRealCount + (currentRealCount/increaseFraction) == newCount); 0297 0298 //Create a new list where the items from m_items are put into newItems, with the free items evenly 0299 //distributed, and a clean balanced free-tree. 0300 uint newFreeCount = newCount - currentRealCount; 0301 uint freeItemRaster; 0302 if (newFreeCount) 0303 freeItemRaster = newCount / newFreeCount; 0304 else { 0305 freeItemRaster = newCount + 1; //No free items 0306 } 0307 0308 ///@todo Do not iterate through all items, instead use the free-tree and memcpy for the ranges between free items. 0309 ///Ideally, even the free-tree would be built on-the-fly. 0310 Q_ASSERT(freeItemRaster); 0311 uint offset = 0; 0312 uint insertedValidCount = 0; 0313 for (uint a = 0; a < newCount; ++a) { 0314 //Create new free items at the end of their raster range 0315 if (a % freeItemRaster == (freeItemRaster - 1)) { 0316 //We need to insert a free item 0317 ItemHandler::createFreeItem(newItems[a]); 0318 ++offset; 0319 } else { 0320 ++insertedValidCount; 0321 while (ItemHandler::isFree(m_items[a - offset]) && a - offset < m_itemCount) 0322 --offset; 0323 Q_ASSERT(a - offset < m_itemCount); 0324 newItems[a] = m_items[a - offset]; 0325 // Q_ASSERT(!ItemHandler::isFree(newItems[a])); 0326 } 0327 } 0328 0329 Q_ASSERT(insertedValidCount == m_itemCount - countFreeItems(*m_centralFreeItem)); 0330 // qCDebug(UTIL) << m_itemCount << newCount << offset; 0331 // Q_ASSERT(m_itemCount == newCount-offset); 0332 0333 m_items = newItems; 0334 m_itemCount = newCount; 0335 0336 if (newCentralFree) 0337 m_centralFreeItem = newCentralFree; 0338 0339 *m_centralFreeItem = buildFreeTree(newFreeCount, freeItemRaster, freeItemRaster - 1); 0340 0341 // qCDebug(UTIL) << "count of new free items:" << newFreeCount; 0342 0343 // Q_ASSERT(countFreeItems( *m_centralFreeItem ) == newFreeCount); 0344 0345 DEBUG_FREEITEM_COUNT 0346 } 0347 0348 ///Tries to put the item into the list. If the insertion would be too inefficient or is not possible, returns false, unless @param force is true 0349 bool apply(bool force = false) 0350 { 0351 if (m_applied) 0352 return true; 0353 0354 if (*m_centralFreeItem == -1) { 0355 Q_ASSERT(!force); 0356 return false; 0357 } 0358 0359 //Find the free item that is nearest to the target position in the item order 0360 int previousItem = -1; 0361 int currentItem = *m_centralFreeItem; 0362 int replaceCurrentWith = -1; 0363 0364 //In currentLowerBound and currentUpperBound, we count the smallest contiguous range between free 0365 //items that the added items needs to be sorted into. If the range is empty, the item can just be inserted. 0366 int currentLowerBound = 0; 0367 int currentUpperBound = m_itemCount; 0368 0369 //Now go down the chain, always into the items direction 0370 0371 while (1) { 0372 QPair<int, int> freeBounds = leftAndRightRealItems(currentItem); 0373 const Data& current(m_items[currentItem]); 0374 if (freeBounds.first != -1 && m_add < m_items[freeBounds.first]) { 0375 //Follow left child 0376 currentUpperBound = freeBounds.first + 1; 0377 0378 if (ItemHandler::leftChild(current) != -1) { 0379 //Continue traversing 0380 previousItem = currentItem; 0381 currentItem = ItemHandler::leftChild(current); 0382 } else { 0383 replaceCurrentWith = ItemHandler::rightChild(current); 0384 break; 0385 } 0386 } else if (freeBounds.second != -1 && m_items[freeBounds.second] < m_add) { 0387 //Follow right child 0388 currentLowerBound = freeBounds.second; 0389 0390 if (ItemHandler::rightChild(current) != -1) { 0391 //Continue traversing 0392 previousItem = currentItem; 0393 currentItem = ItemHandler::rightChild(current); 0394 } else { 0395 replaceCurrentWith = ItemHandler::leftChild(current); 0396 break; 0397 } 0398 } else { 0399 //We will use this item! So find a replacement for it in the tree, and update the structure 0400 force = true; 0401 currentLowerBound = currentUpperBound = currentItem; 0402 0403 int leftReplaceCandidate = -1, rightReplaceCandidate = -1; 0404 if (ItemHandler::leftChild(current) != -1) 0405 leftReplaceCandidate = rightMostChild(ItemHandler::leftChild(current)); 0406 if (ItemHandler::rightChild(current) != -1) 0407 rightReplaceCandidate = leftMostChild(ItemHandler::rightChild(current)); 0408 0409 ///@todo it's probably better using lowerBound and upperBound like in the "remove" version 0410 //Left and right bounds of all children of current 0411 int leftChildBound = leftMostChild(currentItem), rightChildBound = rightMostChild(currentItem); 0412 Q_ASSERT(leftChildBound != -1 && rightChildBound != -1); 0413 int childCenter = (leftChildBound + rightChildBound) / 2; 0414 0415 if (leftReplaceCandidate == -1 && rightReplaceCandidate == -1) { 0416 //We don't have a replace candidate, since there is no children 0417 Q_ASSERT(ItemHandler::leftChild(current) == -1); 0418 Q_ASSERT(ItemHandler::rightChild(current) == -1); 0419 } else if (rightReplaceCandidate == -1 || 0420 abs(leftReplaceCandidate - childCenter) < abs(rightReplaceCandidate - childCenter)) { 0421 //pick the left replacement, since it's more central 0422 Q_ASSERT(leftReplaceCandidate != -1); 0423 replaceCurrentWith = leftReplaceCandidate; 0424 0425 Data& replaceWith(m_items[replaceCurrentWith]); 0426 0427 if (replaceCurrentWith == ItemHandler::leftChild(current)) { 0428 //The left child of replaceWith can just stay as it is, and we just need to add the right child 0429 Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); 0430 } else { 0431 takeRightMostChild(ItemHandler::leftChild(current)); 0432 0433 //Since we'll be clearing the item, we have to put this childsomewhere else. 0434 // Either make it our new "left" child, or make it the new left children "rightmost" child. 0435 int addRightMostLeftChild = ItemHandler::leftChild(replaceWith); 0436 0437 ItemHandler::setLeftChild(replaceWith, -1); 0438 0439 Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); 0440 Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); 0441 0442 if (ItemHandler::leftChild(current) != -1) { 0443 Q_ASSERT(rightMostChild(ItemHandler::leftChild(current)) != replaceCurrentWith); 0444 Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild( 0445 current) < replaceCurrentWith); 0446 ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); 0447 0448 if (addRightMostLeftChild != -1) { 0449 int rightMostLeft = rightMostChild(ItemHandler::leftChild(replaceWith)); 0450 Q_ASSERT(rightMostLeft != -1); 0451 // Q_ASSERT(item(rightMostLeft).ItemHandler::rightChild() == -1); 0452 Q_ASSERT(rightMostLeft < addRightMostLeftChild); 0453 ItemHandler::setRightChild(m_items[rightMostLeft], addRightMostLeftChild); 0454 } 0455 } else { 0456 Q_ASSERT(addRightMostLeftChild == -1 || addRightMostLeftChild < replaceCurrentWith); 0457 ItemHandler::setLeftChild(replaceWith, addRightMostLeftChild); 0458 } 0459 } 0460 0461 Q_ASSERT(ItemHandler::rightChild(current) == -1 || 0462 replaceCurrentWith < ItemHandler::rightChild(current)); 0463 ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); 0464 } else { 0465 //pick the right replacement, since it's more central 0466 Q_ASSERT(rightReplaceCandidate != -1); 0467 replaceCurrentWith = rightReplaceCandidate; 0468 0469 Data& replaceWith(m_items[replaceCurrentWith]); 0470 0471 if (replaceCurrentWith == ItemHandler::rightChild(current)) { 0472 //The right child of replaceWith can just stay as it is, and we just need to add the left child 0473 Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); 0474 } else { 0475 takeLeftMostChild(ItemHandler::rightChild(current)); 0476 0477 //Since we'll be clearing the item, we have to put this childsomewhere else. 0478 // Either make it our new "right" child, or make it the new right children "leftmost" child. 0479 int addLeftMostRightChild = ItemHandler::rightChild(replaceWith); 0480 0481 ItemHandler::setRightChild(replaceWith, -1); 0482 0483 Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); 0484 Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); 0485 0486 if (ItemHandler::rightChild(current) != -1) { 0487 Q_ASSERT(leftMostChild(ItemHandler::rightChild(current)) != replaceCurrentWith); 0488 Q_ASSERT(ItemHandler::rightChild( 0489 current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); 0490 ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); 0491 0492 if (addLeftMostRightChild != -1) { 0493 int leftMostRight = leftMostChild(ItemHandler::rightChild(replaceWith)); 0494 Q_ASSERT(leftMostRight != -1); 0495 Q_ASSERT(ItemHandler::leftChild(m_items[leftMostRight]) == -1); 0496 Q_ASSERT(addLeftMostRightChild < leftMostRight); 0497 ItemHandler::setLeftChild(m_items[leftMostRight], addLeftMostRightChild); 0498 } 0499 } else { 0500 Q_ASSERT(addLeftMostRightChild == -1 || replaceCurrentWith < addLeftMostRightChild); 0501 ItemHandler::setRightChild(replaceWith, addLeftMostRightChild); 0502 } 0503 } 0504 0505 Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild( 0506 current) < replaceCurrentWith); 0507 ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); 0508 } 0509 break; 0510 } 0511 } 0512 0513 //We can insert now 0514 //currentItem and previousItem are the two items that best enclose the target item 0515 0516 // for(int a = currentLowerBound; a < currentUpperBound; ++a) { 0517 // Q_ASSERT(!ItemHandler::isFree(m_items[a])); 0518 // } 0519 0520 Q_ASSERT(currentItem < currentLowerBound || currentItem >= currentUpperBound); 0521 0522 //If the current item is on a border of the bounds, it needs to be inserted in the right position. 0523 //Else, the current position already is right, and we only need to copy it in. 0524 if (currentLowerBound < currentUpperBound && 0525 (currentItem == currentLowerBound - 1 || currentItem == currentUpperBound)) { 0526 if (!insertSorted(m_add, currentItem, currentLowerBound, currentUpperBound, force)) { 0527 return false; 0528 } 0529 } else { 0530 ItemHandler::copyTo(m_add, m_items[currentItem]); 0531 } 0532 0533 m_applied = true; 0534 0535 //First, take currentItem out of the chain, by replacing it with current.rightChild in the parent 0536 if (previousItem != -1) { 0537 Data& previous(m_items[previousItem]); 0538 if (ItemHandler::leftChild(previous) == currentItem) { 0539 Q_ASSERT(replaceCurrentWith == -1 || replaceCurrentWith < previousItem); 0540 ItemHandler::setLeftChild(previous, replaceCurrentWith); 0541 } else if (ItemHandler::rightChild(previous) == currentItem) { 0542 Q_ASSERT(replaceCurrentWith == -1 || previousItem < replaceCurrentWith); 0543 ItemHandler::setRightChild(previous, replaceCurrentWith); 0544 } else { 0545 Q_ASSERT(0); 0546 } 0547 } else { 0548 *m_centralFreeItem = replaceCurrentWith; 0549 } 0550 0551 return true; 0552 0553 DEBUG_FREEITEM_COUNT 0554 } 0555 0556 private: 0557 void verifyTreeConsistent(int position, int lowerBound, int upperBound) 0558 { 0559 if (position == -1) 0560 return; 0561 Q_ASSERT(lowerBound <= position && position < upperBound); 0562 verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); 0563 verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position + 1, upperBound); 0564 } 0565 0566 void debugFreeItemCount() 0567 { 0568 uint count = 0; 0569 for (uint a = 0; a < m_itemCount; ++a) { 0570 if (isFree(m_items[a])) 0571 ++count; 0572 } 0573 0574 uint counted = countFreeItems(*m_centralFreeItem); 0575 Q_ASSERT(count == counted); 0576 Q_UNUSED(counted); 0577 } 0578 0579 QPair<int, int> leftAndRightRealItems(int pos) 0580 { 0581 Q_ASSERT(ItemHandler::isFree(m_items[pos])); 0582 int left = -1, right = -1; 0583 for (int a = pos - 1; a >= 0; --a) { 0584 if (!ItemHandler::isFree(m_items[a])) { 0585 left = a; 0586 break; 0587 } 0588 } 0589 0590 for (uint a = pos + 1; a < m_itemCount; ++a) { 0591 if (!ItemHandler::isFree(m_items[a])) { 0592 right = a; 0593 break; 0594 } 0595 } 0596 0597 return qMakePair(left, right); 0598 } 0599 0600 int buildFreeTree(int count, uint raster, int start) 0601 { 0602 Q_ASSERT((start % raster) == (raster - 1)); 0603 Q_ASSERT(count != 0); 0604 Q_ASSERT(count <= ( int )m_itemCount); 0605 if (count == 1) { 0606 ItemHandler::createFreeItem(m_items[start]); 0607 return start; 0608 } else { 0609 int central = start + (count / 2) * raster; 0610 int leftCount = count / 2; 0611 int midCount = 1; 0612 int rightCount = count - leftCount - midCount; 0613 Q_ASSERT(leftCount + midCount <= count); 0614 ItemHandler::createFreeItem(m_items[central]); 0615 Q_ASSERT(ItemHandler::isFree(m_items[central])); 0616 0617 int leftFreeTree = buildFreeTree(leftCount, raster, start); 0618 Q_ASSERT(leftFreeTree == -1 || leftFreeTree < central); 0619 ItemHandler::setLeftChild(m_items[central], leftFreeTree); 0620 0621 if (rightCount > 0) { 0622 int rightFreeTree = buildFreeTree(rightCount, raster, central + raster); 0623 Q_ASSERT(rightFreeTree == -1 || central < rightFreeTree); 0624 ItemHandler::setRightChild(m_items[central], rightFreeTree); 0625 } 0626 0627 return central; 0628 } 0629 } 0630 0631 uint countFreeItems(int item) const 0632 { 0633 if (item == -1) 0634 return 0; 0635 const Data& current(m_items[item]); 0636 0637 return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); 0638 } 0639 0640 int leftMostChild(int pos) const 0641 { 0642 while (1) { 0643 if (ItemHandler::leftChild(m_items[pos]) != -1) 0644 pos = ItemHandler::leftChild(m_items[pos]); 0645 else 0646 return pos; 0647 } 0648 } 0649 0650 int takeLeftMostChild(int pos) const 0651 { 0652 int parent = -1; 0653 while (1) { 0654 if (ItemHandler::leftChild(m_items[pos]) != -1) { 0655 parent = pos; 0656 pos = ItemHandler::leftChild(m_items[pos]); 0657 } else { 0658 ItemHandler::setLeftChild(m_items[parent], -1); 0659 return pos; 0660 } 0661 } 0662 } 0663 0664 int rightMostChild(int pos) const 0665 { 0666 while (1) { 0667 if (ItemHandler::rightChild(m_items[pos]) != -1) 0668 pos = ItemHandler::rightChild(m_items[pos]); 0669 else 0670 return pos; 0671 } 0672 } 0673 0674 int takeRightMostChild(int pos) const 0675 { 0676 int parent = -1; 0677 while (1) { 0678 if (ItemHandler::rightChild(m_items[pos]) != -1) { 0679 parent = pos; 0680 pos = ItemHandler::rightChild(m_items[pos]); 0681 } else { 0682 ItemHandler::setRightChild(m_items[parent], -1); 0683 return pos; 0684 } 0685 } 0686 } 0687 0688 ///Maximum "moving" out of the way of items without forcing a complete rebuild of the list 0689 inline int maxMoveAround() const 0690 { 0691 return increaseFraction * rebuildIfInsertionMoreExpensive; 0692 } 0693 0694 ///Inserts the given data item into position @p pos, and updates the sorting 0695 ///@param data can be another empty item, that together with @p pos represents the closest enclosure of the target position 0696 ///@return Whether the item could be inserted. It is not inserted if 0697 bool insertSorted(const Data& data, int pos, int start, int end, bool force) 0698 { 0699 0700 if (pos < start) 0701 start = pos; 0702 if (pos >= end) 0703 end = pos + 1; 0704 0705 /* for(int a = start; a < end; ++a) { 0706 if(a != pos) { 0707 Q_ASSERT(!ItemHandler::isFree(m_items[a])); 0708 } 0709 }*/ 0710 EmbeddedTreeAlgorithms<Data, ItemHandler> alg(m_items, m_itemCount, *m_centralFreeItem); 0711 int bound = alg.lowerBound(data, start, end); 0712 //Now find the position that should be taken 0713 if (bound == -1) 0714 bound = end; 0715 0716 //Now our item should end up right before bound 0717 0718 int target; 0719 //bound cannot be pos, because pos is invalid 0720 Q_ASSERT(bound != pos); 0721 0722 #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800) 0723 #pragma GCC diagnostic push 0724 #pragma GCC diagnostic ignored "-Wclass-memaccess" 0725 #endif 0726 //Shuffle around the item at the free pos, so reference counting in constructors/destructors is not screwed up 0727 char backup[sizeof(Data)]; 0728 memcpy(backup, m_items + pos, sizeof(Data)); 0729 0730 if (bound < pos) { 0731 if (!force && pos - bound > maxMoveAround()) { 0732 // qCDebug(UTIL) << "increasing because" << pos-bound << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem) << "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; 0733 return false; 0734 } 0735 //Move [bound, pos) one to right, and insert at bound 0736 memmove(m_items + bound + 1, m_items + bound, sizeof(Data) * (pos - bound)); 0737 target = bound; 0738 } else { 0739 Q_ASSERT(bound > pos); 0740 if (!force && bound - pos - 1 > maxMoveAround()) { 0741 // qCDebug(UTIL) << "increasing because" << bound-pos-1 << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem)<< "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; 0742 return false; 0743 } 0744 //Move (pos, bound) one to left, and insert at bound-1 0745 memmove(m_items + pos, m_items + pos + 1, sizeof(Data) * (bound - pos - 1)); 0746 target = bound - 1; 0747 } 0748 memcpy(m_items + target, backup, sizeof(Data)); 0749 #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800) 0750 #pragma GCC diagnostic pop 0751 #endif 0752 0753 ItemHandler::copyTo(data, m_items[target]); 0754 return true; 0755 } 0756 0757 Q_DISABLE_COPY(EmbeddedTreeAddItem) 0758 0759 private: 0760 const Data& m_add; 0761 Data* m_items; 0762 uint m_itemCount; 0763 int* m_centralFreeItem; 0764 bool m_applied, m_needResize; 0765 }; 0766 0767 /**Use this to add items. 0768 * The removed item must be in the set! 0769 * General usage: 0770 * - Construct the object 0771 * - Check if newItemCount() equals the previous item-count. If not, construct 0772 * a new list as large as newItemCount, and call object.transferData to transfer the data 0773 * into the new list. The new size must match the returned newItemCount. 0774 * However this may also be ignored if the memory-saving is not wanted in that moment. 0775 * */ 0776 template<class Data, class ItemHandler, int increaseFraction = 5> 0777 class EmbeddedTreeRemoveItem 0778 { 0779 0780 public: 0781 0782 EmbeddedTreeRemoveItem(Data* items, uint itemCount, int& centralFreeItem, const Data& remove) : m_remove(remove) 0783 , m_items(items) 0784 , m_itemCount(itemCount) 0785 , m_centralFreeItem(¢ralFreeItem) 0786 , m_insertedAtDepth(0) 0787 { 0788 apply(); 0789 } 0790 0791 ~EmbeddedTreeRemoveItem() 0792 { 0793 } 0794 0795 ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then 0796 ///the data needs to be transferred into a new list using transferData 0797 uint newItemCount() const 0798 { 0799 uint maxFreeItems = ((m_itemCount / increaseFraction) * 3) / 2 + 1; 0800 //First we approximate the count of free items using the insertion depth 0801 if (m_insertedAtDepth >= 32 || (1u << m_insertedAtDepth) >= maxFreeItems) { 0802 uint freeCount = countFreeItems(*m_centralFreeItem); 0803 if (freeCount > maxFreeItems || freeCount == m_itemCount) { 0804 return m_itemCount - freeCount; 0805 } 0806 } 0807 0808 return m_itemCount; 0809 } 0810 0811 ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() 0812 void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) 0813 { 0814 DEBUG_FREEITEM_COUNT 0815 //We only transfer into a new list when all the free items are used up 0816 0817 //Create a list where only the non-free items exist 0818 uint offset = 0; 0819 for (uint a = 0; a < m_itemCount; ++a) { 0820 if (!ItemHandler::isFree(m_items[a])) { 0821 newItems[offset] = m_items[a]; 0822 ++offset; 0823 } 0824 } 0825 0826 Q_ASSERT(offset == newCount); 0827 0828 if (newCentralFree) 0829 m_centralFreeItem = newCentralFree; 0830 *m_centralFreeItem = -1; 0831 m_items = newItems; 0832 m_itemCount = newCount; 0833 DEBUG_FREEITEM_COUNT 0834 } 0835 0836 private: 0837 void verifyTreeConsistent(int position, int lowerBound, int upperBound) 0838 { 0839 if (position == -1) 0840 return; 0841 Q_ASSERT(lowerBound <= position && position < upperBound); 0842 verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); 0843 verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position + 1, upperBound); 0844 } 0845 0846 uint countFreeItems(int item) const 0847 { 0848 if (item == -1) 0849 return 0; 0850 const Data& current(m_items[item]); 0851 0852 return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); 0853 } 0854 0855 int findItem(const Data& data, uint start, uint end) 0856 { 0857 EmbeddedTreeAlgorithms<Data, ItemHandler> alg(m_items, m_itemCount, *m_centralFreeItem); 0858 return alg.indexOf(data, start, end); 0859 } 0860 0861 void apply() 0862 { 0863 DEBUG_FREEITEM_COUNT 0864 0865 int removeIndex = findItem(m_remove, 0, m_itemCount); 0866 Q_ASSERT(removeIndex != -1); 0867 Q_ASSERT(!ItemHandler::isFree(m_items[removeIndex])); 0868 0869 //Find the free item that is nearest to the target position in the item order 0870 int currentItem = *m_centralFreeItem; 0871 0872 int lowerBound = 0; //The minimum position the searched item can have 0873 int upperBound = m_itemCount; //The lowest known position the searched item can _not_ have 0874 0875 if (*m_centralFreeItem == -1) { 0876 *m_centralFreeItem = removeIndex; 0877 Q_ASSERT(*m_centralFreeItem != -1); 0878 ItemHandler::createFreeItem(m_items[*m_centralFreeItem]); 0879 return; 0880 } 0881 0882 //Now go down the chain, always into the items direction 0883 ///@todo make the structure better: Don't just put left/right child, but also swap when needed 0884 /// to balance the tree 0885 while (1) { 0886 Q_ASSERT(removeIndex != currentItem); 0887 Data& current(m_items[currentItem]); 0888 ++m_insertedAtDepth; 0889 if (removeIndex < currentItem) { 0890 upperBound = currentItem; 0891 //Follow left child 0892 if (ItemHandler::leftChild(current) != -1) { 0893 //Continue traversing 0894 currentItem = ItemHandler::leftChild(current); 0895 Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); 0896 } else { 0897 //The to-be deleted item is before current, and can be added as left child to current 0898 int item = findItem(m_remove, lowerBound, upperBound); 0899 Q_ASSERT(item == removeIndex); 0900 ItemHandler::createFreeItem(m_items[item]); 0901 Q_ASSERT(item == -1 || item < currentItem); 0902 ItemHandler::setLeftChild(current, item); 0903 Q_ASSERT(item >= lowerBound && item < upperBound); 0904 break; 0905 } 0906 } else { 0907 lowerBound = currentItem + 1; 0908 //Follow right child 0909 if (ItemHandler::rightChild(current) != -1) { 0910 //Continue traversing 0911 currentItem = ItemHandler::rightChild(current); 0912 Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); 0913 } else { 0914 //The to-be deleted item is behind current, and can be added as right child to current 0915 int item = findItem(m_remove, lowerBound, upperBound); 0916 Q_ASSERT(item == removeIndex); 0917 ItemHandler::createFreeItem(m_items[item]); 0918 Q_ASSERT(item == -1 || currentItem < item); 0919 ItemHandler::setRightChild(current, item); 0920 Q_ASSERT(item >= lowerBound && item < upperBound); 0921 break; 0922 } 0923 } 0924 } 0925 0926 DEBUG_FREEITEM_COUNT 0927 } 0928 0929 void debugFreeItemCount() 0930 { 0931 uint count = 0; 0932 for (uint a = 0; a < m_itemCount; ++a) { 0933 if (ItemHandler::isFree(m_items[a])) 0934 ++count; 0935 } 0936 0937 uint counted = countFreeItems(*m_centralFreeItem); 0938 Q_ASSERT(count == counted); 0939 Q_UNUSED(counted); 0940 } 0941 0942 const Data& m_remove; 0943 Data* m_items; 0944 uint m_itemCount; 0945 int* m_centralFreeItem; 0946 int m_insertedAtDepth; 0947 }; 0948 } 0949 0950 #endif