Suppress "variable may be used uninitialized" warning.
[pgsql.git] / src / common / binaryheap.c
blobc20ed50acc6eb61c107159079bac13b03aa0db6d
1 /*-------------------------------------------------------------------------
3 * binaryheap.c
4 * A simple binary heap implementation
6 * Portions Copyright (c) 2012-2024, PostgreSQL Global Development Group
8 * IDENTIFICATION
9 * src/common/binaryheap.c
11 *-------------------------------------------------------------------------
14 #ifdef FRONTEND
15 #include "postgres_fe.h"
16 #else
17 #include "postgres.h"
18 #endif
20 #include <math.h>
22 #ifdef FRONTEND
23 #include "common/logging.h"
24 #endif
25 #include "common/hashfn.h"
26 #include "lib/binaryheap.h"
29 * Define parameters for hash table code generation. The interface is *also*
30 * declared in binaryheap.h (to generate the types, which are externally
31 * visible).
33 #define SH_PREFIX bh_nodeidx
34 #define SH_ELEMENT_TYPE bh_nodeidx_entry
35 #define SH_KEY_TYPE bh_node_type
36 #define SH_KEY key
37 #define SH_HASH_KEY(tb, key) \
38 hash_bytes((const unsigned char *) &key, sizeof(bh_node_type))
39 #define SH_EQUAL(tb, a, b) (memcmp(&a, &b, sizeof(bh_node_type)) == 0)
40 #define SH_SCOPE extern
41 #ifdef FRONTEND
42 #define SH_RAW_ALLOCATOR pg_malloc0
43 #endif
44 #define SH_STORE_HASH
45 #define SH_GET_HASH(tb, a) a->hash
46 #define SH_DEFINE
47 #include "lib/simplehash.h"
49 static void sift_down(binaryheap *heap, int node_off);
50 static void sift_up(binaryheap *heap, int node_off);
53 * binaryheap_allocate
55 * Returns a pointer to a newly-allocated heap with the given initial number
56 * of nodes, and with the heap property defined by the given comparator
57 * function, which will be invoked with the additional argument specified by
58 * 'arg'.
60 * If 'indexed' is true, we create a hash table to track each node's
61 * index in the heap, enabling to perform some operations such as
62 * binaryheap_remove_node_ptr() etc.
64 binaryheap *
65 binaryheap_allocate(int num_nodes, binaryheap_comparator compare,
66 bool indexed, void *arg)
68 binaryheap *heap;
70 heap = (binaryheap *) palloc(sizeof(binaryheap));
71 heap->bh_space = num_nodes;
72 heap->bh_compare = compare;
73 heap->bh_arg = arg;
75 heap->bh_size = 0;
76 heap->bh_has_heap_property = true;
77 heap->bh_nodes = (bh_node_type *) palloc(sizeof(bh_node_type) * num_nodes);
78 heap->bh_nodeidx = NULL;
80 if (indexed)
82 #ifdef FRONTEND
83 heap->bh_nodeidx = bh_nodeidx_create(num_nodes, NULL);
84 #else
85 heap->bh_nodeidx = bh_nodeidx_create(CurrentMemoryContext, num_nodes,
86 NULL);
87 #endif
90 return heap;
94 * binaryheap_reset
96 * Resets the heap to an empty state, losing its data content but not the
97 * parameters passed at allocation.
99 void
100 binaryheap_reset(binaryheap *heap)
102 heap->bh_size = 0;
103 heap->bh_has_heap_property = true;
105 if (binaryheap_indexed(heap))
106 bh_nodeidx_reset(heap->bh_nodeidx);
110 * binaryheap_free
112 * Releases memory used by the given binaryheap.
114 void
115 binaryheap_free(binaryheap *heap)
117 if (binaryheap_indexed(heap))
118 bh_nodeidx_destroy(heap->bh_nodeidx);
120 pfree(heap->bh_nodes);
121 pfree(heap);
125 * These utility functions return the offset of the left child, right
126 * child, and parent of the node at the given index, respectively.
128 * The heap is represented as an array of nodes, with the root node
129 * stored at index 0. The left child of node i is at index 2*i+1, and
130 * the right child at 2*i+2. The parent of node i is at index (i-1)/2.
133 static inline int
134 left_offset(int i)
136 return 2 * i + 1;
139 static inline int
140 right_offset(int i)
142 return 2 * i + 2;
145 static inline int
146 parent_offset(int i)
148 return (i - 1) / 2;
152 * Double the space allocated for nodes.
154 static void
155 enlarge_node_array(binaryheap *heap)
157 heap->bh_space *= 2;
158 heap->bh_nodes = repalloc(heap->bh_nodes,
159 sizeof(bh_node_type) * heap->bh_space);
163 * Set the given node at the 'index' and track it if required.
165 * Return true if the node's index is already tracked.
167 static bool
168 set_node(binaryheap *heap, bh_node_type node, int index)
170 bool found = false;
172 /* Set the node to the nodes array */
173 heap->bh_nodes[index] = node;
175 if (binaryheap_indexed(heap))
177 bh_nodeidx_entry *ent;
179 /* Keep track of the node index */
180 ent = bh_nodeidx_insert(heap->bh_nodeidx, node, &found);
181 ent->index = index;
184 return found;
188 * Remove the node's index from the hash table if the heap is indexed.
190 static inline void
191 delete_nodeidx(binaryheap *heap, bh_node_type node)
193 if (binaryheap_indexed(heap))
194 bh_nodeidx_delete(heap->bh_nodeidx, node);
198 * Replace the existing node at 'idx' with the given 'new_node'. Also
199 * update their positions accordingly. Note that we assume the new_node's
200 * position is already tracked if enabled, i.e. the new_node is already
201 * present in the heap.
203 static void
204 replace_node(binaryheap *heap, int index, bh_node_type new_node)
206 bool found PG_USED_FOR_ASSERTS_ONLY;
208 /* Quick return if not necessary to move */
209 if (heap->bh_nodes[index] == new_node)
210 return;
212 /* Remove the overwritten node's index */
213 delete_nodeidx(heap, heap->bh_nodes[index]);
216 * Replace it with the given new node. This node's position must also be
217 * tracked as we assume to replace the node with the existing node.
219 found = set_node(heap, new_node, index);
220 Assert(!binaryheap_indexed(heap) || found);
224 * binaryheap_add_unordered
226 * Adds the given datum to the end of the heap's list of nodes in O(1) without
227 * preserving the heap property. This is a convenience to add elements quickly
228 * to a new heap. To obtain a valid heap, one must call binaryheap_build()
229 * afterwards.
231 void
232 binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
234 /* make sure enough space for a new node */
235 if (heap->bh_size >= heap->bh_space)
236 enlarge_node_array(heap);
238 heap->bh_has_heap_property = false;
239 set_node(heap, d, heap->bh_size);
240 heap->bh_size++;
244 * binaryheap_build
246 * Assembles a valid heap in O(n) from the nodes added by
247 * binaryheap_add_unordered(). Not needed otherwise.
249 void
250 binaryheap_build(binaryheap *heap)
252 int i;
254 for (i = parent_offset(heap->bh_size - 1); i >= 0; i--)
255 sift_down(heap, i);
256 heap->bh_has_heap_property = true;
260 * binaryheap_add
262 * Adds the given datum to the heap in O(log n) time, while preserving
263 * the heap property.
265 void
266 binaryheap_add(binaryheap *heap, bh_node_type d)
268 /* make sure enough space for a new node */
269 if (heap->bh_size >= heap->bh_space)
270 enlarge_node_array(heap);
272 set_node(heap, d, heap->bh_size);
273 heap->bh_size++;
274 sift_up(heap, heap->bh_size - 1);
278 * binaryheap_first
280 * Returns a pointer to the first (root, topmost) node in the heap
281 * without modifying the heap. The caller must ensure that this
282 * routine is not used on an empty heap. Always O(1).
284 bh_node_type
285 binaryheap_first(binaryheap *heap)
287 Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
288 return heap->bh_nodes[0];
292 * binaryheap_remove_first
294 * Removes the first (root, topmost) node in the heap and returns a
295 * pointer to it after rebalancing the heap. The caller must ensure
296 * that this routine is not used on an empty heap. O(log n) worst
297 * case.
299 bh_node_type
300 binaryheap_remove_first(binaryheap *heap)
302 bh_node_type result;
304 Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
306 /* extract the root node, which will be the result */
307 result = heap->bh_nodes[0];
309 /* easy if heap contains one element */
310 if (heap->bh_size == 1)
312 heap->bh_size--;
313 delete_nodeidx(heap, result);
315 return result;
319 * Remove the last node, placing it in the vacated root entry, and sift
320 * the new root node down to its correct position.
322 replace_node(heap, 0, heap->bh_nodes[--heap->bh_size]);
323 sift_down(heap, 0);
325 return result;
329 * binaryheap_remove_node
331 * Removes the nth (zero based) node from the heap. The caller must ensure
332 * that there are at least (n + 1) nodes in the heap. O(log n) worst case.
334 void
335 binaryheap_remove_node(binaryheap *heap, int n)
337 int cmp;
339 Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
340 Assert(n >= 0 && n < heap->bh_size);
342 /* compare last node to the one that is being removed */
343 cmp = heap->bh_compare(heap->bh_nodes[--heap->bh_size],
344 heap->bh_nodes[n],
345 heap->bh_arg);
347 /* remove the last node, placing it in the vacated entry */
348 replace_node(heap, n, heap->bh_nodes[heap->bh_size]);
350 /* sift as needed to preserve the heap property */
351 if (cmp > 0)
352 sift_up(heap, n);
353 else if (cmp < 0)
354 sift_down(heap, n);
358 * binaryheap_remove_node_ptr
360 * Similar to binaryheap_remove_node() but removes the given node. The caller
361 * must ensure that the given node is in the heap. O(log n) worst case.
363 * This function can be used only if the heap is indexed.
365 void
366 binaryheap_remove_node_ptr(binaryheap *heap, bh_node_type d)
368 bh_nodeidx_entry *ent;
370 Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
371 Assert(binaryheap_indexed(heap));
373 ent = bh_nodeidx_lookup(heap->bh_nodeidx, d);
374 Assert(ent);
376 binaryheap_remove_node(heap, ent->index);
380 * Workhorse for binaryheap_update_up and binaryheap_update_down.
382 static void
383 resift_node(binaryheap *heap, bh_node_type node, bool sift_dir_up)
385 bh_nodeidx_entry *ent;
387 Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
388 Assert(binaryheap_indexed(heap));
390 ent = bh_nodeidx_lookup(heap->bh_nodeidx, node);
391 Assert(ent);
392 Assert(ent->index >= 0 && ent->index < heap->bh_size);
394 if (sift_dir_up)
395 sift_up(heap, ent->index);
396 else
397 sift_down(heap, ent->index);
401 * binaryheap_update_up
403 * Sift the given node up after the node's key is updated. The caller must
404 * ensure that the given node is in the heap. O(log n) worst case.
406 * This function can be used only if the heap is indexed.
408 void
409 binaryheap_update_up(binaryheap *heap, bh_node_type d)
411 resift_node(heap, d, true);
415 * binaryheap_update_down
417 * Sift the given node down after the node's key is updated. The caller must
418 * ensure that the given node is in the heap. O(log n) worst case.
420 * This function can be used only if the heap is indexed.
422 void
423 binaryheap_update_down(binaryheap *heap, bh_node_type d)
425 resift_node(heap, d, false);
429 * binaryheap_replace_first
431 * Replace the topmost element of a non-empty heap, preserving the heap
432 * property. O(1) in the best case, or O(log n) if it must fall back to
433 * sifting the new node down.
435 void
436 binaryheap_replace_first(binaryheap *heap, bh_node_type d)
438 Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
440 replace_node(heap, 0, d);
442 if (heap->bh_size > 1)
443 sift_down(heap, 0);
447 * Sift a node up to the highest position it can hold according to the
448 * comparator.
450 static void
451 sift_up(binaryheap *heap, int node_off)
453 bh_node_type node_val = heap->bh_nodes[node_off];
456 * Within the loop, the node_off'th array entry is a "hole" that
457 * notionally holds node_val, but we don't actually store node_val there
458 * till the end, saving some unnecessary data copying steps.
460 while (node_off != 0)
462 int cmp;
463 int parent_off;
464 bh_node_type parent_val;
467 * If this node is smaller than its parent, the heap condition is
468 * satisfied, and we're done.
470 parent_off = parent_offset(node_off);
471 parent_val = heap->bh_nodes[parent_off];
472 cmp = heap->bh_compare(node_val,
473 parent_val,
474 heap->bh_arg);
475 if (cmp <= 0)
476 break;
479 * Otherwise, swap the parent value with the hole, and go on to check
480 * the node's new parent.
482 set_node(heap, parent_val, node_off);
483 node_off = parent_off;
485 /* Re-fill the hole */
486 set_node(heap, node_val, node_off);
490 * Sift a node down from its current position to satisfy the heap
491 * property.
493 static void
494 sift_down(binaryheap *heap, int node_off)
496 bh_node_type node_val = heap->bh_nodes[node_off];
499 * Within the loop, the node_off'th array entry is a "hole" that
500 * notionally holds node_val, but we don't actually store node_val there
501 * till the end, saving some unnecessary data copying steps.
503 while (true)
505 int left_off = left_offset(node_off);
506 int right_off = right_offset(node_off);
507 int swap_off = 0;
509 /* Is the left child larger than the parent? */
510 if (left_off < heap->bh_size &&
511 heap->bh_compare(node_val,
512 heap->bh_nodes[left_off],
513 heap->bh_arg) < 0)
514 swap_off = left_off;
516 /* Is the right child larger than the parent? */
517 if (right_off < heap->bh_size &&
518 heap->bh_compare(node_val,
519 heap->bh_nodes[right_off],
520 heap->bh_arg) < 0)
522 /* swap with the larger child */
523 if (!swap_off ||
524 heap->bh_compare(heap->bh_nodes[left_off],
525 heap->bh_nodes[right_off],
526 heap->bh_arg) < 0)
527 swap_off = right_off;
531 * If we didn't find anything to swap, the heap condition is
532 * satisfied, and we're done.
534 if (!swap_off)
535 break;
538 * Otherwise, swap the hole with the child that violates the heap
539 * property; then go on to check its children.
541 set_node(heap, heap->bh_nodes[swap_off], node_off);
542 node_off = swap_off;
544 /* Re-fill the hole */
545 set_node(heap, node_val, node_off);