/* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EMBEDDED_FREE_TREE #define EMBEDDED_FREE_TREE #include #include #include #include #include "kdevvarlengtharray.h" #include //Uncomment this to search for tree-inconsistencies, however it's very expensive // #define DEBUG_FREEITEM_COUNT debugFreeItemCount(); verifyTreeConsistent(*m_centralFreeItem, 0, m_itemCount); #define DEBUG_FREEITEM_COUNT /** * This file implements algorithms that allow managing a sorted list of items, and managing "free" items * for reuse efficiently in that list. Among those free items a tree is built, and they are traversed * on insertion/removal to manage free items in the tree. * * There is specific needs on the embedded items: * - They must be markable "invalid", so after they are deleted they can stay in their place as invalid items. * - While they are invalid, they still must be able to hold 2 integers, needed for managing the tree of free items. * - One integer is needed for each list to hold a pointer to the central free item. * * Only these functions must be used to manipulate the lists, from the beginning up. First create an empty list * and initialize centralFreeItem with -1, and then you start adding items. * * Since the list is sorted, and each item can be contained only once, these lists actually represent a set. * * EmbeddedTreeAlgorithms implements an efficient "contains" function that uses binary search within the list. */ namespace KDevelop { ///Responsible for handling the items in the list ///This is an example. ItemHandler::rightChild(..) and ItemHandler::leftChild(..) must be values that must be able to hold the count of positive ///values that will be the maximum size of the list, and additionally -1. // template // class ExampleItemHandler { // public: // ExampleItemHandler(const Data& data) : m_data(data) { // } // int ItemHandler::rightChild() const { // Q_ASSERT(0); // } // int ItemHandler::leftChild() const { // Q_ASSERT(0); // } // void ItemHandler::setLeftChild(int child) { // Q_ASSERT(0); // } // void setRightChild(int child) { // Q_ASSERT(0); // } // bool operator<(const StandardItemHandler& rhs) const { // Q_ASSERT(0); // } // //Copies this item into the given one // void copyTo(Data& data) const { // data = m_data; // } // // static void createFreeItem(Data& data) { // data = Data(); // } // // bool isFree() const { // Q_ASSERT(0); // } // // const Data& data() { // } // // private: // const Data& m_data; // }; /** * Use this for several constant algorithms on sorted lists with free-trees * */ template class EmbeddedTreeAlgorithms { public: EmbeddedTreeAlgorithms(const Data* items, uint itemCount, const int& centralFreeItem) : m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem) { } ~EmbeddedTreeAlgorithms() { } ///Efficiently checks whether the item is contained in the set. ///If it is contained, returns the index. Else, returns -1. int indexOf(const Data& data) { return indexOf(data, 0, m_itemCount); } ///Searches the given item within the specified bounds. int indexOf(const Data& data, uint start, uint end) { while(1) { if(start >= end) return -1; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < (int)end; ) { if(!ItemHandler::isFree(m_items[center])) break; ++center; } if(center == (int)end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ if(ItemHandler::equals(data, m_items[center])) { return center; }else if(data < m_items[center]) { end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } ///Returns the first valid index that has a data-value larger or equal to @param data. ///Returns -1 if nothing is found. int lowerBound(const Data& data, int start, int end) { int currentBound = -1; while(1) { if(start >= end) return currentBound; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < end; ) { if(!ItemHandler::isFree(m_items[center])) break; ++center; } if(center == end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ if(ItemHandler::equals(data, m_items[center])) { return center; }else if(data < m_items[center]) { currentBound = center; end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } uint countFreeItems() const { return countFreeItemsInternal(*m_centralFreeItem); } uint countFreeItemsNaive() const { uint ret = 0; for(uint a = 0; a < m_itemCount; ++a) { if(ItemHandler::isFree(m_items[a])) ++ret; } return ret; } void verifyOrder() { Data last; for(uint a = 0; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { if(!ItemHandler::isFree(last)) Q_ASSERT(last < m_items[a]); last = m_items[a]; } } } void verifyTreeConsistent() { verifyTreeConsistentInternal(*m_centralFreeItem, 0, m_itemCount); Q_ASSERT(countFreeItems() == countFreeItemsNaive()); } private: void verifyTreeConsistentInternal(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistentInternal(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistentInternal(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } uint countFreeItemsInternal(int item) const { if(item == -1) return 0; return 1 + countFreeItemsInternal(ItemHandler::leftChild(m_items[item])) + countFreeItemsInternal(ItemHandler::rightChild(m_items[item])); } const Data* m_items; uint m_itemCount; const int* m_centralFreeItem; }; /**Use this to add items. * The added item must not be in the set yet! * General usage: * - Construct the object * - Check if newItemCount() equals the previous item-count. If not, construct * a new list as large as newItemCount, and call object.transferData to transfer the data * into the new list. The new size must match the returned newItemCount. * - Either call object.apply(), or let it be called automatically by the destructor. * @param 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. * @param rebuildIfInsertionMoreExpensive The structure is rebuilt completely when an insertion needs a moving around of more than rebuildIfInsertionMoreExpensive times the count of items needed to be moved in worst case in a fresh tree. * After rebuilding the tree, the free space is evenly distributed, and thus insertions require much less moving. * */ template class EmbeddedTreeAddItem { public: EmbeddedTreeAddItem(Data* items, uint itemCount, int& centralFreeItem, const Data& add) : m_add(add), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_applied(false), m_needResize(false) { m_needResize = !apply(); } ~EmbeddedTreeAddItem() { if(!m_applied) apply(true); } ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then ///the data needs to be transferred into a new list using transferData uint newItemCount() const { if(!m_applied) { if(*m_centralFreeItem == -1) { uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); uint newItemCount = realItemCount + (realItemCount/increaseFraction); if(newItemCount <= m_itemCount) newItemCount = m_itemCount+1; return newItemCount; }else if(m_needResize) { uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); uint newItemCount = realItemCount + (realItemCount/increaseFraction); return newItemCount; } } return m_itemCount; } ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() void transferData(Data* newItems, uint newCount, int* newCentralFree = 0) { DEBUG_FREEITEM_COUNT uint currentRealCount = m_itemCount - countFreeItems(*m_centralFreeItem); // Q_ASSERT(currentRealCount + (currentRealCount/increaseFraction) == newCount); //Create a new list where the items from m_items are put into newItems, with the free items evenly //distributed, and a clean balanced free-tree. uint newFreeCount = newCount - currentRealCount; volatile uint freeItemRaster; if(newFreeCount) freeItemRaster = newCount / newFreeCount; else { freeItemRaster = newCount+1; //No free items } ///@todo Do not iterate through all items, instead use the free-tree and memcpy for the ranges between free items. ///Ideally, even the free-tree would be built on-the-fly. Q_ASSERT(freeItemRaster); uint offset = 0; uint insertedValidCount = 0; for(uint a = 0; a < newCount; ++a) { //Create new free items at the end of their raster range if(a % freeItemRaster == (freeItemRaster-1)) { //We need to insert a free item ItemHandler::createFreeItem(newItems[a]); ++offset; }else{ ++insertedValidCount; while(ItemHandler::isFree(m_items[a-offset]) && a-offset < m_itemCount) --offset; Q_ASSERT(a-offset < m_itemCount); newItems[a] = m_items[a-offset]; // Q_ASSERT(!ItemHandler::isFree(newItems[a])); } } Q_ASSERT(insertedValidCount == m_itemCount - countFreeItems(*m_centralFreeItem)); // kDebug() << m_itemCount << newCount << offset; // Q_ASSERT(m_itemCount == newCount-offset); m_items = newItems; m_itemCount = newCount; if(newCentralFree) m_centralFreeItem = newCentralFree; *m_centralFreeItem = buildFreeTree(newFreeCount, freeItemRaster, freeItemRaster-1); // kDebug() << "count of new free items:" << newFreeCount; // Q_ASSERT(countFreeItems( *m_centralFreeItem ) == newFreeCount); DEBUG_FREEITEM_COUNT } ///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 bool apply(bool force = false) { if(m_applied) return true; if(*m_centralFreeItem == -1) { Q_ASSERT(!force); return false; } //Find the free item that is nearest to the target position in the item order int previousItem = -1; int currentItem = *m_centralFreeItem; int replaceCurrentWith = -1; //In currentLowerBound and currentUpperBound, we count the smallest contiguous range between free //items that the added items needs to be sorted into. If the range is empty, the item can just be inserted. int currentLowerBound = 0; int currentUpperBound = m_itemCount; //Now go down the chain, always into the items direction while(1) { QPair freeBounds = leftAndRightRealItems(currentItem); const Data& current(m_items[currentItem]); if(freeBounds.first != -1 && m_add < m_items[freeBounds.first]) { //Follow left child currentUpperBound = freeBounds.first+1; if(ItemHandler::leftChild(current) != -1) { //Continue traversing previousItem = currentItem; currentItem = ItemHandler::leftChild(current); }else{ replaceCurrentWith = ItemHandler::rightChild(current); break; } }else if(freeBounds.second != -1 && m_items[freeBounds.second] < m_add) { //Follow right child currentLowerBound = freeBounds.second; if(ItemHandler::rightChild(current) != -1) { //Continue traversing previousItem = currentItem; currentItem = ItemHandler::rightChild(current); }else{ replaceCurrentWith = ItemHandler::leftChild(current); break; } }else{ //We will use this item! So find a replacement for it in the tree, and update the structure force = true; currentLowerBound = currentUpperBound = currentItem; int leftReplaceCandidate = -1, rightReplaceCandidate = -1; if(ItemHandler::leftChild(current) != -1) leftReplaceCandidate = rightMostChild(ItemHandler::leftChild(current)); if(ItemHandler::rightChild(current) != -1) rightReplaceCandidate = leftMostChild(ItemHandler::rightChild(current)); ///@todo it's probably better using lowerBound and upperBound like in the "remove" version //Left and right bounds of all children of current int leftChildBound = leftMostChild(currentItem), rightChildBound = rightMostChild(currentItem); Q_ASSERT(leftChildBound != -1 && rightChildBound != -1); int childCenter = (leftChildBound + rightChildBound)/2; if(leftReplaceCandidate == -1 && rightReplaceCandidate == -1) { //We don't have a replace candidate, since there is no children Q_ASSERT(ItemHandler::leftChild(current) == -1); Q_ASSERT(ItemHandler::rightChild(current) == -1); }else if(rightReplaceCandidate == -1 || abs(leftReplaceCandidate - childCenter) < abs(rightReplaceCandidate - childCenter)) { //pick the left replacement, since it's more central Q_ASSERT(leftReplaceCandidate != -1); replaceCurrentWith = leftReplaceCandidate; Data& replaceWith(m_items[replaceCurrentWith]); if(replaceCurrentWith == ItemHandler::leftChild(current)) { //The left child of replaceWith can just stay as it is, and we just need to add the right child Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); }else{ takeRightMostChild(ItemHandler::leftChild(current)); //Since we'll be clearing the item, we have to put this childsomewhere else. // Either make it our new "left" child, or make it the new left children "rightmost" child. int addRightMostLeftChild = ItemHandler::leftChild(replaceWith); ItemHandler::setLeftChild(replaceWith, -1); Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); if(ItemHandler::leftChild(current) != -1) { Q_ASSERT(rightMostChild(ItemHandler::leftChild(current)) != replaceCurrentWith); Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); if(addRightMostLeftChild != -1) { int rightMostLeft = rightMostChild(ItemHandler::leftChild(replaceWith)); Q_ASSERT(rightMostLeft != -1); // Q_ASSERT(item(rightMostLeft).ItemHandler::rightChild() == -1); Q_ASSERT(rightMostLeft < addRightMostLeftChild); ItemHandler::setRightChild(m_items[rightMostLeft], addRightMostLeftChild); } }else{ Q_ASSERT(addRightMostLeftChild == -1 || addRightMostLeftChild < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, addRightMostLeftChild); } } Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); }else{ //pick the right replacement, since it's more central Q_ASSERT(rightReplaceCandidate != -1); replaceCurrentWith = rightReplaceCandidate; Data& replaceWith(m_items[replaceCurrentWith]); if(replaceCurrentWith == ItemHandler::rightChild(current)) { //The right child of replaceWith can just stay as it is, and we just need to add the left child Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); }else{ takeLeftMostChild(ItemHandler::rightChild(current)); //Since we'll be clearing the item, we have to put this childsomewhere else. // Either make it our new "right" child, or make it the new right children "leftmost" child. int addLeftMostRightChild = ItemHandler::rightChild(replaceWith); ItemHandler::setRightChild(replaceWith, -1); Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); if(ItemHandler::rightChild(current) != -1) { Q_ASSERT(leftMostChild(ItemHandler::rightChild(current)) != replaceCurrentWith); Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); if(addLeftMostRightChild != -1) { int leftMostRight = leftMostChild(ItemHandler::rightChild(replaceWith)); Q_ASSERT(leftMostRight != -1); Q_ASSERT(ItemHandler::leftChild(m_items[leftMostRight]) == -1); Q_ASSERT(addLeftMostRightChild < leftMostRight); ItemHandler::setLeftChild(m_items[leftMostRight], addLeftMostRightChild); } }else{ Q_ASSERT(addLeftMostRightChild == -1 || replaceCurrentWith < addLeftMostRightChild); ItemHandler::setRightChild(replaceWith, addLeftMostRightChild); } } Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); } break; } } //We can insert now //currentItem and previousItem are the two items that best enclose the target item // for(int a = currentLowerBound; a < currentUpperBound; ++a) { // Q_ASSERT(!ItemHandler::isFree(m_items[a])); // } Q_ASSERT(currentItem < currentLowerBound || currentItem >= currentUpperBound); //If the current item is on a border of the bounds, it needs to be inserted in the right position. //Else, the current position already is right, and we only need to copy it in. if(currentLowerBound < currentUpperBound && (currentItem == currentLowerBound-1 || currentItem == currentUpperBound)) { if(!insertSorted(m_add, currentItem, currentLowerBound, currentUpperBound, force)) { return false; } }else{ ItemHandler::copyTo(m_add, m_items[currentItem]); } m_applied = true; //First, take currentItem out of the chain, by replacing it with current.rightChild in the parent if(previousItem != -1) { Data& previous(m_items[previousItem]); if(ItemHandler::leftChild(previous) == currentItem) { Q_ASSERT(replaceCurrentWith == -1 || replaceCurrentWith < previousItem); ItemHandler::setLeftChild(previous, replaceCurrentWith); } else if(ItemHandler::rightChild(previous) == currentItem) { Q_ASSERT(replaceCurrentWith == -1 || previousItem < replaceCurrentWith); ItemHandler::setRightChild(previous, replaceCurrentWith); } else { Q_ASSERT(0); } } else { *m_centralFreeItem = replaceCurrentWith; } return true; DEBUG_FREEITEM_COUNT } private: void verifyTreeConsistent(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } void debugFreeItemCount() { uint count = 0; for(uint a = 0; a < m_itemCount; ++a) { if(isFree(m_items[a])) ++count; } uint counted = countFreeItems(*m_centralFreeItem); Q_ASSERT(count == counted); Q_UNUSED(counted); } QPair leftAndRightRealItems(int pos) { Q_ASSERT(ItemHandler::isFree(m_items[pos])); int left = -1, right = -1; for(int a = pos-1; a >= 0; --a) { if(!ItemHandler::isFree(m_items[a])) { left = a; break; } } for(uint a = pos+1; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { right = a; break; } } return qMakePair(left, right); } int buildFreeTree(int count, uint raster, int start) { Q_ASSERT((start % raster) == (raster-1)); Q_ASSERT(count != 0); Q_ASSERT(count <= (int)m_itemCount); if(count == 1) { ItemHandler::createFreeItem(m_items[start]); return start; }else{ int central = start + (count / 2) * raster; int leftCount = count / 2; int midCount = 1; int rightCount = count - leftCount - midCount; Q_ASSERT(leftCount + midCount <= count); ItemHandler::createFreeItem(m_items[central]); Q_ASSERT(ItemHandler::isFree(m_items[central])); int leftFreeTree = buildFreeTree(leftCount, raster, start); Q_ASSERT(leftFreeTree == -1 || leftFreeTree < central); ItemHandler::setLeftChild(m_items[central], leftFreeTree ); if(rightCount > 0) { int rightFreeTree = buildFreeTree(rightCount, raster, central+raster); Q_ASSERT(rightFreeTree == -1 || central < rightFreeTree); ItemHandler::setRightChild(m_items[central], rightFreeTree ); } return central; } } uint countFreeItems(int item) const { if(item == -1) return 0; const Data& current(m_items[item]); return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); } int leftMostChild(int pos) const { while(1) { if(ItemHandler::leftChild(m_items[pos]) != -1) pos = ItemHandler::leftChild(m_items[pos]); else return pos; } } int takeLeftMostChild(int pos) const { int parent = -1; while(1) { if(ItemHandler::leftChild(m_items[pos]) != -1) { parent = pos; pos = ItemHandler::leftChild(m_items[pos]); } else { ItemHandler::setLeftChild(m_items[parent], -1); return pos; } } } int rightMostChild(int pos) const { while(1) { if(ItemHandler::rightChild(m_items[pos]) != -1) pos = ItemHandler::rightChild(m_items[pos]); else return pos; } } int takeRightMostChild(int pos) const { int parent = -1; while(1) { if(ItemHandler::rightChild(m_items[pos]) != -1) { parent = pos; pos = ItemHandler::rightChild(m_items[pos]); } else { ItemHandler::setRightChild(m_items[parent], -1); return pos; } } } void insertBubbleSorted(const Data& data, int pos) { //Since we don't know how the target is enclosed, just do naive bubble sort ItemHandler::copyTo(data, m_items[pos]); while(1) { int prev = pos-1; int next = pos+1; if(prev >= 0 && !ItemHandler::isFree(m_items[prev]) && m_items[pos] < m_items[prev]) { Data backup(m_items[prev]); m_items[prev] = m_items[pos]; m_items[pos] = backup; pos = prev; }else if(next < m_itemCount && !ItemHandler::isFree(m_items[next]) && m_items[next] < m_items[pos]) { Data backup(m_items[next]); m_items[next] = m_items[pos]; m_items[pos] = backup; pos = next; }else{ break; } } } ///Maximum "moving" out of the way of items without forcing a complete rebuild of the list inline int maxMoveAround() const { return increaseFraction * rebuildIfInsertionMoreExpensive; } ///Inserts the given data item into position pos, and updates the sorting ///@param otherBound can be another empty item, that together with @param pos represents the closest enclosure of the target position ///@return Whether the item could be inserted. It is not inserted if bool insertSorted(const Data& data, int pos, int start, int end, bool force) { if(pos < start) start = pos; if(pos >= end) end = pos+1; /* for(int a = start; a < end; ++a) { if(a != pos) { Q_ASSERT(!ItemHandler::isFree(m_items[a])); } }*/ EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); int bound = alg.lowerBound(data, start, end); //Now find the position that should be taken if(bound == -1) bound = end; //Now our item should end up right before bound int target; //bound cannot be pos, because pos is invalid Q_ASSERT(bound != pos); //Shuffle around the item at the free pos, so reference counting in constructors/destructors is not screwed up char backup[sizeof(Data)]; memcpy(backup, m_items+pos, sizeof(Data)); if(bound < pos) { if(!force && pos-bound > maxMoveAround()) { // kDebug() << "increasing because" << pos-bound << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem) << "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; return false; } //Move [bound, pos) one to right, and insert at bound memmove(m_items+bound+1, m_items+bound, sizeof(Data)*(pos-bound)); target = bound; }else { Q_ASSERT(bound > pos); if(!force && bound-pos-1 > maxMoveAround()) { // kDebug() << "increasing because" << bound-pos-1 << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem)<< "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; return false; } //Move (pos, bound) one to left, and insert at bound-1 memmove(m_items+pos, m_items+pos+1, sizeof(Data)*(bound-pos-1)); target = bound-1; } memcpy(m_items+target, backup, sizeof(Data)); ItemHandler::copyTo(data, m_items[target]); return true; } const Data& m_add; Data* m_items; uint m_itemCount; int* m_centralFreeItem; bool m_applied, m_needResize; }; /**Use this to add items. * The removed item must be in the set! * General usage: * - Construct the object * - Check if newItemCount() equals the previous item-count. If not, construct * a new list as large as newItemCount, and call object.transferData to transfer the data * into the new list. The new size must match the returned newItemCount. * However this may also be ignored if the memory-saving is not wanted in that moment. * */ template class EmbeddedTreeRemoveItem { public: EmbeddedTreeRemoveItem(Data* items, uint itemCount, int& centralFreeItem, const Data& remove) : m_remove(remove), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_insertedAtDepth(0) { apply(); } ~EmbeddedTreeRemoveItem() { } ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then ///the data needs to be transferred into a new list using transferData uint newItemCount() const { uint maxFreeItems = ((m_itemCount / increaseFraction)*3)/2 + 1; //First we approximate the count of free items using the insertion depth if((1u << m_insertedAtDepth) >= maxFreeItems) { uint freeCount = countFreeItems(*m_centralFreeItem); if(freeCount > maxFreeItems || freeCount == m_itemCount) { return m_itemCount - freeCount; } } return m_itemCount; } ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() void transferData(Data* newItems, uint newCount, int* newCentralFree = 0) { DEBUG_FREEITEM_COUNT //We only transfer into a new list when all the free items are used up //Create a list where only the non-free items exist uint offset = 0; for(uint a = 0; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { newItems[offset] = m_items[a]; ++offset; } } Q_ASSERT(offset == newCount); if(newCentralFree) m_centralFreeItem = newCentralFree; *m_centralFreeItem = -1; m_items = newItems; m_itemCount = newCount; DEBUG_FREEITEM_COUNT } private: void verifyTreeConsistent(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } uint countFreeItems(int item) const { if(item == -1) return 0; const Data& current(m_items[item]); return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); } int findItem(const Data& data, uint start, uint end) { EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); return alg.indexOf(data, start, end); } void apply() { DEBUG_FREEITEM_COUNT int removeIndex = findItem(m_remove, 0, m_itemCount); Q_ASSERT(removeIndex != -1); Q_ASSERT(!ItemHandler::isFree(m_items[removeIndex])); //Find the free item that is nearest to the target position in the item order int currentItem = *m_centralFreeItem; int lowerBound = 0; //The minimum position the searched item can have int upperBound = m_itemCount; //The lowest known position the searched item can _not_ have if(*m_centralFreeItem == -1) { *m_centralFreeItem = removeIndex; Q_ASSERT(*m_centralFreeItem != -1); ItemHandler::createFreeItem(m_items[*m_centralFreeItem]); return; } //Now go down the chain, always into the items direction ///@todo make the structure better: Don't just put left/right child, but also swap when neede /// to balance the tree while(1) { Q_ASSERT(removeIndex != currentItem); Data& current(m_items[currentItem]); ++m_insertedAtDepth; if(removeIndex < currentItem) { upperBound = currentItem; //Follow left child if(ItemHandler::leftChild(current) != -1) { //Continue traversing currentItem = ItemHandler::leftChild(current); Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); }else{ //The to-be deleted item is before current, and can be added as left child to current int item = findItem(m_remove, lowerBound, upperBound); Q_ASSERT(item == removeIndex); ItemHandler::createFreeItem(m_items[item]); Q_ASSERT(item == -1 || item < currentItem); ItemHandler::setLeftChild(current, item); Q_ASSERT(item >= lowerBound && item < upperBound); break; } }else{ lowerBound = currentItem+1; //Follow right child if(ItemHandler::rightChild(current) != -1) { //Continue traversing currentItem = ItemHandler::rightChild(current); Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); }else{ //The to-be deleted item is behind current, and can be added as right child to current int item = findItem(m_remove, lowerBound, upperBound); Q_ASSERT(item == removeIndex); ItemHandler::createFreeItem(m_items[item]); Q_ASSERT(item == -1 || currentItem < item); ItemHandler::setRightChild(current, item); Q_ASSERT(item >= lowerBound && item < upperBound); break; } } } DEBUG_FREEITEM_COUNT } void debugFreeItemCount() { uint count = 0; for(uint a = 0; a < m_itemCount; ++a) { if(ItemHandler::isFree(m_items[a])) ++count; } uint counted = countFreeItems(*m_centralFreeItem); Q_ASSERT(count == counted); Q_UNUSED(counted); } const Data& m_remove; Data* m_items; uint m_itemCount; int* m_centralFreeItem; int m_insertedAtDepth; }; } #endif