xref: /openbmc/linux/include/linux/min_heap.h (revision 6e24628d)
16e24628dSIan Rogers /* SPDX-License-Identifier: GPL-2.0 */
26e24628dSIan Rogers #ifndef _LINUX_MIN_HEAP_H
36e24628dSIan Rogers #define _LINUX_MIN_HEAP_H
46e24628dSIan Rogers 
56e24628dSIan Rogers #include <linux/bug.h>
66e24628dSIan Rogers #include <linux/string.h>
76e24628dSIan Rogers #include <linux/types.h>
86e24628dSIan Rogers 
96e24628dSIan Rogers /**
106e24628dSIan Rogers  * struct min_heap - Data structure to hold a min-heap.
116e24628dSIan Rogers  * @data: Start of array holding the heap elements.
126e24628dSIan Rogers  * @nr: Number of elements currently in the heap.
136e24628dSIan Rogers  * @size: Maximum number of elements that can be held in current storage.
146e24628dSIan Rogers  */
156e24628dSIan Rogers struct min_heap {
166e24628dSIan Rogers 	void *data;
176e24628dSIan Rogers 	int nr;
186e24628dSIan Rogers 	int size;
196e24628dSIan Rogers };
206e24628dSIan Rogers 
216e24628dSIan Rogers /**
226e24628dSIan Rogers  * struct min_heap_callbacks - Data/functions to customise the min_heap.
236e24628dSIan Rogers  * @elem_size: The nr of each element in bytes.
246e24628dSIan Rogers  * @less: Partial order function for this heap.
256e24628dSIan Rogers  * @swp: Swap elements function.
266e24628dSIan Rogers  */
276e24628dSIan Rogers struct min_heap_callbacks {
286e24628dSIan Rogers 	int elem_size;
296e24628dSIan Rogers 	bool (*less)(const void *lhs, const void *rhs);
306e24628dSIan Rogers 	void (*swp)(void *lhs, void *rhs);
316e24628dSIan Rogers };
326e24628dSIan Rogers 
336e24628dSIan Rogers /* Sift the element at pos down the heap. */
346e24628dSIan Rogers static __always_inline
min_heapify(struct min_heap * heap,int pos,const struct min_heap_callbacks * func)356e24628dSIan Rogers void min_heapify(struct min_heap *heap, int pos,
366e24628dSIan Rogers 		const struct min_heap_callbacks *func)
376e24628dSIan Rogers {
386e24628dSIan Rogers 	void *left, *right, *parent, *smallest;
396e24628dSIan Rogers 	void *data = heap->data;
406e24628dSIan Rogers 
416e24628dSIan Rogers 	for (;;) {
426e24628dSIan Rogers 		if (pos * 2 + 1 >= heap->nr)
436e24628dSIan Rogers 			break;
446e24628dSIan Rogers 
456e24628dSIan Rogers 		left = data + ((pos * 2 + 1) * func->elem_size);
466e24628dSIan Rogers 		parent = data + (pos * func->elem_size);
476e24628dSIan Rogers 		smallest = parent;
486e24628dSIan Rogers 		if (func->less(left, smallest))
496e24628dSIan Rogers 			smallest = left;
506e24628dSIan Rogers 
516e24628dSIan Rogers 		if (pos * 2 + 2 < heap->nr) {
526e24628dSIan Rogers 			right = data + ((pos * 2 + 2) * func->elem_size);
536e24628dSIan Rogers 			if (func->less(right, smallest))
546e24628dSIan Rogers 				smallest = right;
556e24628dSIan Rogers 		}
566e24628dSIan Rogers 		if (smallest == parent)
576e24628dSIan Rogers 			break;
586e24628dSIan Rogers 		func->swp(smallest, parent);
596e24628dSIan Rogers 		if (smallest == left)
606e24628dSIan Rogers 			pos = (pos * 2) + 1;
616e24628dSIan Rogers 		else
626e24628dSIan Rogers 			pos = (pos * 2) + 2;
636e24628dSIan Rogers 	}
646e24628dSIan Rogers }
656e24628dSIan Rogers 
666e24628dSIan Rogers /* Floyd's approach to heapification that is O(nr). */
676e24628dSIan Rogers static __always_inline
min_heapify_all(struct min_heap * heap,const struct min_heap_callbacks * func)686e24628dSIan Rogers void min_heapify_all(struct min_heap *heap,
696e24628dSIan Rogers 		const struct min_heap_callbacks *func)
706e24628dSIan Rogers {
716e24628dSIan Rogers 	int i;
726e24628dSIan Rogers 
736e24628dSIan Rogers 	for (i = heap->nr / 2; i >= 0; i--)
746e24628dSIan Rogers 		min_heapify(heap, i, func);
756e24628dSIan Rogers }
766e24628dSIan Rogers 
776e24628dSIan Rogers /* Remove minimum element from the heap, O(log2(nr)). */
786e24628dSIan Rogers static __always_inline
min_heap_pop(struct min_heap * heap,const struct min_heap_callbacks * func)796e24628dSIan Rogers void min_heap_pop(struct min_heap *heap,
806e24628dSIan Rogers 		const struct min_heap_callbacks *func)
816e24628dSIan Rogers {
826e24628dSIan Rogers 	void *data = heap->data;
836e24628dSIan Rogers 
846e24628dSIan Rogers 	if (WARN_ONCE(heap->nr <= 0, "Popping an empty heap"))
856e24628dSIan Rogers 		return;
866e24628dSIan Rogers 
876e24628dSIan Rogers 	/* Place last element at the root (position 0) and then sift down. */
886e24628dSIan Rogers 	heap->nr--;
896e24628dSIan Rogers 	memcpy(data, data + (heap->nr * func->elem_size), func->elem_size);
906e24628dSIan Rogers 	min_heapify(heap, 0, func);
916e24628dSIan Rogers }
926e24628dSIan Rogers 
936e24628dSIan Rogers /*
946e24628dSIan Rogers  * Remove the minimum element and then push the given element. The
956e24628dSIan Rogers  * implementation performs 1 sift (O(log2(nr))) and is therefore more
966e24628dSIan Rogers  * efficient than a pop followed by a push that does 2.
976e24628dSIan Rogers  */
986e24628dSIan Rogers static __always_inline
min_heap_pop_push(struct min_heap * heap,const void * element,const struct min_heap_callbacks * func)996e24628dSIan Rogers void min_heap_pop_push(struct min_heap *heap,
1006e24628dSIan Rogers 		const void *element,
1016e24628dSIan Rogers 		const struct min_heap_callbacks *func)
1026e24628dSIan Rogers {
1036e24628dSIan Rogers 	memcpy(heap->data, element, func->elem_size);
1046e24628dSIan Rogers 	min_heapify(heap, 0, func);
1056e24628dSIan Rogers }
1066e24628dSIan Rogers 
1076e24628dSIan Rogers /* Push an element on to the heap, O(log2(nr)). */
1086e24628dSIan Rogers static __always_inline
min_heap_push(struct min_heap * heap,const void * element,const struct min_heap_callbacks * func)1096e24628dSIan Rogers void min_heap_push(struct min_heap *heap, const void *element,
1106e24628dSIan Rogers 		const struct min_heap_callbacks *func)
1116e24628dSIan Rogers {
1126e24628dSIan Rogers 	void *data = heap->data;
1136e24628dSIan Rogers 	void *child, *parent;
1146e24628dSIan Rogers 	int pos;
1156e24628dSIan Rogers 
1166e24628dSIan Rogers 	if (WARN_ONCE(heap->nr >= heap->size, "Pushing on a full heap"))
1176e24628dSIan Rogers 		return;
1186e24628dSIan Rogers 
1196e24628dSIan Rogers 	/* Place at the end of data. */
1206e24628dSIan Rogers 	pos = heap->nr;
1216e24628dSIan Rogers 	memcpy(data + (pos * func->elem_size), element, func->elem_size);
1226e24628dSIan Rogers 	heap->nr++;
1236e24628dSIan Rogers 
1246e24628dSIan Rogers 	/* Sift child at pos up. */
1256e24628dSIan Rogers 	for (; pos > 0; pos = (pos - 1) / 2) {
1266e24628dSIan Rogers 		child = data + (pos * func->elem_size);
1276e24628dSIan Rogers 		parent = data + ((pos - 1) / 2) * func->elem_size;
1286e24628dSIan Rogers 		if (func->less(parent, child))
1296e24628dSIan Rogers 			break;
1306e24628dSIan Rogers 		func->swp(parent, child);
1316e24628dSIan Rogers 	}
1326e24628dSIan Rogers }
1336e24628dSIan Rogers 
1346e24628dSIan Rogers #endif /* _LINUX_MIN_HEAP_H */
135