Coverage Report

Created: 2025-09-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/common/binaryheap.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * binaryheap.c
4
 *    A simple binary heap implementation
5
 *
6
 * Portions Copyright (c) 2012-2025, 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
binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
40
0
{
41
0
  int     sz;
42
0
  binaryheap *heap;
43
44
0
  sz = offsetof(binaryheap, bh_nodes) + sizeof(bh_node_type) * capacity;
45
0
  heap = (binaryheap *) palloc(sz);
46
0
  heap->bh_space = capacity;
47
0
  heap->bh_compare = compare;
48
0
  heap->bh_arg = arg;
49
50
0
  heap->bh_size = 0;
51
0
  heap->bh_has_heap_property = true;
52
53
0
  return heap;
54
0
}
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
binaryheap_reset(binaryheap *heap)
64
0
{
65
0
  heap->bh_size = 0;
66
0
  heap->bh_has_heap_property = true;
67
0
}
68
69
/*
70
 * binaryheap_free
71
 *
72
 * Releases memory used by the given binaryheap.
73
 */
74
void
75
binaryheap_free(binaryheap *heap)
76
0
{
77
0
  pfree(heap);
78
0
}
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
left_offset(int i)
91
0
{
92
0
  return 2 * i + 1;
93
0
}
94
95
static inline int
96
right_offset(int i)
97
0
{
98
0
  return 2 * i + 2;
99
0
}
100
101
static inline int
102
parent_offset(int i)
103
0
{
104
0
  return (i - 1) / 2;
105
0
}
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
binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
117
0
{
118
0
  if (heap->bh_size >= heap->bh_space)
119
0
  {
120
#ifdef FRONTEND
121
    pg_fatal("out of binary heap slots");
122
#else
123
0
    elog(ERROR, "out of binary heap slots");
124
0
#endif
125
0
  }
126
0
  heap->bh_has_heap_property = false;
127
0
  heap->bh_nodes[heap->bh_size] = d;
128
0
  heap->bh_size++;
129
0
}
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
binaryheap_build(binaryheap *heap)
139
0
{
140
0
  int     i;
141
142
0
  for (i = parent_offset(heap->bh_size - 1); i >= 0; i--)
143
0
    sift_down(heap, i);
144
0
  heap->bh_has_heap_property = true;
145
0
}
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
binaryheap_add(binaryheap *heap, bh_node_type d)
155
0
{
156
0
  if (heap->bh_size >= heap->bh_space)
157
0
  {
158
#ifdef FRONTEND
159
    pg_fatal("out of binary heap slots");
160
#else
161
0
    elog(ERROR, "out of binary heap slots");
162
0
#endif
163
0
  }
164
0
  heap->bh_nodes[heap->bh_size] = d;
165
0
  heap->bh_size++;
166
0
  sift_up(heap, heap->bh_size - 1);
167
0
}
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
binaryheap_first(binaryheap *heap)
178
0
{
179
0
  Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
180
0
  return heap->bh_nodes[0];
181
0
}
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
binaryheap_remove_first(binaryheap *heap)
193
0
{
194
0
  bh_node_type result;
195
196
0
  Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
197
198
  /* extract the root node, which will be the result */
199
0
  result = heap->bh_nodes[0];
200
201
  /* easy if heap contains one element */
202
0
  if (heap->bh_size == 1)
203
0
  {
204
0
    heap->bh_size--;
205
0
    return result;
206
0
  }
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
0
  heap->bh_nodes[0] = heap->bh_nodes[--heap->bh_size];
213
0
  sift_down(heap, 0);
214
215
0
  return result;
216
0
}
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
binaryheap_remove_node(binaryheap *heap, int n)
226
0
{
227
0
  int     cmp;
228
229
0
  Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
230
0
  Assert(n >= 0 && n < heap->bh_size);
231
232
  /* compare last node to the one that is being removed */
233
0
  cmp = heap->bh_compare(heap->bh_nodes[--heap->bh_size],
234
0
               heap->bh_nodes[n],
235
0
               heap->bh_arg);
236
237
  /* remove the last node, placing it in the vacated entry */
238
0
  heap->bh_nodes[n] = heap->bh_nodes[heap->bh_size];
239
240
  /* sift as needed to preserve the heap property */
241
0
  if (cmp > 0)
242
0
    sift_up(heap, n);
243
0
  else if (cmp < 0)
244
0
    sift_down(heap, n);
245
0
}
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
binaryheap_replace_first(binaryheap *heap, bh_node_type d)
256
0
{
257
0
  Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
258
259
0
  heap->bh_nodes[0] = d;
260
261
0
  if (heap->bh_size > 1)
262
0
    sift_down(heap, 0);
263
0
}
264
265
/*
266
 * Sift a node up to the highest position it can hold according to the
267
 * comparator.
268
 */
269
static void
270
sift_up(binaryheap *heap, int node_off)
271
0
{
272
0
  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
0
  while (node_off != 0)
280
0
  {
281
0
    int     cmp;
282
0
    int     parent_off;
283
0
    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
0
    parent_off = parent_offset(node_off);
290
0
    parent_val = heap->bh_nodes[parent_off];
291
0
    cmp = heap->bh_compare(node_val,
292
0
                 parent_val,
293
0
                 heap->bh_arg);
294
0
    if (cmp <= 0)
295
0
      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
0
    heap->bh_nodes[node_off] = parent_val;
302
0
    node_off = parent_off;
303
0
  }
304
  /* Re-fill the hole */
305
0
  heap->bh_nodes[node_off] = node_val;
306
0
}
307
308
/*
309
 * Sift a node down from its current position to satisfy the heap
310
 * property.
311
 */
312
static void
313
sift_down(binaryheap *heap, int node_off)
314
0
{
315
0
  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
0
  while (true)
323
0
  {
324
0
    int     left_off = left_offset(node_off);
325
0
    int     right_off = right_offset(node_off);
326
0
    int     swap_off = left_off;
327
328
    /* Is the right child larger than the left child? */
329
0
    if (right_off < heap->bh_size &&
330
0
      heap->bh_compare(heap->bh_nodes[left_off],
331
0
               heap->bh_nodes[right_off],
332
0
               heap->bh_arg) < 0)
333
0
      swap_off = right_off;
334
335
    /*
336
     * If no children or parent is >= the larger child, heap condition is
337
     * satisfied, and we're done.
338
     */
339
0
    if (left_off >= heap->bh_size ||
340
0
      heap->bh_compare(node_val,
341
0
               heap->bh_nodes[swap_off],
342
0
               heap->bh_arg) >= 0)
343
0
      break;
344
345
    /*
346
     * Otherwise, swap the hole with the child that violates the heap
347
     * property; then go on to check its children.
348
     */
349
0
    heap->bh_nodes[node_off] = heap->bh_nodes[swap_off];
350
0
    node_off = swap_off;
351
0
  }
352
  /* Re-fill the hole */
353
0
  heap->bh_nodes[node_off] = node_val;
354
0
}