|
Packit Service |
d40955 |
/*
|
|
Packit Service |
d40955 |
* Copyright (c) 2020 Red Hat, Inc.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
d40955 |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
d40955 |
* as published by the Free Software Foundation; either version 2
|
|
Packit Service |
d40955 |
* of the License, or (at your option) any later version.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
d40955 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
d40955 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
d40955 |
* GNU General Public License for more details.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
d40955 |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
d40955 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
d40955 |
* 02110-1301, USA.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/heap.c#2 $
|
|
Packit Service |
d40955 |
*/
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
#include "heap.h"
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
#include "errors.h"
|
|
Packit Service |
d40955 |
#include "logger.h"
|
|
Packit Service |
d40955 |
#include "numeric.h"
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
#include "statusCodes.h"
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
void initializeHeap(Heap *heap,
|
|
Packit Service |
d40955 |
HeapComparator *comparator,
|
|
Packit Service |
d40955 |
HeapSwapper *swapper,
|
|
Packit Service |
d40955 |
void *array,
|
|
Packit Service |
d40955 |
size_t capacity,
|
|
Packit Service |
d40955 |
size_t elementSize)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
*heap = (Heap) {
|
|
Packit Service |
d40955 |
.comparator = comparator,
|
|
Packit Service |
d40955 |
.swapper = swapper,
|
|
Packit Service |
d40955 |
.capacity = capacity,
|
|
Packit Service |
d40955 |
.elementSize = elementSize,
|
|
Packit Service |
d40955 |
};
|
|
Packit Service |
d40955 |
if (array != NULL) {
|
|
Packit Service |
d40955 |
// Calculating child indexes is simplified by pretending the element array
|
|
Packit Service |
d40955 |
// is 1-based.
|
|
Packit Service |
d40955 |
heap->array = ((byte *) array - elementSize);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
static void siftHeapDown(Heap *heap, size_t topNode, size_t lastNode)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
// Keep sifting until the sub-heap rooted at topNode has no children.
|
|
Packit Service |
d40955 |
size_t leftChild;
|
|
Packit Service |
d40955 |
while ((leftChild = (2 * topNode)) <= lastNode) {
|
|
Packit Service |
d40955 |
// If there are two children, select the largest child to swap with.
|
|
Packit Service |
d40955 |
size_t swapNode = leftChild;
|
|
Packit Service |
d40955 |
if (leftChild < lastNode) {
|
|
Packit Service |
d40955 |
size_t rightChild = leftChild + heap->elementSize;
|
|
Packit Service |
d40955 |
if (heap->comparator(&heap->array[leftChild],
|
|
Packit Service |
d40955 |
&heap->array[rightChild]) < 0) {
|
|
Packit Service |
d40955 |
swapNode = rightChild;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Stop sifting if topNode is at least as large as its largest child,
|
|
Packit Service |
d40955 |
// which means the heap invariant was restored by the previous swap.
|
|
Packit Service |
d40955 |
if (heap->comparator(&heap->array[topNode], &heap->array[swapNode]) >= 0) {
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Swap the element we've been sifting down with the larger child.
|
|
Packit Service |
d40955 |
heap->swapper(&heap->array[topNode], &heap->array[swapNode]);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Descend into the sub-heap rooted at that child, going around the loop
|
|
Packit Service |
d40955 |
// again in place of a tail-recursive call to siftHeapDown().
|
|
Packit Service |
d40955 |
topNode = swapNode;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// We sifted the element all the way to a leaf node of the heap, so the heap
|
|
Packit Service |
d40955 |
// invariant has now been restored.
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
void buildHeap(Heap *heap, size_t count)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
heap->count = minSizeT(count, heap->capacity);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
if ((heap->count < 2) || (heap->elementSize == 0)) {
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/*
|
|
Packit Service |
d40955 |
* All the leaf nodes are trivially valid sub-heaps. Starting with the parent
|
|
Packit Service |
d40955 |
* of the right-most leaf node, restore the heap invariant in that sub-heap
|
|
Packit Service |
d40955 |
* by sifting the top node of the sub-heap down into one of its children's
|
|
Packit Service |
d40955 |
* valid sub-heaps (or not, if the top node is already larger than its
|
|
Packit Service |
d40955 |
* children). Continue iterating through all the interior nodes in the heap,
|
|
Packit Service |
d40955 |
* in sort of a reverse breadth-first traversal, restoring the heap
|
|
Packit Service |
d40955 |
* invariant for each (increasingly larger) sub-heap until we reach the root
|
|
Packit Service |
d40955 |
* of the heap. Once we sift the root node down into one of its two valid
|
|
Packit Service |
d40955 |
* children, the entire heap must be valid, by induction.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* Even though we operate on every node and potentially perform an O(log N)
|
|
Packit Service |
d40955 |
* traversal for each node, the combined probabilities of actually needing
|
|
Packit Service |
d40955 |
* to do a swap and the heights of the sub-heaps sum to a constant, so
|
|
Packit Service |
d40955 |
* restoring a heap from the bottom-up like this has only O(N) complexity.
|
|
Packit Service |
d40955 |
*/
|
|
Packit Service |
d40955 |
size_t size = heap->elementSize;
|
|
Packit Service |
d40955 |
size_t lastParent = size * (heap->count / 2);
|
|
Packit Service |
d40955 |
size_t lastNode = size * heap->count;
|
|
Packit Service |
d40955 |
for (size_t topNode = lastParent; topNode > 0; topNode -= size) {
|
|
Packit Service |
d40955 |
siftHeapDown(heap, topNode, lastNode);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
bool popMaxHeapElement(Heap *heap, void *elementPtr)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
if (heap->count == 0) {
|
|
Packit Service |
d40955 |
return false;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
size_t rootNode = (heap->elementSize * 1);
|
|
Packit Service |
d40955 |
size_t lastNode = (heap->elementSize * heap->count);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Return the maximum element (the root of the heap) if the caller wanted it.
|
|
Packit Service |
d40955 |
if (elementPtr != NULL) {
|
|
Packit Service |
d40955 |
memcpy(elementPtr, &heap->array[rootNode], heap->elementSize);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Move the right-most leaf node to the vacated root node, reducing the
|
|
Packit Service |
d40955 |
// number of elements by one and violating the heap invariant.
|
|
Packit Service |
d40955 |
if (rootNode != lastNode) {
|
|
Packit Service |
d40955 |
memcpy(&heap->array[rootNode], &heap->array[lastNode], heap->elementSize);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
heap->count -= 1;
|
|
Packit Service |
d40955 |
lastNode -= heap->elementSize;
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Restore the heap invariant by sifting the root back down into the heap.
|
|
Packit Service |
d40955 |
siftHeapDown(heap, rootNode, lastNode);
|
|
Packit Service |
d40955 |
return true;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
static inline size_t siftAndSort(Heap *heap, size_t rootNode, size_t lastNode)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
/*
|
|
Packit Service |
d40955 |
* We have a valid heap, so the largest unsorted element is now at the top
|
|
Packit Service |
d40955 |
* of the heap. That element belongs at the start of the partially-sorted
|
|
Packit Service |
d40955 |
* array, preceding all the larger elements that we've already removed
|
|
Packit Service |
d40955 |
* from the heap. Swap that largest unsorted element with the the
|
|
Packit Service |
d40955 |
* right-most leaf node in the heap, moving it to its sorted position in
|
|
Packit Service |
d40955 |
* the array.
|
|
Packit Service |
d40955 |
*/
|
|
Packit Service |
d40955 |
heap->swapper(&heap->array[rootNode], &heap->array[lastNode]);
|
|
Packit Service |
d40955 |
// The sorted list is now one element larger and valid. The heap is
|
|
Packit Service |
d40955 |
// one element smaller, and invalid.
|
|
Packit Service |
d40955 |
lastNode -= heap->elementSize;
|
|
Packit Service |
d40955 |
// Restore the heap invariant by sifting the swapped element back down
|
|
Packit Service |
d40955 |
// into the heap.
|
|
Packit Service |
d40955 |
siftHeapDown(heap, rootNode, lastNode);
|
|
Packit Service |
d40955 |
return lastNode;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
size_t sortHeap(Heap *heap)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
// All zero-length records are identical and therefore already sorted, as
|
|
Packit Service |
d40955 |
// are empty or singleton arrays.
|
|
Packit Service |
d40955 |
if ((heap->count < 2) || (heap->elementSize == 0)) {
|
|
Packit Service |
d40955 |
return heap->count;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Get the byte array offset of the root node, and the right-most leaf node
|
|
Packit Service |
d40955 |
// in the 1-based array of records that will form the heap.
|
|
Packit Service |
d40955 |
size_t rootNode = (heap->elementSize * 1);
|
|
Packit Service |
d40955 |
size_t lastNode = (heap->elementSize * heap->count);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
while (lastNode > rootNode) {
|
|
Packit Service |
d40955 |
lastNode = siftAndSort(heap, rootNode, lastNode);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
size_t count = heap->count;
|
|
Packit Service |
d40955 |
heap->count = 0;
|
|
Packit Service |
d40955 |
return count;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
void *sortNextHeapElement(Heap *heap)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
if ((heap->count == 0) || (heap->elementSize == 0)) {
|
|
Packit Service |
d40955 |
return NULL;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Get the byte array offset of the root node, and the right-most leaf node
|
|
Packit Service |
d40955 |
// in the 1-based array of records that will form the heap.
|
|
Packit Service |
d40955 |
size_t rootNode = (heap->elementSize * 1);
|
|
Packit Service |
d40955 |
size_t lastNode = (heap->elementSize * heap->count);
|
|
Packit Service |
d40955 |
if (heap->count > 1) {
|
|
Packit Service |
d40955 |
siftAndSort(heap, rootNode, lastNode);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
heap->count--;
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
return &heap->array[lastNode];
|
|
Packit Service |
d40955 |
}
|