Blame source/vdo/base/heap.c

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
}