aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2012-03-21 14:59:52 -0400
committerGlenn Elliott <gelliott@cs.unc.edu>2012-03-21 15:04:00 -0400
commit5b73afc4eb1b0303cb92eb29a2ecc59c1db69537 (patch)
treeabf221060b4ddf8f4a8cf046a28966296d9a3b29
parent6a00f206debf8a5c8899055726ad127dbeeed098 (diff)
Binary heap implementation
Motivation: Linux's prio_heap.h is of fixed size. Litmus's binomial heap may be overkill (and perhaps not general enough) for some applications. Implemented in the style of linked lists.
-rw-r--r--include/litmus/binheap.h543
1 files changed, 543 insertions, 0 deletions
diff --git a/include/litmus/binheap.h b/include/litmus/binheap.h
new file mode 100644
index 000000000000..b8dd8e03da60
--- /dev/null
+++ b/include/litmus/binheap.h
@@ -0,0 +1,543 @@
1#ifndef LITMUS_BINARY_HEAP_H
2#define LITMUS_BINARY_HEAP_H
3
4/**
5 * Simple binary heap with add, arbitrary delete, delete_root, and top
6 * operations.
7 *
8 * Style meant to conform with list.h.
9 *
10 * Motivation: Linux's prio_heap.h is of fixed size. Litmus's binomial
11 * heap may be overkill (and perhaps not general enough) for some applications.
12 *
13 * Note: In order to make node swaps fast, a node inserted with a data pointer
14 * may not always hold said data pointer. This is similar to the binomial heap
15 * implementation. This does make node deletion tricky since we have to
16 * (1) locate the node that holds the data pointer to delete, and (2) the
17 * node that was originally inserted with said data pointer. These have to be
18 * coalesced into a single node before removal (see usage of
19 * __binheap_safe_swap()). We have to track node references to accomplish this.
20 */
21
22struct binheap_node {
23 void *data;
24 struct binheap_node *parent;
25 struct binheap_node *left;
26 struct binheap_node *right;
27
28 /* pointer to binheap_node that holds *data for which this binheap_node
29 * was originally inserted. (*data "owns" this node)
30 */
31 struct binheap_node *ref;
32 struct binheap_node **ref_ptr;
33};
34
35/**
36 * Signature of compator function. Assumed 'less-than' (min-heap).
37 * Pass in 'greater-than' for max-heap.
38 *
39 * TODO: Consider macor-based implementation that allows comparator to be
40 * inlined (similar to Linux red/black tree) for greater efficiency.
41 */
42typedef int (*binheap_order_t)(struct binheap_node *a,
43 struct binheap_node *b);
44
45
46struct binheap_handle {
47 struct binheap_node *root;
48
49 /* pointer to node to take next inserted child */
50 struct binheap_node *next;
51
52 /* pointer to last node in complete binary tree */
53 struct binheap_node *last;
54
55 /* comparator function pointer */
56 binheap_order_t compare;
57};
58
59
60/**
61 * binheap_entry - get the struct for this heap node.
62 * Only valid when called upon heap nodes other than the root handle.
63 * @ptr: the heap node.
64 * @type: the type of struct pointed to by binheap_node::data.
65 * @member: unused.
66 */
67#define binheap_entry(ptr, type, member) \
68((type *)((ptr)->data))
69
70/**
71 * binheap_node_container - get the struct that contains this node.
72 * Only valid when called upon heap nodes other than the root handle.
73 * @ptr: the heap node.
74 * @type: the type of struct the node is embedded in.
75 * @member: the name of the binheap_struct within the (type) struct.
76 */
77#define binheap_node_container(ptr, type, member) \
78container_of((ptr), type, member)
79
80/**
81 * binheap_top_entry - get the struct for the node at the top of the heap.
82 * Only valid when called upon the heap handle node.
83 * @ptr: the special heap-handle node.
84 * @type: the type of the struct the head is embedded in.
85 * @member: the name of the binheap_struct within the (type) struct.
86 */
87#define binheap_top_entry(ptr, type, member) \
88binheap_entry((ptr)->root, type, member)
89
90/**
91 * binheap_delete_root - remove the root element from the heap.
92 * @handle: handle to the heap.
93 * @type: the type of the struct the head is embedded in.
94 * @member: the name of the binheap_struct within the (type) struct.
95 */
96#define binheap_delete_root(handle, type, member) \
97__binheap_delete_root((handle), &((type *)((handle)->root->data))->member)
98
99/**
100 * binheap_delete - remove an arbitrary element from the heap.
101 * @to_delete: pointer to node to be removed.
102 * @handle: handle to the heap.
103 * @type: the type of the struct the head is embedded in.
104 * @member: the name of the binheap_struct within the (type) struct.
105 */
106#define binheap_delete(to_delete, handle, type, member) \
107__binheap_delete((to_delete), &((((type*)((to_delete)->data))->member)), (handle))
108
109/**
110 * binheap_add - insert an element to the heap
111 * new_node: node to add.
112 * @handle: handle to the heap.
113 * @type: the type of the struct the head is embedded in.
114 * @member: the name of the binheap_struct within the (type) struct.
115 */
116#define binheap_add(new_node, handle, type, member) \
117__binheap_add((new_node), (handle), container_of((new_node), type, member))
118
119
120#define BINHEAP_NODE_INIT() { NULL, NULL, NULL, NULL , NULL, NULL}
121
122#define BINHEAP_NODE(name) \
123 struct binheap_node name = BINHEAP_NODE_INIT()
124
125
126static inline void INIT_BINHEAP_NODE(struct binheap_node *n)
127{
128 n->data = NULL;
129 n->parent = NULL;
130 n->left = NULL;
131 n->right = NULL;
132 n->ref = NULL;
133 n->ref_ptr = NULL;
134}
135
136static inline void INIT_BINHEAP_HANDLE(
137 struct binheap_handle *handle,
138 binheap_order_t compare)
139{
140 handle->root = NULL;
141 handle->next = NULL;
142 handle->last = NULL;
143 handle->compare = compare;
144}
145
146/* Returns true (1) if binheap is empty. */
147static inline int binheap_empty(struct binheap_handle *handle)
148{
149 return(handle->root == NULL);
150}
151
152
153/* Update the node reference pointers. Same logic as Litmus binomial heap. */
154static inline void __update_ref(struct binheap_node *parent,
155 struct binheap_node *child)
156{
157 *(parent->ref_ptr) = child;
158 *(child->ref_ptr) = parent;
159
160 swap(parent->ref_ptr, child->ref_ptr);
161}
162
163/* Swaps data between two nodes. */
164static inline void __binheap_swap(struct binheap_node *parent,
165 struct binheap_node *child)
166{
167 swap(parent->data, child->data);
168 __update_ref(parent, child);
169}
170
171/* Swaps memory and data between two nodes. Actual nodes swap instead of
172 * just data. Needed when we delete nodes from the heap.
173 */
174static inline void __binheap_swap_safe(struct binheap_handle *handle,
175 struct binheap_node *a,
176 struct binheap_node *b)
177{
178 swap(a->data, b->data);
179 __update_ref(a, b);
180
181 if((a->parent != NULL) && (a->parent == b->parent)) {
182 /* special case: shared parent */
183 swap(a->parent->left, a->parent->right);
184 }
185 else {
186 /* Update pointers to swap parents. */
187
188 if(a->parent) {
189 if(a == a->parent->left) {
190 a->parent->left = b;
191 }
192 else {
193 a->parent->right = b;
194 }
195 }
196
197 if(b->parent) {
198 if(b == b->parent->left) {
199 b->parent->left = a;
200 }
201 else {
202 b->parent->right = a;
203 }
204 }
205
206 swap(a->parent, b->parent);
207 }
208
209 /* swap children */
210
211 if(a->left) {
212 a->left->parent = b;
213
214 if(a->right) {
215 a->right->parent = b;
216 }
217 }
218
219 if(b->left) {
220 b->left->parent = a;
221
222 if(b->right) {
223 b->right->parent = a;
224 }
225 }
226
227 swap(a->left, b->left);
228 swap(a->right, b->right);
229
230
231 /* update next/last/root pointers */
232
233 if(a == handle->next) {
234 handle->next = b;
235 }
236 else if(b == handle->next) {
237 handle->next = a;
238 }
239
240 if(a == handle->last) {
241 handle->last = b;
242 }
243 else if(b == handle->last) {
244 handle->last = a;
245 }
246
247 if(a == handle->root) {
248 handle->root = b;
249 }
250 else if(b == handle->root) {
251 handle->root = a;
252 }
253}
254
255
256/**
257 * Update the pointer to the last node in the complete binary tree.
258 * Called internally after the root node has been deleted.
259 */
260static inline void __binheap_update_last(struct binheap_handle *handle)
261{
262 struct binheap_node *temp = handle->last;
263
264 /* find a "bend" in the tree. */
265 while(temp->parent && (temp == temp->parent->left)) {
266 temp = temp->parent;
267 }
268
269 /* step over to sibling if we're not at root */
270 if(temp->parent != NULL) {
271 temp = temp->parent->left;
272 }
273
274 /* now travel right as far as possible. */
275 while(temp->right != NULL) {
276 temp = temp->right;
277 }
278
279 /* take one step to the left if we're not at the bottom-most level. */
280 if(temp->left != NULL) {
281 temp = temp->left;
282 }
283
284 //BUG_ON(!(temp->left == NULL && temp->right == NULL));
285
286 handle->last = temp;
287}
288
289/**
290 * Update the pointer to the node that will take the next inserted node.
291 * Called internally after a node has been inserted.
292 */
293static inline void __binheap_update_next(struct binheap_handle *handle)
294{
295 struct binheap_node *temp = handle->next;
296
297 /* find a "bend" in the tree. */
298 while(temp->parent && (temp == temp->parent->right)) {
299 temp = temp->parent;
300 }
301
302 /* step over to sibling if we're not at root */
303 if(temp->parent != NULL) {
304 temp = temp->parent->right;
305 }
306
307 /* now travel left as far as possible. */
308 while(temp->left != NULL) {
309 temp = temp->left;
310 }
311
312 handle->next = temp;
313}
314
315
316
317/* bubble node up towards root */
318static inline void __binheap_bubble_up(
319 struct binheap_handle *handle,
320 struct binheap_node *node)
321{
322 /* Note: NULL data pointers are used internally for arbitrary delete */
323 while((node->parent != NULL) &&
324 ((node->data == NULL) /* let null data bubble to the top */ ||
325 handle->compare(node, node->parent))) {
326 __binheap_swap(node->parent, node);
327 node = node->parent;
328 }
329}
330
331
332/* bubble node down, swapping with min-child */
333static inline void __binheap_bubble_down(struct binheap_handle *handle)
334{
335 struct binheap_node *node = handle->root;
336
337 while(node->left != NULL) {
338 if(node->right && handle->compare(node->right, node->left)) {
339 if(handle->compare(node->right, node)) {
340 __binheap_swap(node, node->right);
341 node = node->right;
342 }
343 else {
344 break;
345 }
346 }
347 else {
348 if(handle->compare(node->left, node)) {
349 __binheap_swap(node, node->left);
350 node = node->left;
351 }
352 else {
353 break;
354 }
355 }
356 }
357}
358
359
360
361static inline void __binheap_add(struct binheap_node *new_node,
362 struct binheap_handle *handle,
363 void *data)
364{
365 /* NULL data pointers are used internally */
366 if(!data) {
367 WARN();
368 return;
369 }
370
371 new_node->data = data;
372 new_node->ref = new_node;
373 new_node->ref_ptr = &(new_node->ref);
374
375 if(!binheap_empty(handle)) {
376 /* insert left side first */
377 if(handle->next->left == NULL) {
378 handle->next->left = new_node;
379 new_node->parent = handle->next;
380 new_node->left = NULL;
381 new_node->right = NULL;
382
383 handle->last = new_node;
384
385 __binheap_bubble_up(handle, new_node);
386 }
387 else {
388 /* left occupied. insert right. */
389 handle->next->right = new_node;
390 new_node->parent = handle->next;
391 new_node->left = NULL;
392 new_node->right = NULL;
393
394 handle->last = new_node;
395
396 __binheap_update_next(handle);
397 __binheap_bubble_up(handle, new_node);
398 }
399 }
400 else {
401 /* first node in heap */
402
403 new_node->parent = NULL;
404 new_node->left = NULL;
405 new_node->right = NULL;
406
407 handle->root = new_node;
408 handle->next = new_node;
409 handle->last = new_node;
410 }
411}
412
413
414
415/**
416 * Removes the root node from the heap. The node is removed after coalescing
417 * the binheap_node with its original data pointer at the root of the tree.
418 *
419 * The 'last' node in the tree is then swapped up to the root and bubbled
420 * down.
421 */
422static inline void __binheap_delete_root(struct binheap_handle *handle,
423 struct binheap_node *container)
424{
425 struct binheap_node *root = handle->root;
426
427 if(root != container) {
428 /* coalesce */
429 __binheap_swap_safe(handle, root, container);
430 root = container;
431 }
432
433 if(handle->last != root) {
434 /* swap 'last' node up to root and bubble it down. */
435
436 struct binheap_node *to_move = handle->last;
437
438 if(to_move->parent != root) {
439 handle->next = to_move->parent;
440
441 if(handle->next->right == to_move) {
442 /* disconnect from parent */
443 to_move->parent->right = NULL;
444 handle->last = handle->next->left;
445 }
446 else {
447 /* find new 'last' before we disconnect */
448 __binheap_update_last(handle);
449
450 /* disconnect from parent */
451 to_move->parent->left = NULL;
452 }
453 }
454 else {
455 /* 'last' is direct child of root */
456
457 handle->next = to_move;
458
459 if(to_move == to_move->parent->right) {
460 to_move->parent->right = NULL;
461 handle->last = to_move->parent->left;
462 }
463 else {
464 to_move->parent->left = NULL;
465 handle->last = to_move;
466 }
467 }
468 to_move->parent = NULL;
469
470 /* reconnect as root. We can't just swap data ptrs since root node
471 * may be freed after this function returns.
472 */
473 to_move->left = root->left;
474 to_move->right = root->right;
475 if(to_move->left != NULL) {
476 to_move->left->parent = to_move;
477 }
478 if(to_move->right != NULL) {
479 to_move->right->parent = to_move;
480 }
481
482 handle->root = to_move;
483
484 /* bubble down */
485 __binheap_bubble_down(handle);
486 }
487 else {
488 /* removing last node in tree */
489 handle->root = NULL;
490 handle->next = NULL;
491 handle->last = NULL;
492 }
493}
494
495
496/**
497 * Delete an arbitrary node. We first coalesce, then bubble
498 * node to delete up to the root, and then delete to root.
499 */
500static inline void __binheap_delete(
501 struct binheap_node *node_to_delete,
502 struct binheap_node *user_of_node,
503 struct binheap_handle *handle)
504{
505 struct binheap_node *root, *target;
506 void *temp_data;
507
508 /* Position the node to delete at the root. */
509
510 /* coalesce: swap nodes with user of the node to delete. */
511 if(node_to_delete != user_of_node) {
512 /* we need to take container's memory/node out of the heap,
513 * so swap with the user of the node
514 */
515 __binheap_swap_safe(handle, user_of_node, node_to_delete);
516 }
517
518 root = handle->root;
519
520 /* Move the _node_ to the root */
521 if(root != node_to_delete) {
522 __binheap_swap_safe(handle, root, node_to_delete);
523 node_to_delete = root;
524 }
525
526 node_to_delete = handle->root;
527 target = node_to_delete->ref;
528
529 /* Bubble the data pointer back up to its node, which has already been
530 * positioned at the root. */
531
532 /* temporarily set data to null to allow node to bubble up to the top. */
533 temp_data = target->data;
534 target->data = NULL;
535
536 __binheap_bubble_up(handle, target);
537 __binheap_delete_root(handle, node_to_delete);
538
539 node_to_delete->data = temp_data; /* restore node data pointer */
540}
541
542#endif
543