Browse Source
Introduce a binary min-heap implementation as a generic data structure for use in kernel and application code. A min-heap always maintains the smallest element at the root, making insertion and removal of the minimum element efficient (O(log n)). The API allows both static and dynamic initialization, supports custom comparators. Signed-off-by: Sayooj K Karun <sayooj@aerlync.com>pull/91334/head
6 changed files with 465 additions and 0 deletions
@ -0,0 +1,249 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Aerlync Labs Inc. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_SYS_MIN_HEAP_H_ |
||||||
|
#define ZEPHYR_INCLUDE_SYS_MIN_HEAP_H_ |
||||||
|
|
||||||
|
#include <zephyr/sys/util.h> |
||||||
|
#include <zephyr/kernel.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief min_heap |
||||||
|
* @defgroup min_heap_apis Min-Heap service |
||||||
|
* @ingroup datastructure_apis |
||||||
|
* @{ |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Comparator function type for min-heap ordering. |
||||||
|
* |
||||||
|
* This function compares two heap nodes to establish their relative order. |
||||||
|
* It must be implemented by the user and provided at min-heap |
||||||
|
* initialization. |
||||||
|
* |
||||||
|
* @param a First user defined data pointer for comparison. |
||||||
|
* @param b Second user defined data pointer for comparison. |
||||||
|
* |
||||||
|
* @return Negative value if @p a is less than @p b, |
||||||
|
* positive value if @p a is greater than @p b, |
||||||
|
* zero if they are equal. |
||||||
|
*/ |
||||||
|
typedef int (*min_heap_cmp_t)(const void *a, |
||||||
|
const void *b); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Equality function for finding a node in the heap. |
||||||
|
* |
||||||
|
* @param node Pointer to a user defined data. |
||||||
|
* @param other Pointer to a user-defined key or structure to compare with. |
||||||
|
* |
||||||
|
* @return true if the node matches the search criteria, false otherwise. |
||||||
|
*/ |
||||||
|
typedef bool (*min_heap_eq_t)(const void *node, |
||||||
|
const void *other); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief min-heap data structure with user-provided comparator. |
||||||
|
*/ |
||||||
|
struct min_heap { |
||||||
|
/** Raw pointer to contiguous memory for elements */ |
||||||
|
void *storage; |
||||||
|
/** Maximum number of elements */ |
||||||
|
size_t capacity; |
||||||
|
/** Size of each element*/ |
||||||
|
size_t elem_size; |
||||||
|
/** Current elements count */ |
||||||
|
size_t size; |
||||||
|
/** Comparator function */ |
||||||
|
min_heap_cmp_t cmp; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Define and initialize a heap instance at runtime. |
||||||
|
* |
||||||
|
* @param name Name of the heap variable. |
||||||
|
* @param storage Pointer to the preallocated storage. |
||||||
|
* @param cap Capacity (number of elements). |
||||||
|
* @param size Size of each element. |
||||||
|
* @param cmp_func Comparator function for the heap. |
||||||
|
*/ |
||||||
|
#define MIN_HEAP_DEFINE(name, storage, cap, size, cmp_func) \ |
||||||
|
struct min_heap name; \ |
||||||
|
min_heap_init(&name, storage, cap, size, cmp_func) |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Define a statically allocated and aligned min-heap instance. |
||||||
|
* |
||||||
|
* @param name Base name for the heap instance. |
||||||
|
* @param cap Capacity (number of elements). |
||||||
|
* @param elem_sz Size in bytes of each element. |
||||||
|
* @param align Required alignment of each element. |
||||||
|
* @param cmp_func Comparator function used by the heap |
||||||
|
*/ |
||||||
|
#define MIN_HEAP_DEFINE_STATIC(name, cap, elem_sz, align, cmp_func) \ |
||||||
|
static uint8_t name##_storage[(cap) * (elem_sz)] __aligned(align); \ |
||||||
|
static struct min_heap name = { \ |
||||||
|
.storage = name##_storage, \ |
||||||
|
.capacity = (cap), \ |
||||||
|
.elem_size = (elem_sz), \ |
||||||
|
.size = 0, \ |
||||||
|
.cmp = (cmp_func) \ |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a min-heap instance at runtime. |
||||||
|
* |
||||||
|
* Sets up the internal structure of a min heap using a user-provided |
||||||
|
* memory block, capacity, and comparator function. This function must |
||||||
|
* be called before using the heap if not statically defined. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap structure. |
||||||
|
* @param storage Pointer to memory block for storing elements. |
||||||
|
* @param cap Maximum number of elements the heap can store. |
||||||
|
* @param elem_size Size in bytes of each element. |
||||||
|
* @param cmp Comparator function used to order the heap elements. |
||||||
|
* |
||||||
|
* @note All arguments must be valid. This function does not allocate memory. |
||||||
|
* |
||||||
|
*/ |
||||||
|
void min_heap_init(struct min_heap *heap, void *storage, size_t cap, |
||||||
|
size_t elem_size, min_heap_cmp_t cmp); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push an element into the min-heap. |
||||||
|
* |
||||||
|
* Adds a new element to the min-heap and restores the heap order by moving it |
||||||
|
* upward as necessary. Insert operation will fail if the min-heap |
||||||
|
* has reached full capacity. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap. |
||||||
|
* @param item Pointer to the item to insert. |
||||||
|
* |
||||||
|
* @return 0 on Success, -ENOMEM if the heap is full. |
||||||
|
*/ |
||||||
|
int min_heap_push(struct min_heap *heap, const void *item); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Peek at the top element of the min-heap. |
||||||
|
* |
||||||
|
* The function will not remove the element from the min-heap. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap. |
||||||
|
* |
||||||
|
* @return Pointer to the top priority element, or NULL if the heap is empty. |
||||||
|
*/ |
||||||
|
void *min_heap_peek(const struct min_heap *heap); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a specific element from the min-heap. |
||||||
|
* |
||||||
|
* Removes the specified node from the min-heap based on the ID it stores |
||||||
|
* internally. The min-heap is rebalanced after removal to ensure |
||||||
|
* proper ordering. |
||||||
|
* The caller gains ownership of the returned element and is responsible for |
||||||
|
* any further management of its memory or reuse. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap. |
||||||
|
* @param id element ID to be removed. |
||||||
|
* @param out_buf User-provided buffer where the removed element will be copied. |
||||||
|
* |
||||||
|
* @return true in success, false otherwise. |
||||||
|
*/ |
||||||
|
bool min_heap_remove(struct min_heap *heap, size_t id, void *out_buf); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the min heap is empty. |
||||||
|
* |
||||||
|
* This function checks whether the heap contains any elements. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min heap. |
||||||
|
* |
||||||
|
* @return true if heap is empty, false otherwise. |
||||||
|
*/ |
||||||
|
static inline bool min_heap_is_empty(struct min_heap *heap) |
||||||
|
{ |
||||||
|
__ASSERT_NO_MSG(heap != NULL); |
||||||
|
|
||||||
|
return (heap->size == 0); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove and return the highest priority element in the heap. |
||||||
|
* |
||||||
|
* The caller gains ownership of the returned element and is responsible for |
||||||
|
* any further management of its memory or reuse. The min-heap is rebalanced |
||||||
|
* after removal to ensure proper ordering. |
||||||
|
* |
||||||
|
* @param heap Pointer to heap. |
||||||
|
* @param out_buf User-provided buffer where the removed element will be copied. |
||||||
|
* |
||||||
|
* @return true in success, false otherwise. |
||||||
|
*/ |
||||||
|
bool min_heap_pop(struct min_heap *heap, void *out_buf); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Search for a node in the heap matching a condition. |
||||||
|
* |
||||||
|
* @param heap Pointer to the heap structure. |
||||||
|
* @param eq Function used to compare each node with the search target. |
||||||
|
* @param other Pointer to the data used for comparison in the eq function. |
||||||
|
* @param out_id Optional output parameter to store the index of the found node. |
||||||
|
* |
||||||
|
* @return Pointer to the first matching element, or NULL if not found. |
||||||
|
*/ |
||||||
|
void *min_heap_find(struct min_heap *heap, min_heap_eq_t eq, |
||||||
|
const void *other, size_t *out_id); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a pointer to the element at the specified index. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap. |
||||||
|
* @param index Index of the element to retrieve. |
||||||
|
* |
||||||
|
* @return Pointer to the element at the given index. |
||||||
|
*/ |
||||||
|
static inline void *min_heap_get_element(const struct min_heap *heap, |
||||||
|
size_t index) |
||||||
|
{ |
||||||
|
return (void *)((uintptr_t)heap->storage + index * heap->elem_size); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Iterate over each node in the heap. |
||||||
|
* |
||||||
|
* @param heap Pointer to the heap. |
||||||
|
* @param node_var The loop variable used to reference each node. |
||||||
|
* @param body Code block to execute for each node. |
||||||
|
* |
||||||
|
* Example: |
||||||
|
* ``` |
||||||
|
* void *node; |
||||||
|
* MIN_HEAP_FOREACH(&heap, node, { |
||||||
|
* printk("Value: %d\n", node->value); |
||||||
|
* }); |
||||||
|
* ``` |
||||||
|
*/ |
||||||
|
#define MIN_HEAP_FOREACH(heap, node_var, body) \ |
||||||
|
do { for (size_t _i = 0; _i < (heap)->size && \ |
||||||
|
(((node_var) = min_heap_get_element((heap), _i)) || true); \ |
||||||
|
++_i) { \ |
||||||
|
body; \ |
||||||
|
} \ |
||||||
|
} while (0) |
||||||
|
|
||||||
|
/**
|
||||||
|
* @} |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_SYS_MIN_HEAP_H_ */ |
@ -0,0 +1,11 @@ |
|||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
zephyr_interface_library_named(min_heap) |
||||||
|
|
||||||
|
target_include_directories(min_heap INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) |
||||||
|
|
||||||
|
zephyr_library() |
||||||
|
|
||||||
|
zephyr_library_sources(min_heap.c) |
||||||
|
|
||||||
|
zephyr_library_link_libraries(min_heap) |
@ -0,0 +1,22 @@ |
|||||||
|
# Copyright (c) 2025 Aerlync Labs Inc. |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
config MIN_HEAP |
||||||
|
bool "Min-Heap Data Structure" |
||||||
|
help |
||||||
|
Enable support for a generic Min-Heap data structure library. |
||||||
|
|
||||||
|
A Min-Heap is a binary tree-based data structure in which the |
||||||
|
smallest element is always at the root. It supports efficient |
||||||
|
insertion and removal of the minimum element in O(log n) time, |
||||||
|
making it useful for priority queues, schedulers, and timeout |
||||||
|
queues. |
||||||
|
|
||||||
|
This implementation is designed for general-purpose use in both |
||||||
|
kernel and application code. It supports static and dynamic |
||||||
|
initialization and allows for custom comparator functions. |
||||||
|
|
||||||
|
Note: This is unrelated to the kernel's heap memory allocator |
||||||
|
(used for dynamic memory allocation with `k_malloc()` or |
||||||
|
`k_heap_alloc()`). The "heap" in Min-Heap refers to the ordering |
||||||
|
structure, not memory management. |
@ -0,0 +1,178 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Aerlync Labs Inc. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <zephyr/sys/min_heap.h> |
||||||
|
#include <zephyr/logging/log.h> |
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(min_heap); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swap two elements in the heap. |
||||||
|
* |
||||||
|
* This function swaps the contents of two elements at the specified indices |
||||||
|
* within the heap. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap. |
||||||
|
* @param a Index of the first element. |
||||||
|
* @param b Index of the second element. |
||||||
|
*/ |
||||||
|
static void swap(struct min_heap *heap, size_t a, size_t b) |
||||||
|
{ |
||||||
|
uint8_t tmp[heap->elem_size]; |
||||||
|
void *elem_a = min_heap_get_element(heap, a); |
||||||
|
void *elem_b = min_heap_get_element(heap, b); |
||||||
|
|
||||||
|
memcpy(tmp, elem_a, heap->elem_size); |
||||||
|
memcpy(elem_a, elem_b, heap->elem_size); |
||||||
|
memcpy(elem_b, tmp, heap->elem_size); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Restore heap order by moving a node up the tree. |
||||||
|
* |
||||||
|
* Moves the node at the given index upward in the heap until the min-heap |
||||||
|
* property is restored. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap. |
||||||
|
* @param index Index of the node to heapify upwards. |
||||||
|
*/ |
||||||
|
static void heapify_up(struct min_heap *heap, size_t index) |
||||||
|
{ |
||||||
|
size_t parent; |
||||||
|
void *curr, *par; |
||||||
|
|
||||||
|
while (index > 0) { |
||||||
|
parent = (index - 1) / 2; |
||||||
|
curr = min_heap_get_element(heap, index); |
||||||
|
par = min_heap_get_element(heap, parent); |
||||||
|
if (heap->cmp(curr, par) >= 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
swap(heap, index, parent); |
||||||
|
index = parent; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Restore heap order by moving a node down the tree. |
||||||
|
* |
||||||
|
* Moves the node at the specified index downward in the heap until the |
||||||
|
* min-heap property is restored. |
||||||
|
* |
||||||
|
* @param heap Pointer to the min-heap. |
||||||
|
* @param index Index of the node to heapify downward. |
||||||
|
*/ |
||||||
|
|
||||||
|
static void heapify_down(struct min_heap *heap, size_t index) |
||||||
|
{ |
||||||
|
size_t left, right, smallest; |
||||||
|
|
||||||
|
while (true) { |
||||||
|
left = 2 * index + 1; |
||||||
|
right = 2 * index + 2; |
||||||
|
smallest = index; |
||||||
|
|
||||||
|
if (left < heap->size && |
||||||
|
heap->cmp(min_heap_get_element(heap, left), |
||||||
|
min_heap_get_element(heap, smallest)) < 0) { |
||||||
|
smallest = left; |
||||||
|
} |
||||||
|
|
||||||
|
if (right < heap->size && |
||||||
|
heap->cmp(min_heap_get_element(heap, right), |
||||||
|
min_heap_get_element(heap, smallest)) < 0) { |
||||||
|
smallest = right; |
||||||
|
} |
||||||
|
|
||||||
|
if (smallest == index) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
swap(heap, index, smallest); |
||||||
|
index = smallest; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void min_heap_init(struct min_heap *heap, void *storage, size_t cap, |
||||||
|
size_t elem_size, min_heap_cmp_t cmp) |
||||||
|
{ |
||||||
|
heap->storage = storage; |
||||||
|
heap->capacity = cap; |
||||||
|
heap->elem_size = elem_size; |
||||||
|
heap->cmp = cmp; |
||||||
|
heap->size = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void *min_heap_peek(const struct min_heap *heap) |
||||||
|
{ |
||||||
|
if (heap->size == 0) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
return min_heap_get_element(heap, 0); |
||||||
|
} |
||||||
|
|
||||||
|
int min_heap_push(struct min_heap *heap, const void *item) |
||||||
|
{ |
||||||
|
if (heap->size >= heap->capacity) { |
||||||
|
return -ENOMEM; |
||||||
|
} |
||||||
|
|
||||||
|
void *dest = min_heap_get_element(heap, heap->size); |
||||||
|
|
||||||
|
memcpy(dest, item, heap->elem_size); |
||||||
|
heapify_up(heap, heap->size); |
||||||
|
heap->size++; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool min_heap_remove(struct min_heap *heap, size_t id, void *out_buf) |
||||||
|
{ |
||||||
|
if (id >= heap->size) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void *removed = min_heap_get_element(heap, id); |
||||||
|
|
||||||
|
memcpy(out_buf, removed, heap->elem_size); |
||||||
|
heap->size--; |
||||||
|
if (id != heap->size) { |
||||||
|
void *last = min_heap_get_element(heap, heap->size); |
||||||
|
|
||||||
|
memcpy(removed, last, heap->elem_size); |
||||||
|
heapify_down(heap, id); |
||||||
|
heapify_up(heap, id); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool min_heap_pop(struct min_heap *heap, void *out_buf) |
||||||
|
{ |
||||||
|
return min_heap_remove(heap, 0, out_buf); |
||||||
|
} |
||||||
|
|
||||||
|
void *min_heap_find(struct min_heap *heap, min_heap_eq_t eq, |
||||||
|
const void *other, size_t *out_id) |
||||||
|
{ |
||||||
|
void *element; |
||||||
|
|
||||||
|
for (size_t i = 0; i < heap->size; ++i) { |
||||||
|
|
||||||
|
element = min_heap_get_element(heap, i); |
||||||
|
if (eq(element, other)) { |
||||||
|
if (out_id) { |
||||||
|
*out_id = i; |
||||||
|
} |
||||||
|
return element; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
Loading…
Reference in new issue