mirror of
https://github.com/jrcutler/threadless.io.git
synced 2024-07-07 10:35:49 +00:00
Add binary heap implementation and test
This commit is contained in:
parent
aa8cc3f804
commit
ecf9cc606c
@ -24,6 +24,9 @@ target_link_libraries(coroutine LINK_PUBLIC allocation)
|
|||||||
add_library(default_allocator src/default_allocator.c)
|
add_library(default_allocator src/default_allocator.c)
|
||||||
set(ALLOCATORS default_allocator)
|
set(ALLOCATORS default_allocator)
|
||||||
|
|
||||||
|
add_library(heap src/heap.c)
|
||||||
|
target_link_libraries(heap LINK_PUBLIC allocation)
|
||||||
|
|
||||||
if(HAVE_MMAP)
|
if(HAVE_MMAP)
|
||||||
add_library(mmap_allocator src/mmap_allocator.c)
|
add_library(mmap_allocator src/mmap_allocator.c)
|
||||||
set(ALLOCATORS ${ALLOCATORS} mmap_allocator)
|
set(ALLOCATORS ${ALLOCATORS} mmap_allocator)
|
||||||
@ -33,3 +36,5 @@ add_executable(test-allocation test/allocation.c)
|
|||||||
target_link_libraries(test-allocation LINK_PUBLIC allocation ${ALLOCATORS})
|
target_link_libraries(test-allocation LINK_PUBLIC allocation ${ALLOCATORS})
|
||||||
add_executable(test-coroutine test/coroutine.c)
|
add_executable(test-coroutine test/coroutine.c)
|
||||||
target_link_libraries(test-coroutine LINK_PUBLIC coroutine ${ALLOCATORS})
|
target_link_libraries(test-coroutine LINK_PUBLIC coroutine ${ALLOCATORS})
|
||||||
|
add_executable(test-heap test/heap.c)
|
||||||
|
target_link_libraries(test-heap LINK_PUBLIC heap ${ALLOCATORS})
|
||||||
|
134
src/heap.c
Normal file
134
src/heap.c
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/* threadless.io
|
||||||
|
* Copyright (c) 2016 Justin R. Cutler
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for
|
||||||
|
* full license information.
|
||||||
|
*/
|
||||||
|
/** @file
|
||||||
|
* heap interface implementation
|
||||||
|
* @author Justin R. Cutler <justin.r.cutler@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* size_t, NULL */
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* allocation_realloc_array */
|
||||||
|
#include <threadless/allocation.h>
|
||||||
|
/* ... */
|
||||||
|
#include <threadless/heap.h>
|
||||||
|
|
||||||
|
|
||||||
|
static inline void swap(heap_node_t **storage, size_t a, size_t b)
|
||||||
|
{
|
||||||
|
/* swap elements */
|
||||||
|
heap_node_t *tmp = storage[a];
|
||||||
|
storage[a] = storage[b];
|
||||||
|
storage[b] = tmp;
|
||||||
|
/* update back references */
|
||||||
|
storage[a]->index = a;
|
||||||
|
storage[b]->index = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void sift_down(const heap_t *heap, size_t start, size_t pos)
|
||||||
|
{
|
||||||
|
heap_node_t **storage = heap->allocation.memory;
|
||||||
|
while (pos > start) {
|
||||||
|
/* if node pos is "better" than parent, swap them */
|
||||||
|
size_t parent = (pos - 1) >> 1;
|
||||||
|
if (heap->compare(storage[pos], storage[parent]) < 0) {
|
||||||
|
swap(storage, pos, parent);
|
||||||
|
pos = parent;
|
||||||
|
} else {
|
||||||
|
/* node pos is in the correct location */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void sift_up(const heap_t *heap, size_t pos, size_t end)
|
||||||
|
{
|
||||||
|
heap_node_t **storage = heap->allocation.memory;
|
||||||
|
size_t start = pos;
|
||||||
|
size_t child = (pos << 1) + 1;
|
||||||
|
/* bubble smaller child up until hitting a leaf */
|
||||||
|
while (child < end) {
|
||||||
|
size_t right = child + 1;
|
||||||
|
if (right < end && heap->compare(storage[right], storage[child]) < 0) {
|
||||||
|
child = right;
|
||||||
|
}
|
||||||
|
/* move smaller child up */
|
||||||
|
swap(storage, pos, child);
|
||||||
|
pos = child;
|
||||||
|
child = (pos << 1) + 1;
|
||||||
|
}
|
||||||
|
/* bubble node originally at pos into place */
|
||||||
|
sift_down(heap, start, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int heap_push(heap_t *heap, heap_node_t *node)
|
||||||
|
{
|
||||||
|
int error = allocation_realloc_array(&(heap->allocation), heap->count + 1,
|
||||||
|
sizeof(heap_node_t *));
|
||||||
|
if (!error) {
|
||||||
|
heap_node_t **storage = heap->allocation.memory;
|
||||||
|
/* place item at end of heap */
|
||||||
|
node->heap = heap;
|
||||||
|
node->index = heap->count++;
|
||||||
|
storage[node->index] = node;
|
||||||
|
/* bubble item into place */
|
||||||
|
sift_down(heap, 0, node->index);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void heap_remove(heap_node_t *node)
|
||||||
|
{
|
||||||
|
heap_t *heap = node->heap;
|
||||||
|
size_t pos = node->index;
|
||||||
|
|
||||||
|
if (heap->count) {
|
||||||
|
/* shrink heap */
|
||||||
|
heap->count--;
|
||||||
|
if (pos != heap->count) {
|
||||||
|
/* exchange previous last element for removed element */
|
||||||
|
swap(heap->allocation.memory, pos, heap->count);
|
||||||
|
/* shrink storage */
|
||||||
|
(void) allocation_realloc_array(&(heap->allocation), heap->count,
|
||||||
|
sizeof(heap_node_t *));
|
||||||
|
/* restore heap invariant */
|
||||||
|
sift_up(heap, pos, heap->count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disassociate node from heap */
|
||||||
|
node->heap = NULL;
|
||||||
|
node->index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
heap_node_t *heap_pop(heap_t *heap)
|
||||||
|
{
|
||||||
|
heap_node_t *node = heap_peek(heap);
|
||||||
|
|
||||||
|
if (NULL != node) {
|
||||||
|
heap_remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void heap_fini(heap_t *heap)
|
||||||
|
{
|
||||||
|
/* diassociate all remaining nodes from heap */
|
||||||
|
heap_node_t **storage = heap->allocation.memory;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < heap->count; ++i) {
|
||||||
|
storage[i]->heap = NULL;
|
||||||
|
storage[i]->index = 0;
|
||||||
|
}
|
||||||
|
allocation_free(&(heap->allocation));
|
||||||
|
}
|
117
test/heap.c
Normal file
117
test/heap.c
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/* threadless.io
|
||||||
|
* Copyright (c) 2016 Justin R. Cutler
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for
|
||||||
|
* full license information.
|
||||||
|
*/
|
||||||
|
/** @file
|
||||||
|
* heap interface test
|
||||||
|
* @author Justin R. Cutler <justin.r.cutler@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* HAVE_* */
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/* errno, EINVAL */
|
||||||
|
#include <errno.h>
|
||||||
|
/* printf, perror */
|
||||||
|
#include <stdio.h>
|
||||||
|
/* EXIT_SUCCESS, EXIT_FAILURE */
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* container_of */
|
||||||
|
#include <threadless/container_of.h>
|
||||||
|
/* default_allocator_get */
|
||||||
|
#include <threadless/default_allocator.h>
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
/* mmap_allocator_get */
|
||||||
|
# include <threadless/mmap_allocator.h>
|
||||||
|
#endif
|
||||||
|
/* ... */
|
||||||
|
#include <threadless/heap.h>
|
||||||
|
|
||||||
|
|
||||||
|
const int data[] = { 4 /* duplicate */, 1, 9, 2, 8, 4, 0, 5, 3, 6, 7, 99 };
|
||||||
|
const size_t data_count = sizeof(data) / sizeof(data[0]);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
heap_node_t node;
|
||||||
|
int value;
|
||||||
|
} value_t;
|
||||||
|
|
||||||
|
|
||||||
|
static int min_compare(const heap_node_t *a, const heap_node_t *b)
|
||||||
|
{
|
||||||
|
int va = container_of(a, const value_t, node)->value;
|
||||||
|
int vb = container_of(b, const value_t, node)->value;
|
||||||
|
return va - vb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int run(allocator_t *allocator)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
allocation_t alloc;
|
||||||
|
value_t *values = NULL;
|
||||||
|
heap_t heap;
|
||||||
|
size_t i;
|
||||||
|
heap_node_t *node;
|
||||||
|
|
||||||
|
allocation_init(&alloc, allocator);
|
||||||
|
error = allocation_realloc_array(&alloc, data_count, sizeof(*values));
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
values = alloc.memory;
|
||||||
|
|
||||||
|
heap_init(&heap, allocator, min_compare);
|
||||||
|
|
||||||
|
/* push values into heap */
|
||||||
|
for (i = 0; !error && i < data_count; ++i) {
|
||||||
|
values[i].value = data[i];
|
||||||
|
error = heap_push(&heap, &(values[i].node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove from middle */
|
||||||
|
heap_remove(&(values[0].node));
|
||||||
|
/* remove from end */
|
||||||
|
heap_remove(&(values[data_count - 1].node));
|
||||||
|
|
||||||
|
/* pull values out of heap (minimum first) */
|
||||||
|
while (NULL != (node = heap_pop(&heap))) {
|
||||||
|
value_t *value = container_of(node, value_t, node);
|
||||||
|
printf("%i\n", value->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_fini(&heap);
|
||||||
|
|
||||||
|
allocation_free(&alloc);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
allocator_t *allocator;
|
||||||
|
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
|
||||||
|
printf("default allocator:\n");
|
||||||
|
allocator = default_allocator_get();
|
||||||
|
error = run(allocator);
|
||||||
|
allocator_destroy(allocator);
|
||||||
|
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
if (!error) {
|
||||||
|
printf("mmap allocator:\n");
|
||||||
|
allocator = mmap_allocator_get();
|
||||||
|
error = run(allocator);
|
||||||
|
allocator_destroy(allocator);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return !error ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
112
threadless/heap.h
Normal file
112
threadless/heap.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/* threadless.io
|
||||||
|
* Copyright (c) 2016 Justin R. Cutler
|
||||||
|
* Licensed under the MIT License. See LICENSE file in the project root for
|
||||||
|
* full license information.
|
||||||
|
*/
|
||||||
|
/** @file
|
||||||
|
* heap interface definition
|
||||||
|
* @author Justin R. Cutler <justin.r.cutler@gmail.com>
|
||||||
|
*/
|
||||||
|
#ifndef THREADLESS_HEAP_H
|
||||||
|
#define THREADLESS_HEAP_H
|
||||||
|
|
||||||
|
/* size_t, NULL */
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* allocation_t, allocator_t, allocation_init */
|
||||||
|
#include <threadless/allocation.h>
|
||||||
|
|
||||||
|
/** Heap descriptor type */
|
||||||
|
typedef struct heap heap_t;
|
||||||
|
|
||||||
|
/** Heap node type */
|
||||||
|
typedef struct {
|
||||||
|
/** Back-pointer to containing heap */
|
||||||
|
heap_t *heap;
|
||||||
|
/** Current index within @p heap */
|
||||||
|
size_t index;
|
||||||
|
} heap_node_t;
|
||||||
|
|
||||||
|
/** Heap node comparison function type
|
||||||
|
* @param[in] a first node
|
||||||
|
* @param[in] b second node
|
||||||
|
* @retval >0 @p a is "better" than @p b
|
||||||
|
* @retval 0 @p a is equivalent to @p b
|
||||||
|
* @retval <0 @p a is "worse" than @p b
|
||||||
|
*/
|
||||||
|
typedef int (heap_compare_function_t)(const heap_node_t *a,
|
||||||
|
const heap_node_t *b);
|
||||||
|
|
||||||
|
/** Heap descriptor structure */
|
||||||
|
struct heap {
|
||||||
|
/** Heap node pointer storage */
|
||||||
|
allocation_t allocation;
|
||||||
|
/** Number of nodes in heap */
|
||||||
|
size_t count;
|
||||||
|
/** Heap node comparison function */
|
||||||
|
heap_compare_function_t *compare;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/** Initialize a heap descriptor
|
||||||
|
* @param[out] heap heap to initialize
|
||||||
|
* @param allocator allocator instance
|
||||||
|
* @param compare comparison function
|
||||||
|
* @post @p heap represents an empty heap with given @p compare function
|
||||||
|
*/
|
||||||
|
static inline void heap_init(heap_t *heap, allocator_t *allocator,
|
||||||
|
heap_compare_function_t *compare)
|
||||||
|
{
|
||||||
|
allocation_init(&heap->allocation, allocator);
|
||||||
|
heap->count = 0;
|
||||||
|
heap->compare = compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Peek at the "best" node in @p heap
|
||||||
|
* @param[in] heap heap
|
||||||
|
* @retval NULL @p heap is empty
|
||||||
|
* @retval non-NULL pointer to "best" node in @p heap
|
||||||
|
*/
|
||||||
|
static inline heap_node_t *heap_peek(const heap_t *heap)
|
||||||
|
{
|
||||||
|
heap_node_t **storage = heap->count ? heap->allocation.memory : NULL;
|
||||||
|
return storage ? *storage : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Push a @p node into @p heap
|
||||||
|
* @param[in,out] heap heap
|
||||||
|
* @param[in,out] node node
|
||||||
|
* @retval 0 success
|
||||||
|
* @retval -1 error
|
||||||
|
*/
|
||||||
|
int heap_push(heap_t *heap, heap_node_t *node);
|
||||||
|
|
||||||
|
/** Remove an arbitrary @p node from its heap
|
||||||
|
* @param[in,out] node node
|
||||||
|
* @pre @p node must be in a heap
|
||||||
|
* @post @p node is no longer in a heap
|
||||||
|
*/
|
||||||
|
void heap_remove(heap_node_t *node);
|
||||||
|
|
||||||
|
/** Remove and return the "best" node in @p heap
|
||||||
|
* @param[in,out] heap heap
|
||||||
|
* @retval NULL @p heap was empty
|
||||||
|
* @retval non-NULL pointer to removed "best" node from @p heap
|
||||||
|
*/
|
||||||
|
heap_node_t *heap_pop(heap_t *heap);
|
||||||
|
|
||||||
|
/** Finalize a heap
|
||||||
|
* @param[in,out] heap heap
|
||||||
|
* @post All nodes have been removed from @p heap and all backing memory has
|
||||||
|
* been released
|
||||||
|
*/
|
||||||
|
void heap_fini(heap_t *heap);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* THREADLESS_HEAP_H */
|
Loading…
x
Reference in New Issue
Block a user