LCOV - code coverage report
Current view: top level - src/common - binaryheap.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 93 98 94.9 %
Date: 2024-04-29 20:11:15 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * binaryheap.c
       4             :  *    A simple binary heap implementation
       5             :  *
       6             :  * Portions Copyright (c) 2012-2024, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/common/binaryheap.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : 
      14             : #ifdef FRONTEND
      15             : #include "postgres_fe.h"
      16             : #else
      17             : #include "postgres.h"
      18             : #endif
      19             : 
      20             : #include <math.h>
      21             : 
      22             : #ifdef FRONTEND
      23             : #include "common/logging.h"
      24             : #endif
      25             : #include "lib/binaryheap.h"
      26             : 
      27             : static void sift_down(binaryheap *heap, int node_off);
      28             : static void sift_up(binaryheap *heap, int node_off);
      29             : 
      30             : /*
      31             :  * binaryheap_allocate
      32             :  *
      33             :  * Returns a pointer to a newly-allocated heap that has the capacity to
      34             :  * store the given number of nodes, with the heap property defined by
      35             :  * the given comparator function, which will be invoked with the additional
      36             :  * argument specified by 'arg'.
      37             :  */
      38             : binaryheap *
      39        6470 : binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
      40             : {
      41             :     int         sz;
      42             :     binaryheap *heap;
      43             : 
      44        6470 :     sz = offsetof(binaryheap, bh_nodes) + sizeof(bh_node_type) * capacity;
      45        6470 :     heap = (binaryheap *) palloc(sz);
      46        6470 :     heap->bh_space = capacity;
      47        6470 :     heap->bh_compare = compare;
      48        6470 :     heap->bh_arg = arg;
      49             : 
      50        6470 :     heap->bh_size = 0;
      51        6470 :     heap->bh_has_heap_property = true;
      52             : 
      53        6470 :     return heap;
      54             : }
      55             : 
      56             : /*
      57             :  * binaryheap_reset
      58             :  *
      59             :  * Resets the heap to an empty state, losing its data content but not the
      60             :  * parameters passed at allocation.
      61             :  */
      62             : void
      63         336 : binaryheap_reset(binaryheap *heap)
      64             : {
      65         336 :     heap->bh_size = 0;
      66         336 :     heap->bh_has_heap_property = true;
      67         336 : }
      68             : 
      69             : /*
      70             :  * binaryheap_free
      71             :  *
      72             :  * Releases memory used by the given binaryheap.
      73             :  */
      74             : void
      75        5630 : binaryheap_free(binaryheap *heap)
      76             : {
      77        5630 :     pfree(heap);
      78        5630 : }
      79             : 
      80             : /*
      81             :  * These utility functions return the offset of the left child, right
      82             :  * child, and parent of the node at the given index, respectively.
      83             :  *
      84             :  * The heap is represented as an array of nodes, with the root node
      85             :  * stored at index 0. The left child of node i is at index 2*i+1, and
      86             :  * the right child at 2*i+2. The parent of node i is at index (i-1)/2.
      87             :  */
      88             : 
      89             : static inline int
      90    21244922 : left_offset(int i)
      91             : {
      92    21244922 :     return 2 * i + 1;
      93             : }
      94             : 
      95             : static inline int
      96    21244922 : right_offset(int i)
      97             : {
      98    21244922 :     return 2 * i + 2;
      99             : }
     100             : 
     101             : static inline int
     102     5444832 : parent_offset(int i)
     103             : {
     104     5444832 :     return (i - 1) / 2;
     105             : }
     106             : 
     107             : /*
     108             :  * binaryheap_add_unordered
     109             :  *
     110             :  * Adds the given datum to the end of the heap's list of nodes in O(1) without
     111             :  * preserving the heap property. This is a convenience to add elements quickly
     112             :  * to a new heap. To obtain a valid heap, one must call binaryheap_build()
     113             :  * afterwards.
     114             :  */
     115             : void
     116       52956 : binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
     117             : {
     118       52956 :     if (heap->bh_size >= heap->bh_space)
     119             :     {
     120             : #ifdef FRONTEND
     121           0 :         pg_fatal("out of binary heap slots");
     122             : #else
     123           0 :         elog(ERROR, "out of binary heap slots");
     124             : #endif
     125             :     }
     126       52956 :     heap->bh_has_heap_property = false;
     127       52956 :     heap->bh_nodes[heap->bh_size] = d;
     128       52956 :     heap->bh_size++;
     129       52956 : }
     130             : 
     131             : /*
     132             :  * binaryheap_build
     133             :  *
     134             :  * Assembles a valid heap in O(n) from the nodes added by
     135             :  * binaryheap_add_unordered(). Not needed otherwise.
     136             :  */
     137             : void
     138        6078 : binaryheap_build(binaryheap *heap)
     139             : {
     140             :     int         i;
     141             : 
     142       34500 :     for (i = parent_offset(heap->bh_size - 1); i >= 0; i--)
     143       28422 :         sift_down(heap, i);
     144        6078 :     heap->bh_has_heap_property = true;
     145        6078 : }
     146             : 
     147             : /*
     148             :  * binaryheap_add
     149             :  *
     150             :  * Adds the given datum to the heap in O(log n) time, while preserving
     151             :  * the heap property.
     152             :  */
     153             : void
     154     2054126 : binaryheap_add(binaryheap *heap, bh_node_type d)
     155             : {
     156     2054126 :     if (heap->bh_size >= heap->bh_space)
     157             :     {
     158             : #ifdef FRONTEND
     159           0 :         pg_fatal("out of binary heap slots");
     160             : #else
     161           0 :         elog(ERROR, "out of binary heap slots");
     162             : #endif
     163             :     }
     164     2054126 :     heap->bh_nodes[heap->bh_size] = d;
     165     2054126 :     heap->bh_size++;
     166     2054126 :     sift_up(heap, heap->bh_size - 1);
     167     2054126 : }
     168             : 
     169             : /*
     170             :  * binaryheap_first
     171             :  *
     172             :  * Returns a pointer to the first (root, topmost) node in the heap
     173             :  * without modifying the heap. The caller must ensure that this
     174             :  * routine is not used on an empty heap. Always O(1).
     175             :  */
     176             : bh_node_type
     177     1972976 : binaryheap_first(binaryheap *heap)
     178             : {
     179             :     Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
     180     1972976 :     return heap->bh_nodes[0];
     181             : }
     182             : 
     183             : /*
     184             :  * binaryheap_remove_first
     185             :  *
     186             :  * Removes the first (root, topmost) node in the heap and returns a
     187             :  * pointer to it after rebalancing the heap. The caller must ensure
     188             :  * that this routine is not used on an empty heap. O(log n) worst
     189             :  * case.
     190             :  */
     191             : bh_node_type
     192     2106780 : binaryheap_remove_first(binaryheap *heap)
     193             : {
     194             :     bh_node_type result;
     195             : 
     196             :     Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
     197             : 
     198             :     /* extract the root node, which will be the result */
     199     2106780 :     result = heap->bh_nodes[0];
     200             : 
     201             :     /* easy if heap contains one element */
     202     2106780 :     if (heap->bh_size == 1)
     203             :     {
     204        8186 :         heap->bh_size--;
     205        8186 :         return result;
     206             :     }
     207             : 
     208             :     /*
     209             :      * Remove the last node, placing it in the vacated root entry, and sift
     210             :      * the new root node down to its correct position.
     211             :      */
     212     2098594 :     heap->bh_nodes[0] = heap->bh_nodes[--heap->bh_size];
     213     2098594 :     sift_down(heap, 0);
     214             : 
     215     2098594 :     return result;
     216             : }
     217             : 
     218             : /*
     219             :  * binaryheap_remove_node
     220             :  *
     221             :  * Removes the nth (zero based) node from the heap.  The caller must ensure
     222             :  * that there are at least (n + 1) nodes in the heap.  O(log n) worst case.
     223             :  */
     224             : void
     225          92 : binaryheap_remove_node(binaryheap *heap, int n)
     226             : {
     227             :     int         cmp;
     228             : 
     229             :     Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
     230             :     Assert(n >= 0 && n < heap->bh_size);
     231             : 
     232             :     /* compare last node to the one that is being removed */
     233          92 :     cmp = heap->bh_compare(heap->bh_nodes[--heap->bh_size],
     234             :                            heap->bh_nodes[n],
     235             :                            heap->bh_arg);
     236             : 
     237             :     /* remove the last node, placing it in the vacated entry */
     238          92 :     heap->bh_nodes[n] = heap->bh_nodes[heap->bh_size];
     239             : 
     240             :     /* sift as needed to preserve the heap property */
     241          92 :     if (cmp > 0)
     242           0 :         sift_up(heap, n);
     243          92 :     else if (cmp < 0)
     244          64 :         sift_down(heap, n);
     245          92 : }
     246             : 
     247             : /*
     248             :  * binaryheap_replace_first
     249             :  *
     250             :  * Replace the topmost element of a non-empty heap, preserving the heap
     251             :  * property.  O(1) in the best case, or O(log n) if it must fall back to
     252             :  * sifting the new node down.
     253             :  */
     254             : void
     255     1549088 : binaryheap_replace_first(binaryheap *heap, bh_node_type d)
     256             : {
     257             :     Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
     258             : 
     259     1549088 :     heap->bh_nodes[0] = d;
     260             : 
     261     1549088 :     if (heap->bh_size > 1)
     262      593088 :         sift_down(heap, 0);
     263     1549088 : }
     264             : 
     265             : /*
     266             :  * Sift a node up to the highest position it can hold according to the
     267             :  * comparator.
     268             :  */
     269             : static void
     270     2054126 : sift_up(binaryheap *heap, int node_off)
     271             : {
     272     2054126 :     bh_node_type node_val = heap->bh_nodes[node_off];
     273             : 
     274             :     /*
     275             :      * Within the loop, the node_off'th array entry is a "hole" that
     276             :      * notionally holds node_val, but we don't actually store node_val there
     277             :      * till the end, saving some unnecessary data copying steps.
     278             :      */
     279     5636922 :     while (node_off != 0)
     280             :     {
     281             :         int         cmp;
     282             :         int         parent_off;
     283             :         bh_node_type parent_val;
     284             : 
     285             :         /*
     286             :          * If this node is smaller than its parent, the heap condition is
     287             :          * satisfied, and we're done.
     288             :          */
     289     5438754 :         parent_off = parent_offset(node_off);
     290     5438754 :         parent_val = heap->bh_nodes[parent_off];
     291     5438754 :         cmp = heap->bh_compare(node_val,
     292             :                                parent_val,
     293             :                                heap->bh_arg);
     294     5438754 :         if (cmp <= 0)
     295     1855958 :             break;
     296             : 
     297             :         /*
     298             :          * Otherwise, swap the parent value with the hole, and go on to check
     299             :          * the node's new parent.
     300             :          */
     301     3582796 :         heap->bh_nodes[node_off] = parent_val;
     302     3582796 :         node_off = parent_off;
     303             :     }
     304             :     /* Re-fill the hole */
     305     2054126 :     heap->bh_nodes[node_off] = node_val;
     306     2054126 : }
     307             : 
     308             : /*
     309             :  * Sift a node down from its current position to satisfy the heap
     310             :  * property.
     311             :  */
     312             : static void
     313     2720168 : sift_down(binaryheap *heap, int node_off)
     314             : {
     315     2720168 :     bh_node_type node_val = heap->bh_nodes[node_off];
     316             : 
     317             :     /*
     318             :      * Within the loop, the node_off'th array entry is a "hole" that
     319             :      * notionally holds node_val, but we don't actually store node_val there
     320             :      * till the end, saving some unnecessary data copying steps.
     321             :      */
     322             :     while (true)
     323    18524754 :     {
     324    21244922 :         int         left_off = left_offset(node_off);
     325    21244922 :         int         right_off = right_offset(node_off);
     326    21244922 :         int         swap_off = 0;
     327             : 
     328             :         /* Is the left child larger than the parent? */
     329    40852170 :         if (left_off < heap->bh_size &&
     330    19607248 :             heap->bh_compare(node_val,
     331             :                              heap->bh_nodes[left_off],
     332             :                              heap->bh_arg) < 0)
     333    18042614 :             swap_off = left_off;
     334             : 
     335             :         /* Is the right child larger than the parent? */
     336    40174932 :         if (right_off < heap->bh_size &&
     337    18930010 :             heap->bh_compare(node_val,
     338             :                              heap->bh_nodes[right_off],
     339             :                              heap->bh_arg) < 0)
     340             :         {
     341             :             /* swap with the larger child */
     342    35643172 :             if (!swap_off ||
     343    17580516 :                 heap->bh_compare(heap->bh_nodes[left_off],
     344             :                                  heap->bh_nodes[right_off],
     345             :                                  heap->bh_arg) < 0)
     346     8974862 :                 swap_off = right_off;
     347             :         }
     348             : 
     349             :         /*
     350             :          * If we didn't find anything to swap, the heap condition is
     351             :          * satisfied, and we're done.
     352             :          */
     353    21244922 :         if (!swap_off)
     354     2720168 :             break;
     355             : 
     356             :         /*
     357             :          * Otherwise, swap the hole with the child that violates the heap
     358             :          * property; then go on to check its children.
     359             :          */
     360    18524754 :         heap->bh_nodes[node_off] = heap->bh_nodes[swap_off];
     361    18524754 :         node_off = swap_off;
     362             :     }
     363             :     /* Re-fill the hole */
     364     2720168 :     heap->bh_nodes[node_off] = node_val;
     365     2720168 : }

Generated by: LCOV version 1.14