/* Copyright (c) 2015, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation, nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include <LocHeap.h> class LocHeapNode { friend class LocHeap; // size of of the subtree, excluding self, 1 if no subtree int mSize; LocHeapNode* mLeft; LocHeapNode* mRight; LocRankable* mData; public: inline LocHeapNode(LocRankable& data) : mSize(1), mLeft(NULL), mRight(NULL), mData(&data) {} ~LocHeapNode(); // this only swaps the data of the two nodes, so no // detach / re-attached is necessary void swap(LocHeapNode& node); LocRankable* detachData(); // push a node into the tree stucture, keeping sorted by rank void push(LocHeapNode& node); // pop the head node out of the tree stucture. keeping sorted by rank static LocHeapNode* pop(LocHeapNode*& top); // remove a specific node from the tree // returns the pointer to the node removed, which would be either the // same as input (if successfully removed); or NULL (if failed). static LocHeapNode* remove(LocHeapNode*& top, LocRankable& data); // convenience method to compare data ranking inline bool outRanks(LocHeapNode& node) { return mData->outRanks(*node.mData); } inline bool outRanks(LocRankable& data) { return mData->outRanks(data); } // checks if mSize is correct, AND this node is the highest ranking // of the entire subtree bool checkNodes(); inline int getSize() { return mSize; } }; inline LocHeapNode::~LocHeapNode() { if (mLeft) { delete mLeft; mLeft = NULL; } if (mRight) { delete mRight; mRight = NULL; } if (mData) { mData = NULL; } } inline void LocHeapNode::swap(LocHeapNode& node) { LocRankable* tmpData = node.mData; node.mData = mData; mData = tmpData; } inline LocRankable* LocHeapNode::detachData() { LocRankable* data = mData; mData = NULL; return data; } // push keeps the tree sorted by rank, it also tries to balance the // tree by adding the new node to the smaller of the subtrees. // The pointer to the tree and internal links never change. If the // mData of tree top ranks lower than that of the incoming node, // mData will be swapped with that of the incoming node to ensure // ranking, no restructuring the container nodes. void LocHeapNode::push(LocHeapNode& node) { // ensure the current node ranks higher than in the incoming one if (node.outRanks(*this)) { swap(node); } // now drop the new node (ensured lower than *this) into a subtree if (NULL == mLeft) { mLeft = &node; } else if (NULL == mRight) { mRight = &node; } else if (mLeft->mSize <= mRight->mSize) { mLeft->push(node); } else { mRight->push(node); } mSize++; } // pop keeps the tree sorted by rank, but it does not try to balance // the tree. It recursively swaps with the higher ranked top of the // subtrees. // The return is a popped out node from leaf level, that has the data // swapped all the way down from the top. The pinter to the tree and // internal links will not be changed or restructured, except for the // node that is popped out. // If the return pointer == this, this the last node in the tree. LocHeapNode* LocHeapNode::pop(LocHeapNode*& top) { // we know the top has the highest ranking at this point, else // the tree is broken. This top will be popped out. But we need // a node from the left or right child, whichever ranks higher, // to replace the current top. This then will need to be done // recursively to the leaf level. So we swap the mData of the // current top node all the way down to the leaf level. LocHeapNode* poppedNode = top; // top is losing a node in its subtree top->mSize--; if (top->mLeft || top->mRight) { // if mLeft is NULL, mRight for sure is NOT NULL, take that; // else if mRight is NULL, mLeft for sure is NOT, take that; // else we take the address of whatever has higher ranking mData LocHeapNode*& subTop = (NULL == top->mLeft) ? top->mRight : ((NULL == top->mRight) ? top->mLeft : (top->mLeft->outRanks(*(top->mRight)) ? top->mLeft : top->mRight)); // swap mData, the tree top gets updated with the new data. top->swap(*subTop); // pop out from the subtree poppedNode = pop(subTop); } else { // if the top has only single node // detach the poppedNode from the tree // subTop is the reference of ether mLeft or mRight // NOT a local stack pointer. so it MUST be NULL'ed here. top = NULL; } return poppedNode; } // navigating through the tree and find the node that hass the input // data. Since this is a heap, we do recursive linear search. // returns the pointer to the node removed, which would be either the // same as input (if successfully removed); or NULL (if failed). LocHeapNode* LocHeapNode::remove(LocHeapNode*& top, LocRankable& data) { LocHeapNode* removedNode = NULL; // this is the node, by address if (&data == (LocRankable*)(top->mData)) { // pop this node out removedNode = pop(top); } else if (!data.outRanks(*top->mData)) { // subtrees might have this node if (top->mLeft) { removedNode = remove(top->mLeft, data); } // if we did not find in mLeft, and mRight is not empty if (!removedNode && top->mRight) { removedNode = remove(top->mRight, data); } // top lost a node in its subtree if (removedNode) { top->mSize--; } } return removedNode; } // checks if mSize is correct, AND this node is the highest ranking // of the entire subtree bool LocHeapNode::checkNodes() { // size of the current subtree int totalSize = mSize; if (mLeft) { // check the consistency of left subtree if (mLeft->outRanks(*this) || !mLeft->checkNodes()) { return false; } // subtract the size of left subtree (with subtree head) totalSize -= mLeft->mSize; } if (mRight) { // check the consistency of right subtree if (mRight->outRanks(*this) || !mRight->checkNodes()) { return false; } // subtract the size of right subtree (with subtree head) totalSize -= mRight->mSize; } // for the tree nodes to consistent, totalSize must be 1 now return totalSize == 1; } LocHeap::~LocHeap() { if (mTree) { delete mTree; } } void LocHeap::push(LocRankable& node) { LocHeapNode* heapNode = new LocHeapNode(node); if (!mTree) { mTree = heapNode; } else { mTree->push(*heapNode); } } LocRankable* LocHeap::peek() { LocRankable* top = NULL; if (mTree) { top = mTree->mData; } return top; } LocRankable* LocHeap::pop() { LocRankable* locNode = NULL; if (mTree) { // mTree may become NULL after this call LocHeapNode* heapNode = LocHeapNode::pop(mTree); locNode = heapNode->detachData(); delete heapNode; } return locNode; } LocRankable* LocHeap::remove(LocRankable& rankable) { LocRankable* locNode = NULL; if (mTree) { // mTree may become NULL after this call LocHeapNode* heapNode = LocHeapNode::remove(mTree, rankable); if (heapNode) { locNode = heapNode->detachData(); delete heapNode; } } return locNode; } #ifdef __LOC_UNIT_TEST__ bool LocHeap::checkTree() { return ((NULL == mTree) || mTree->checkNodes()); } uint32_t LocHeap::getTreeSize() { return (NULL == mTree) ? 0 : mTree->getSize(); } #endif #ifdef __LOC_DEBUG__ #include <stdio.h> #include <stdlib.h> #include <time.h> class LocHeapDebug : public LocHeap { public: bool checkTree() { return ((NULL == mTree) || mTree->checkNodes()); } uint32_t getTreeSize() { return (NULL == mTree) ? 0 : (mTree->getSize()); } }; class LocHeapDebugData : public LocRankable { const int mID; public: LocHeapDebugData(int id) : mID(id) {} inline virtual int ranks(LocRankable& rankable) { LocHeapDebugData* testData = dynamic_cast<LocHeapDebugData*>(&rankable); return testData->mID - mID; } }; // For Linux command line testing: // compilation: g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../vendor/qcom/proprietary/gps-internal/unit-tests/fakes_for_host -I../../../../system/core/include LocHeap.cpp // test: valgrind --leak-check=full ./a.out 100 int main(int argc, char** argv) { srand(time(NULL)); int tries = atoi(argv[1]); int checks = tries >> 3; LocHeapDebug heap; int treeSize = 0; for (int i = 0; i < tries; i++) { if (i % checks == 0 && !heap.checkTree()) { printf("tree check failed before %dth op\n", i); } int r = rand(); if (r & 1) { LocHeapDebugData* data = new LocHeapDebugData(r >> 1); heap.push(dynamic_cast<LocRankable&>(*data)); treeSize++; } else { LocRankable* rankable = heap.pop(); if (rankable) { delete rankable; } treeSize ? treeSize-- : 0; } printf("%s: %d == %d\n", (r&1)?"push":"pop", treeSize, heap.getTreeSize()); if (treeSize != heap.getTreeSize()) { printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); tries = i+1; break; } } if (!heap.checkTree()) { printf("!!!!!!!!!!tree check failed at the end after %d ops!!!!!!!\n", tries); } else { printf("success!\n"); } for (LocRankable* data = heap.pop(); NULL != data; data = heap.pop()) { delete data; } return 0; } #endif