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
31class 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;
39public:
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
62    inline bool outRanks(LocHeapNode& node) { return mData->outRanks(*node.mData); }
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
69    inline int getSize() { return mSize; }
70};
71
72inline
73LocHeapNode::~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
87inline
88void LocHeapNode::swap(LocHeapNode& node) {
89    LocRankable* tmpData = node.mData;
90    node.mData = mData;
91    mData = tmpData;
92}
93
94inline
95LocRankable* 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.
107void 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.
134LocHeapNode* 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).
170LocHeapNode* 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
197bool 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
222LocHeap::~LocHeap() {
223    if (mTree) {
224        delete mTree;
225    }
226}
227
228void 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
237LocRankable* LocHeap::peek() {
238    LocRankable* top = NULL;
239    if (mTree) {
240        top = mTree->mData;
241    }
242    return top;
243}
244
245LocRankable* 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
256LocRankable* 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__
270bool LocHeap::checkTree() {
271    return ((NULL == mTree) || mTree->checkNodes());
272}
273uint32_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
284class LocHeapDebug : public LocHeap {
285public:
286    bool checkTree() {
287        return ((NULL == mTree) || mTree->checkNodes());
288    }
289
290    uint32_t getTreeSize() {
291        return (NULL == mTree) ? 0 : (mTree->getSize());
292    }
293};
294
295class LocHeapDebugData : public LocRankable {
296    const int mID;
297public:
298    LocHeapDebugData(int id) : mID(id) {}
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
308int 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