You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
11 KiB
354 lines
11 KiB
/* 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
|
|
|