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:
		| @@ -24,6 +24,9 @@ target_link_libraries(coroutine LINK_PUBLIC allocation) | ||||
| add_library(default_allocator src/default_allocator.c) | ||||
| set(ALLOCATORS default_allocator) | ||||
|  | ||||
| add_library(heap src/heap.c) | ||||
| target_link_libraries(heap LINK_PUBLIC allocation) | ||||
|  | ||||
| if(HAVE_MMAP) | ||||
|     add_library(mmap_allocator src/mmap_allocator.c) | ||||
|     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}) | ||||
| add_executable(test-coroutine test/coroutine.c) | ||||
| 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 */ | ||||
		Reference in New Issue
	
	Block a user