1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef _LINUX_MIN_HEAP_H 3 #define _LINUX_MIN_HEAP_H 4 5 #include <linux/bug.h> 6 #include <linux/string.h> 7 #include <linux/types.h> 8 9 /** 10 * Data structure to hold a min-heap. 11 * @nr: Number of elements currently in the heap. 12 * @size: Maximum number of elements that can be held in current storage. 13 * @data: Pointer to the start of array holding the heap elements. 14 * @preallocated: Start of the static preallocated array holding the heap elements. 15 */ 16 #define MIN_HEAP_PREALLOCATED(_type, _name, _nr) \ 17 struct _name { \ 18 int nr; \ 19 int size; \ 20 _type *data; \ 21 _type preallocated[_nr]; \ 22 } 23 24 #define DEFINE_MIN_HEAP(_type, _name) MIN_HEAP_PREALLOCATED(_type, _name, 0) 25 26 typedef DEFINE_MIN_HEAP(char, min_heap_char) min_heap_char; 27 28 #define __minheap_cast(_heap) (typeof((_heap)->data[0]) *) 29 #define __minheap_obj_size(_heap) sizeof((_heap)->data[0]) 30 31 /** 32 * struct min_heap_callbacks - Data/functions to customise the min_heap. 33 * @less: Partial order function for this heap. 34 * @swp: Swap elements function. 35 */ 36 struct min_heap_callbacks { 37 bool (*less)(const void *lhs, const void *rhs, void *args); 38 void (*swp)(void *lhs, void *rhs, void *args); 39 }; 40 41 /* Initialize a min-heap. */ 42 static __always_inline 43 void __min_heap_init(min_heap_char *heap, void *data, int size) 44 { 45 heap->nr = 0; 46 heap->size = size; 47 if (data) 48 heap->data = data; 49 else 50 heap->data = heap->preallocated; 51 } 52 53 #define min_heap_init(_heap, _data, _size) \ 54 __min_heap_init((min_heap_char *)_heap, _data, _size) 55 56 /* Get the minimum element from the heap. */ 57 static __always_inline 58 void *__min_heap_peek(struct min_heap_char *heap) 59 { 60 return heap->nr ? heap->data : NULL; 61 } 62 63 #define min_heap_peek(_heap) \ 64 (__minheap_cast(_heap) __min_heap_peek((min_heap_char *)_heap)) 65 66 /* Check if the heap is full. */ 67 static __always_inline 68 bool __min_heap_full(min_heap_char *heap) 69 { 70 return heap->nr == heap->size; 71 } 72 73 #define min_heap_full(_heap) \ 74 __min_heap_full((min_heap_char *)_heap) 75 76 /* Sift the element at pos down the heap. */ 77 static __always_inline 78 void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size, 79 const struct min_heap_callbacks *func, void *args) 80 { 81 void *left, *right; 82 void *data = heap->data; 83 void *root = data + pos * elem_size; 84 int i = pos, j; 85 86 /* Find the sift-down path all the way to the leaves. */ 87 for (;;) { 88 if (i * 2 + 2 >= heap->nr) 89 break; 90 left = data + (i * 2 + 1) * elem_size; 91 right = data + (i * 2 + 2) * elem_size; 92 i = func->less(left, right, args) ? i * 2 + 1 : i * 2 + 2; 93 } 94 95 /* Special case for the last leaf with no sibling. */ 96 if (i * 2 + 2 == heap->nr) 97 i = i * 2 + 1; 98 99 /* Backtrack to the correct location. */ 100 while (i != pos && func->less(root, data + i * elem_size, args)) 101 i = (i - 1) / 2; 102 103 /* Shift the element into its correct place. */ 104 j = i; 105 while (i != pos) { 106 i = (i - 1) / 2; 107 func->swp(data + i * elem_size, data + j * elem_size, args); 108 } 109 } 110 111 #define min_heap_sift_down(_heap, _pos, _func, _args) \ 112 __min_heap_sift_down((min_heap_char *)_heap, _pos, __minheap_obj_size(_heap), _func, _args) 113 114 /* Sift up ith element from the heap, O(log2(nr)). */ 115 static __always_inline 116 void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx, 117 const struct min_heap_callbacks *func, void *args) 118 { 119 void *data = heap->data; 120 size_t parent; 121 122 while (idx) { 123 parent = (idx - 1) / 2; 124 if (func->less(data + parent * elem_size, data + idx * elem_size, args)) 125 break; 126 func->swp(data + parent * elem_size, data + idx * elem_size, args); 127 idx = parent; 128 } 129 } 130 131 #define min_heap_sift_up(_heap, _idx, _func, _args) \ 132 __min_heap_sift_up((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args) 133 134 /* Floyd's approach to heapification that is O(nr). */ 135 static __always_inline 136 void __min_heapify_all(min_heap_char *heap, size_t elem_size, 137 const struct min_heap_callbacks *func, void *args) 138 { 139 int i; 140 141 for (i = heap->nr / 2 - 1; i >= 0; i--) 142 __min_heap_sift_down(heap, i, elem_size, func, args); 143 } 144 145 #define min_heapify_all(_heap, _func, _args) \ 146 __min_heapify_all((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) 147 148 /* Remove minimum element from the heap, O(log2(nr)). */ 149 static __always_inline 150 bool __min_heap_pop(min_heap_char *heap, size_t elem_size, 151 const struct min_heap_callbacks *func, void *args) 152 { 153 void *data = heap->data; 154 155 if (WARN_ONCE(heap->nr <= 0, "Popping an empty heap")) 156 return false; 157 158 /* Place last element at the root (position 0) and then sift down. */ 159 heap->nr--; 160 memcpy(data, data + (heap->nr * elem_size), elem_size); 161 __min_heap_sift_down(heap, 0, elem_size, func, args); 162 163 return true; 164 } 165 166 #define min_heap_pop(_heap, _func, _args) \ 167 __min_heap_pop((min_heap_char *)_heap, __minheap_obj_size(_heap), _func, _args) 168 169 /* 170 * Remove the minimum element and then push the given element. The 171 * implementation performs 1 sift (O(log2(nr))) and is therefore more 172 * efficient than a pop followed by a push that does 2. 173 */ 174 static __always_inline 175 void __min_heap_pop_push(min_heap_char *heap, 176 const void *element, size_t elem_size, 177 const struct min_heap_callbacks *func, 178 void *args) 179 { 180 memcpy(heap->data, element, elem_size); 181 __min_heap_sift_down(heap, 0, elem_size, func, args); 182 } 183 184 #define min_heap_pop_push(_heap, _element, _func, _args) \ 185 __min_heap_pop_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args) 186 187 /* Push an element on to the heap, O(log2(nr)). */ 188 static __always_inline 189 bool __min_heap_push(min_heap_char *heap, const void *element, size_t elem_size, 190 const struct min_heap_callbacks *func, void *args) 191 { 192 void *data = heap->data; 193 int pos; 194 195 if (WARN_ONCE(heap->nr >= heap->size, "Pushing on a full heap")) 196 return false; 197 198 /* Place at the end of data. */ 199 pos = heap->nr; 200 memcpy(data + (pos * elem_size), element, elem_size); 201 heap->nr++; 202 203 /* Sift child at pos up. */ 204 __min_heap_sift_up(heap, elem_size, pos, func, args); 205 206 return true; 207 } 208 209 #define min_heap_push(_heap, _element, _func, _args) \ 210 __min_heap_push((min_heap_char *)_heap, _element, __minheap_obj_size(_heap), _func, _args) 211 212 /* Remove ith element from the heap, O(log2(nr)). */ 213 static __always_inline 214 bool __min_heap_del(min_heap_char *heap, size_t elem_size, size_t idx, 215 const struct min_heap_callbacks *func, void *args) 216 { 217 void *data = heap->data; 218 219 if (WARN_ONCE(heap->nr <= 0, "Popping an empty heap")) 220 return false; 221 222 /* Place last element at the root (position 0) and then sift down. */ 223 heap->nr--; 224 if (idx == heap->nr) 225 return true; 226 func->swp(data + (idx * elem_size), data + (heap->nr * elem_size), args); 227 __min_heap_sift_up(heap, elem_size, idx, func, args); 228 __min_heap_sift_down(heap, idx, elem_size, func, args); 229 230 return true; 231 } 232 233 #define min_heap_del(_heap, _idx, _func, _args) \ 234 __min_heap_del((min_heap_char *)_heap, __minheap_obj_size(_heap), _idx, _func, _args) 235 236 #endif /* _LINUX_MIN_HEAP_H */ 237
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.