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.

355 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