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

Generated by: LCOV version 1.16