diff options
Diffstat (limited to 'src/common/binaryheap.c')
-rw-r--r-- | src/common/binaryheap.c | 198 |
1 files changed, 188 insertions, 10 deletions
diff --git a/src/common/binaryheap.c b/src/common/binaryheap.c index 2ffd656e87b..c20ed50acc6 100644 --- a/src/common/binaryheap.c +++ b/src/common/binaryheap.c @@ -22,8 +22,30 @@ #ifdef FRONTEND #include "common/logging.h" #endif +#include "common/hashfn.h" #include "lib/binaryheap.h" +/* + * Define parameters for hash table code generation. The interface is *also* + * declared in binaryheap.h (to generate the types, which are externally + * visible). + */ +#define SH_PREFIX bh_nodeidx +#define SH_ELEMENT_TYPE bh_nodeidx_entry +#define SH_KEY_TYPE bh_node_type +#define SH_KEY key +#define SH_HASH_KEY(tb, key) \ + hash_bytes((const unsigned char *) &key, sizeof(bh_node_type)) +#define SH_EQUAL(tb, a, b) (memcmp(&a, &b, sizeof(bh_node_type)) == 0) +#define SH_SCOPE extern +#ifdef FRONTEND +#define SH_RAW_ALLOCATOR pg_malloc0 +#endif +#define SH_STORE_HASH +#define SH_GET_HASH(tb, a) a->hash +#define SH_DEFINE +#include "lib/simplehash.h" + static void sift_down(binaryheap *heap, int node_off); static void sift_up(binaryheap *heap, int node_off); @@ -34,9 +56,14 @@ static void sift_up(binaryheap *heap, int node_off); * of nodes, and with the heap property defined by the given comparator * function, which will be invoked with the additional argument specified by * 'arg'. + * + * If 'indexed' is true, we create a hash table to track each node's + * index in the heap, enabling to perform some operations such as + * binaryheap_remove_node_ptr() etc. */ binaryheap * -binaryheap_allocate(int num_nodes, binaryheap_comparator compare, void *arg) +binaryheap_allocate(int num_nodes, binaryheap_comparator compare, + bool indexed, void *arg) { binaryheap *heap; @@ -48,6 +75,17 @@ binaryheap_allocate(int num_nodes, binaryheap_comparator compare, void *arg) heap->bh_size = 0; heap->bh_has_heap_property = true; heap->bh_nodes = (bh_node_type *) palloc(sizeof(bh_node_type) * num_nodes); + heap->bh_nodeidx = NULL; + + if (indexed) + { +#ifdef FRONTEND + heap->bh_nodeidx = bh_nodeidx_create(num_nodes, NULL); +#else + heap->bh_nodeidx = bh_nodeidx_create(CurrentMemoryContext, num_nodes, + NULL); +#endif + } return heap; } @@ -63,6 +101,9 @@ binaryheap_reset(binaryheap *heap) { heap->bh_size = 0; heap->bh_has_heap_property = true; + + if (binaryheap_indexed(heap)) + bh_nodeidx_reset(heap->bh_nodeidx); } /* @@ -73,6 +114,9 @@ binaryheap_reset(binaryheap *heap) void binaryheap_free(binaryheap *heap) { + if (binaryheap_indexed(heap)) + bh_nodeidx_destroy(heap->bh_nodeidx); + pfree(heap->bh_nodes); pfree(heap); } @@ -116,6 +160,67 @@ enlarge_node_array(binaryheap *heap) } /* + * Set the given node at the 'index' and track it if required. + * + * Return true if the node's index is already tracked. + */ +static bool +set_node(binaryheap *heap, bh_node_type node, int index) +{ + bool found = false; + + /* Set the node to the nodes array */ + heap->bh_nodes[index] = node; + + if (binaryheap_indexed(heap)) + { + bh_nodeidx_entry *ent; + + /* Keep track of the node index */ + ent = bh_nodeidx_insert(heap->bh_nodeidx, node, &found); + ent->index = index; + } + + return found; +} + +/* + * Remove the node's index from the hash table if the heap is indexed. + */ +static inline void +delete_nodeidx(binaryheap *heap, bh_node_type node) +{ + if (binaryheap_indexed(heap)) + bh_nodeidx_delete(heap->bh_nodeidx, node); +} + +/* + * Replace the existing node at 'idx' with the given 'new_node'. Also + * update their positions accordingly. Note that we assume the new_node's + * position is already tracked if enabled, i.e. the new_node is already + * present in the heap. + */ +static void +replace_node(binaryheap *heap, int index, bh_node_type new_node) +{ + bool found PG_USED_FOR_ASSERTS_ONLY; + + /* Quick return if not necessary to move */ + if (heap->bh_nodes[index] == new_node) + return; + + /* Remove the overwritten node's index */ + delete_nodeidx(heap, heap->bh_nodes[index]); + + /* + * Replace it with the given new node. This node's position must also be + * tracked as we assume to replace the node with the existing node. + */ + found = set_node(heap, new_node, index); + Assert(!binaryheap_indexed(heap) || found); +} + +/* * binaryheap_add_unordered * * Adds the given datum to the end of the heap's list of nodes in O(1) without @@ -131,7 +236,7 @@ binaryheap_add_unordered(binaryheap *heap, bh_node_type d) enlarge_node_array(heap); heap->bh_has_heap_property = false; - heap->bh_nodes[heap->bh_size] = d; + set_node(heap, d, heap->bh_size); heap->bh_size++; } @@ -164,7 +269,7 @@ binaryheap_add(binaryheap *heap, bh_node_type d) if (heap->bh_size >= heap->bh_space) enlarge_node_array(heap); - heap->bh_nodes[heap->bh_size] = d; + set_node(heap, d, heap->bh_size); heap->bh_size++; sift_up(heap, heap->bh_size - 1); } @@ -205,6 +310,8 @@ binaryheap_remove_first(binaryheap *heap) if (heap->bh_size == 1) { heap->bh_size--; + delete_nodeidx(heap, result); + return result; } @@ -212,7 +319,7 @@ binaryheap_remove_first(binaryheap *heap) * Remove the last node, placing it in the vacated root entry, and sift * the new root node down to its correct position. */ - heap->bh_nodes[0] = heap->bh_nodes[--heap->bh_size]; + replace_node(heap, 0, heap->bh_nodes[--heap->bh_size]); sift_down(heap, 0); return result; @@ -238,7 +345,7 @@ binaryheap_remove_node(binaryheap *heap, int n) heap->bh_arg); /* remove the last node, placing it in the vacated entry */ - heap->bh_nodes[n] = heap->bh_nodes[heap->bh_size]; + replace_node(heap, n, heap->bh_nodes[heap->bh_size]); /* sift as needed to preserve the heap property */ if (cmp > 0) @@ -248,6 +355,77 @@ binaryheap_remove_node(binaryheap *heap, int n) } /* + * binaryheap_remove_node_ptr + * + * Similar to binaryheap_remove_node() but removes the given node. The caller + * must ensure that the given node is in the heap. O(log n) worst case. + * + * This function can be used only if the heap is indexed. + */ +void +binaryheap_remove_node_ptr(binaryheap *heap, bh_node_type d) +{ + bh_nodeidx_entry *ent; + + Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property); + Assert(binaryheap_indexed(heap)); + + ent = bh_nodeidx_lookup(heap->bh_nodeidx, d); + Assert(ent); + + binaryheap_remove_node(heap, ent->index); +} + +/* + * Workhorse for binaryheap_update_up and binaryheap_update_down. + */ +static void +resift_node(binaryheap *heap, bh_node_type node, bool sift_dir_up) +{ + bh_nodeidx_entry *ent; + + Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property); + Assert(binaryheap_indexed(heap)); + + ent = bh_nodeidx_lookup(heap->bh_nodeidx, node); + Assert(ent); + Assert(ent->index >= 0 && ent->index < heap->bh_size); + + if (sift_dir_up) + sift_up(heap, ent->index); + else + sift_down(heap, ent->index); +} + +/* + * binaryheap_update_up + * + * Sift the given node up after the node's key is updated. The caller must + * ensure that the given node is in the heap. O(log n) worst case. + * + * This function can be used only if the heap is indexed. + */ +void +binaryheap_update_up(binaryheap *heap, bh_node_type d) +{ + resift_node(heap, d, true); +} + +/* + * binaryheap_update_down + * + * Sift the given node down after the node's key is updated. The caller must + * ensure that the given node is in the heap. O(log n) worst case. + * + * This function can be used only if the heap is indexed. + */ +void +binaryheap_update_down(binaryheap *heap, bh_node_type d) +{ + resift_node(heap, d, false); +} + +/* * binaryheap_replace_first * * Replace the topmost element of a non-empty heap, preserving the heap @@ -259,7 +437,7 @@ binaryheap_replace_first(binaryheap *heap, bh_node_type d) { Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property); - heap->bh_nodes[0] = d; + replace_node(heap, 0, d); if (heap->bh_size > 1) sift_down(heap, 0); @@ -301,11 +479,11 @@ sift_up(binaryheap *heap, int node_off) * Otherwise, swap the parent value with the hole, and go on to check * the node's new parent. */ - heap->bh_nodes[node_off] = parent_val; + set_node(heap, parent_val, node_off); node_off = parent_off; } /* Re-fill the hole */ - heap->bh_nodes[node_off] = node_val; + set_node(heap, node_val, node_off); } /* @@ -360,9 +538,9 @@ sift_down(binaryheap *heap, int node_off) * Otherwise, swap the hole with the child that violates the heap * property; then go on to check its children. */ - heap->bh_nodes[node_off] = heap->bh_nodes[swap_off]; + set_node(heap, heap->bh_nodes[swap_off], node_off); node_off = swap_off; } /* Re-fill the hole */ - heap->bh_nodes[node_off] = node_val; + set_node(heap, node_val, node_off); } |