diff --git a/src/backend/lib/Makefile b/src/backend/lib/Makefile index 98ce3d7..c2bc5ba 100644 --- a/src/backend/lib/Makefile +++ b/src/backend/lib/Makefile @@ -12,6 +12,7 @@ subdir = src/backend/lib top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -OBJS = ilist.o stringinfo.o + +OBJS = ilist.o simpleheap.o stringinfo.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/lib/simpleheap.c b/src/backend/lib/simpleheap.c new file mode 100644 index 0000000..825d0a8 --- /dev/null +++ b/src/backend/lib/simpleheap.c @@ -0,0 +1,255 @@ +/*------------------------------------------------------------------------- + * + * simpleheap.c + * A simple binary heap implementaion + * + * Portions Copyright (c) 2012, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/lib/simpleheap.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include + +#include "lib/simpleheap.h" + +static inline int +simpleheap_left_off(size_t i) +{ + return 2 * i + 1; +} + +static inline int +simpleheap_right_off(size_t i) +{ + return 2 * i + 2; +} + +static inline int +simpleheap_parent_off(size_t i) +{ + return floor((i - 1) / 2); +} + +/* sift up */ +static void +simpleheap_sift_up(simpleheap *heap, size_t node_off); + +/* sift down */ +static void +simpleheap_sift_down(simpleheap *heap, size_t node_off); + +static inline void +simpleheap_swap(simpleheap *heap, size_t a, size_t b) +{ + simpleheap_kv swap; + swap.value = heap->values[a].value; + swap.key = heap->values[a].key; + + heap->values[a].value = heap->values[b].value; + heap->values[a].key = heap->values[b].key; + + heap->values[b].key = swap.key; + heap->values[b].value = swap.value; +} + +/* sift down */ +static void +simpleheap_sift_down(simpleheap *heap, size_t node_off) +{ + /* manually unrolled tail recursion */ + while (true) + { + size_t left_off = simpleheap_left_off(node_off); + size_t right_off = simpleheap_right_off(node_off); + size_t swap_off = 0; + + /* only one child can violate the heap property after a change */ + + /* check left child */ + if (left_off < heap->size && + heap->compare(&heap->values[left_off], + &heap->values[node_off]) < 0) + { + /* heap condition violated */ + swap_off = left_off; + } + + /* check right child */ + if (right_off < heap->size && + heap->compare(&heap->values[right_off], + &heap->values[node_off]) < 0) + { + /* heap condition violated */ + + /* swap with the smaller child */ + if (!swap_off || + heap->compare(&heap->values[right_off], + &heap->values[left_off]) < 0) + { + swap_off = right_off; + } + } + + if (!swap_off) + { + /* heap condition fullfilled, abort */ + break; + } + + /* swap node with the child violating the property */ + simpleheap_swap(heap, swap_off, node_off); + + /* recurse, check child subtree */ + node_off = swap_off; + } +} + +/* sift up */ +static void +simpleheap_sift_up(simpleheap *heap, size_t node_off) +{ + /* manually unrolled tail recursion */ + while (true) + { + size_t parent_off = simpleheap_parent_off(node_off); + + if (heap->compare(&heap->values[parent_off], + &heap->values[node_off]) < 0) + { + /* heap property violated */ + simpleheap_swap(heap, node_off, parent_off); + + /* recurse */ + node_off = parent_off; + } + else + break; + } +} + +simpleheap* +simpleheap_allocate(size_t allocate) +{ + simpleheap* heap = palloc(sizeof(simpleheap)); + heap->values = palloc(sizeof(simpleheap_kv) * allocate); + heap->size = 0; + heap->space = allocate; + return heap; +} + +void +simpleheap_free(simpleheap* heap) +{ + pfree(heap->values); + pfree(heap); +} + +/* initial building of a heap */ +void +simpleheap_build(simpleheap *heap) +{ + int i; + + for (i = simpleheap_parent_off(heap->size - 1); i >= 0; i--) + { + simpleheap_sift_down(heap, i); + } +} + +/* + * Change the + */ +void +simpleheap_change_key(simpleheap *heap, void* key) +{ + size_t next_off = 0; + int ret; + simpleheap_kv* kv; + + heap->values[0].key = key; + + /* no need to do anything if there is only one element */ + if (heap->size == 1) + { + return; + } + else if (heap->size == 2) + { + next_off = 1; + } + else + { + ret = heap->compare( + &heap->values[simpleheap_left_off(0)], + &heap->values[simpleheap_right_off(0)]); + + if (ret == -1) + next_off = simpleheap_left_off(0); + else + next_off = simpleheap_right_off(0); + } + + /* + * compare with the next key. If were still smaller we can skip + * restructuring heap + */ + ret = heap->compare( + &heap->values[0], + &heap->values[next_off]); + + if (ret == -1) + return; + + kv = simpleheap_remove_first(heap); + simpleheap_add(heap, kv->key, kv->value); +} + +void +simpleheap_add_unordered(simpleheap* heap, void *key, void *value) +{ + if (heap->size + 1 == heap->space) + Assert("Cannot resize heaps"); + heap->values[heap->size].key = key; + heap->values[heap->size++].value = value; +} + +void +simpleheap_add(simpleheap* heap, void *key, void *value) +{ + simpleheap_add_unordered(heap, key, value); + simpleheap_sift_up(heap, heap->size - 1); +} + +simpleheap_kv* +simpleheap_first(simpleheap* heap) +{ + if (!heap->size) + Assert("heap is empty"); + return &heap->values[0]; +} + + +simpleheap_kv* +simpleheap_remove_first(simpleheap* heap) +{ + if (heap->size == 0) + Assert("heap is empty"); + + if (heap->size == 1) + { + heap->size--; + return &heap->values[0]; + } + + simpleheap_swap(heap, 0, heap->size - 1); + simpleheap_sift_down(heap, 0); + + heap->size--; + return &heap->values[heap->size]; +} diff --git a/src/include/lib/simpleheap.h b/src/include/lib/simpleheap.h new file mode 100644 index 0000000..ab2d2ea --- /dev/null +++ b/src/include/lib/simpleheap.h @@ -0,0 +1,91 @@ +/* + * simpleheap.h + * + * A simple binary heap implementation + * + * Portions Copyright (c) 2012, PostgreSQL Global Development Group + * + * src/include/lib/simpleheap.h + */ + +#ifndef SIMPLEHEAP_H +#define SIMPLEHEAP_H + +typedef struct simpleheap_kv +{ + void* key; + void* value; +} simpleheap_kv; + +typedef struct simpleheap +{ + size_t size; + size_t space; + /* + * Has to return: + * -1 iff a < b + * 0 iff a == b + * +1 iff a > b + */ + int (*compare)(simpleheap_kv* a, simpleheap_kv* b); + + simpleheap_kv *values; +} simpleheap; + +simpleheap* +simpleheap_allocate(size_t capacity); + +void +simpleheap_free(simpleheap* heap); + +/* + * Add values without enforcing the heap property. + * + * simpleheap_build has to be called before relying on anything that needs a + * valid heap. This is mostly useful for initially filling a heap and staying + * in O(n) instead of O(n log n). + */ +void +simpleheap_add_unordered(simpleheap* heap, void *key, void *value); + +/* + * Insert key/value pair + * + * O(log n) + */ +void +simpleheap_add(simpleheap* heap, void *key, void *value); + +/* + * Returns the first element as indicated by comparisons of the ->compare() + * operator + * + * O(1) + */ +simpleheap_kv* +simpleheap_first(simpleheap* heap); + +/* + * Returns and removes the first element as indicated by comparisons of the + * ->compare() operator + * + * O(log n) + */ +simpleheap_kv* +simpleheap_remove_first(simpleheap* heap); + +void +simpleheap_change_key(simpleheap *heap, void* newkey); + + +/* + * make the heap fullfill the heap condition. Only needed if elements were + * added with simpleheap_add_unordered() + * + * O(n) + */ +void +simpleheap_build(simpleheap *heap); + + +#endif //SIMPLEHEAP_H