1 /* Copyright (c) 2015, The Linux Foundation. All rights reserved.
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are
5  * met:
6  *     * Redistributions of source code must retain the above copyright
7  *       notice, this list of conditions and the following disclaimer.
8  *     * Redistributions in binary form must reproduce the above
9  *       copyright notice, this list of conditions and the following
10  *       disclaimer in the documentation and/or other materials provided
11  *       with the distribution.
12  *     * Neither the name of The Linux Foundation, nor the names of its
13  *       contributors may be used to endorse or promote products derived
14  *       from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 #include <LocHeap.h>
30 
31 class LocHeapNode {
32     friend class LocHeap;
33 
34     // size of of the subtree, excluding self, 1 if no subtree
35     int mSize;
36     LocHeapNode* mLeft;
37     LocHeapNode* mRight;
38     LocRankable* mData;
39 public:
LocHeapNode(LocRankable & data)40     inline LocHeapNode(LocRankable& data) :
41         mSize(1), mLeft(NULL), mRight(NULL), mData(&data) {}
42     ~LocHeapNode();
43 
44     // this only swaps the data of the two nodes, so no
45     // detach / re-attached is necessary
46     void swap(LocHeapNode& node);
47 
48     LocRankable* detachData();
49 
50     // push a node into the tree stucture, keeping sorted by rank
51     void push(LocHeapNode& node);
52 
53     // pop the head node out of the tree stucture. keeping sorted by rank
54     static LocHeapNode* pop(LocHeapNode*& top);
55 
56     // remove a specific node from the tree
57     // returns the pointer to the node removed, which would be either the
58     //         same as input (if successfully removed); or NULL (if failed).
59     static LocHeapNode* remove(LocHeapNode*& top, LocRankable& data);
60 
61     // convenience method to compare data ranking
outRanks(LocHeapNode & node)62     inline bool outRanks(LocHeapNode& node) { return mData->outRanks(*node.mData); }
outRanks(LocRankable & data)63     inline bool outRanks(LocRankable& data) { return mData->outRanks(data); }
64 
65     // checks if mSize is correct, AND this node is the highest ranking
66     // of the entire subtree
67     bool checkNodes();
68 
getSize()69     inline int getSize() { return mSize; }
70 };
71 
72 inline
~LocHeapNode()73 LocHeapNode::~LocHeapNode() {
74     if (mLeft) {
75         delete mLeft;
76         mLeft = NULL;
77     }
78     if (mRight) {
79         delete mRight;
80         mRight = NULL;
81     }
82     if (mData) {
83         mData = NULL;
84     }
85 }
86 
87 inline
swap(LocHeapNode & node)88 void LocHeapNode::swap(LocHeapNode& node) {
89     LocRankable* tmpData = node.mData;
90     node.mData = mData;
91     mData = tmpData;
92 }
93 
94 inline
detachData()95 LocRankable* LocHeapNode::detachData()  {
96     LocRankable* data = mData;
97     mData = NULL;
98     return data;
99 }
100 
101 // push keeps the tree sorted by rank, it also tries to balance the
102 // tree by adding the new node to the smaller of the subtrees.
103 // The pointer to the tree and internal links never change. If the
104 // mData of tree top ranks lower than that of the incoming node,
105 // mData will be swapped with that of the incoming node to ensure
106 // ranking, no restructuring the container nodes.
push(LocHeapNode & node)107 void LocHeapNode::push(LocHeapNode& node) {
108     // ensure the current node ranks higher than in the incoming one
109     if (node.outRanks(*this)) {
110         swap(node);
111     }
112 
113     // now drop the new node (ensured lower than *this) into a subtree
114     if (NULL == mLeft) {
115         mLeft = &node;
116     } else if (NULL == mRight) {
117         mRight = &node;
118     } else if (mLeft->mSize <= mRight->mSize) {
119         mLeft->push(node);
120     } else {
121         mRight->push(node);
122     }
123     mSize++;
124 }
125 
126 // pop keeps the tree sorted by rank, but it does not try to balance
127 // the tree. It recursively swaps with the higher ranked top of the
128 // subtrees.
129 // The return is a popped out node from leaf level, that has the data
130 // swapped all the way down from the top. The pinter to the tree and
131 // internal links will not be changed or restructured, except for the
132 // node that is popped out.
133 // If the return pointer == this, this the last node in the tree.
pop(LocHeapNode * & top)134 LocHeapNode* LocHeapNode::pop(LocHeapNode*& top) {
135     // we know the top has the highest ranking at this point, else
136     // the tree is broken. This top will be popped out.  But we need
137     // a node from the left or right child, whichever ranks higher,
138     // to replace the current top. This then will need to be done
139     // recursively to the leaf level. So we swap the mData of the
140     // current top node all the way down to the leaf level.
141     LocHeapNode* poppedNode = top;
142     // top is losing a node in its subtree
143     top->mSize--;
144     if (top->mLeft || top->mRight) {
145         // if mLeft is NULL, mRight for sure is NOT NULL, take that;
146         // else if mRight is NULL, mLeft for sure is NOT, take that;
147         // else we take the address of whatever has higher ranking mData
148         LocHeapNode*& subTop = (NULL == top->mLeft) ? top->mRight :
149             ((NULL == top->mRight) ? top->mLeft :
150              (top->mLeft->outRanks(*(top->mRight)) ? top->mLeft : top->mRight));
151         // swap mData, the tree top gets updated with the new data.
152         top->swap(*subTop);
153         // pop out from the subtree
154         poppedNode = pop(subTop);
155     } else {
156         // if the top has only single node
157         // detach the poppedNode from the tree
158         // subTop is the reference of ether mLeft or mRight
159         // NOT a local stack pointer. so it MUST be NULL'ed here.
160         top = NULL;
161     }
162 
163     return poppedNode;
164 }
165 
166 // navigating through the tree and find the node that hass the input
167 // data. Since this is a heap, we do recursive linear search.
168 // returns the pointer to the node removed, which would be either the
169 //         same as input (if successfully removed); or NULL (if failed).
remove(LocHeapNode * & top,LocRankable & data)170 LocHeapNode* LocHeapNode::remove(LocHeapNode*& top, LocRankable& data) {
171     LocHeapNode* removedNode = NULL;
172     // this is the node, by address
173     if (&data == (LocRankable*)(top->mData)) {
174         // pop this node out
175         removedNode = pop(top);
176     } else if (!data.outRanks(*top->mData)) {
177         // subtrees might have this node
178         if (top->mLeft) {
179             removedNode = remove(top->mLeft, data);
180         }
181         // if we did not find in mLeft, and mRight is not empty
182         if (!removedNode && top->mRight) {
183             removedNode = remove(top->mRight, data);
184         }
185 
186         // top lost a node in its subtree
187         if (removedNode) {
188             top->mSize--;
189         }
190     }
191 
192     return removedNode;
193 }
194 
195 // checks if mSize is correct, AND this node is the highest ranking
196 // of the entire subtree
checkNodes()197 bool LocHeapNode::checkNodes() {
198     // size of the current subtree
199     int totalSize = mSize;
200     if (mLeft) {
201         // check the consistency of left subtree
202         if (mLeft->outRanks(*this) || !mLeft->checkNodes()) {
203             return false;
204         }
205         // subtract the size of left subtree (with subtree head)
206         totalSize -= mLeft->mSize;
207     }
208 
209     if (mRight) {
210         // check the consistency of right subtree
211         if (mRight->outRanks(*this) || !mRight->checkNodes()) {
212             return false;
213         }
214         // subtract the size of right subtree (with subtree head)
215         totalSize -= mRight->mSize;
216     }
217 
218     // for the tree nodes to consistent, totalSize must be 1 now
219     return totalSize == 1;
220 }
221 
~LocHeap()222 LocHeap::~LocHeap() {
223     if (mTree) {
224         delete mTree;
225     }
226 }
227 
push(LocRankable & node)228 void LocHeap::push(LocRankable& node) {
229     LocHeapNode* heapNode = new LocHeapNode(node);
230     if (!mTree) {
231         mTree = heapNode;
232     } else {
233         mTree->push(*heapNode);
234     }
235 }
236 
peek()237 LocRankable* LocHeap::peek() {
238     LocRankable* top = NULL;
239     if (mTree) {
240         top = mTree->mData;
241     }
242     return top;
243 }
244 
pop()245 LocRankable* LocHeap::pop() {
246     LocRankable* locNode = NULL;
247     if (mTree) {
248         // mTree may become NULL after this call
249         LocHeapNode* heapNode = LocHeapNode::pop(mTree);
250         locNode = heapNode->detachData();
251         delete heapNode;
252     }
253     return locNode;
254 }
255 
remove(LocRankable & rankable)256 LocRankable* LocHeap::remove(LocRankable& rankable) {
257     LocRankable* locNode = NULL;
258     if (mTree) {
259         // mTree may become NULL after this call
260         LocHeapNode* heapNode = LocHeapNode::remove(mTree, rankable);
261         if (heapNode) {
262             locNode = heapNode->detachData();
263             delete heapNode;
264         }
265     }
266     return locNode;
267 }
268 
269 #ifdef __LOC_UNIT_TEST__
checkTree()270 bool LocHeap::checkTree() {
271     return ((NULL == mTree) || mTree->checkNodes());
272 }
getTreeSize()273 uint32_t LocHeap::getTreeSize() {
274     return (NULL == mTree) ? 0 : mTree->getSize();
275 }
276 #endif
277 
278 #ifdef __LOC_DEBUG__
279 
280 #include <stdio.h>
281 #include <stdlib.h>
282 #include <time.h>
283 
284 class LocHeapDebug : public LocHeap {
285 public:
checkTree()286     bool checkTree() {
287         return ((NULL == mTree) || mTree->checkNodes());
288     }
289 
getTreeSize()290     uint32_t getTreeSize() {
291         return (NULL == mTree) ? 0 : (mTree->getSize());
292     }
293 };
294 
295 class LocHeapDebugData : public LocRankable {
296     const int mID;
297 public:
LocHeapDebugData(int id)298     LocHeapDebugData(int id) : mID(id) {}
ranks(LocRankable & rankable)299     inline virtual int ranks(LocRankable& rankable) {
300         LocHeapDebugData* testData = dynamic_cast<LocHeapDebugData*>(&rankable);
301         return testData->mID - mID;
302     }
303 };
304 
305 // For Linux command line testing:
306 // 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
307 // test: valgrind --leak-check=full ./a.out 100
main(int argc,char ** argv)308 int main(int argc, char** argv) {
309     srand(time(NULL));
310     int tries = atoi(argv[1]);
311     int checks = tries >> 3;
312     LocHeapDebug heap;
313     int treeSize = 0;
314 
315     for (int i = 0; i < tries; i++) {
316         if (i % checks == 0 && !heap.checkTree()) {
317             printf("tree check failed before %dth op\n", i);
318         }
319         int r = rand();
320 
321         if (r & 1) {
322             LocHeapDebugData* data = new LocHeapDebugData(r >> 1);
323             heap.push(dynamic_cast<LocRankable&>(*data));
324             treeSize++;
325         } else {
326             LocRankable* rankable = heap.pop();
327             if (rankable) {
328                 delete rankable;
329             }
330             treeSize ? treeSize-- : 0;
331         }
332 
333         printf("%s: %d == %d\n", (r&1)?"push":"pop", treeSize, heap.getTreeSize());
334         if (treeSize != heap.getTreeSize()) {
335             printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
336             tries = i+1;
337             break;
338         }
339     }
340 
341     if (!heap.checkTree()) {
342         printf("!!!!!!!!!!tree check failed at the end after %d ops!!!!!!!\n", tries);
343     } else {
344         printf("success!\n");
345     }
346 
347     for (LocRankable* data = heap.pop(); NULL != data; data = heap.pop()) {
348         delete data;
349     }
350 
351     return 0;
352 }
353 
354 #endif
355