-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
- Loading branch information
Mathieu Desnoyers
authored and
Greg Kroah-Hartman
committed
Nov 29, 2011
1 parent
b7cd2d9
commit 1b4d28b
Showing
2 changed files
with
324 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
/* | ||
* lttng_prio_heap.c | ||
* | ||
* Priority heap containing pointers. Based on CLRS, chapter 6. | ||
* | ||
* Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
*/ | ||
|
||
#include <linux/slab.h> | ||
#include "lttng_prio_heap.h" | ||
|
||
#ifdef DEBUG_HEAP | ||
void lttng_check_heap(const struct lttng_ptr_heap *heap) | ||
{ | ||
size_t i; | ||
|
||
if (!heap->len) | ||
return; | ||
|
||
for (i = 1; i < heap->len; i++) | ||
WARN_ON_ONCE(!heap->gt(heap->ptrs[i], heap->ptrs[0])); | ||
} | ||
#endif | ||
|
||
static | ||
size_t parent(size_t i) | ||
{ | ||
return (i -1) >> 1; | ||
} | ||
|
||
static | ||
size_t left(size_t i) | ||
{ | ||
return (i << 1) + 1; | ||
} | ||
|
||
static | ||
size_t right(size_t i) | ||
{ | ||
return (i << 1) + 2; | ||
} | ||
|
||
/* | ||
* Copy of heap->ptrs pointer is invalid after heap_grow. | ||
*/ | ||
static | ||
int heap_grow(struct lttng_ptr_heap *heap, size_t new_len) | ||
{ | ||
void **new_ptrs; | ||
|
||
if (heap->alloc_len >= new_len) | ||
return 0; | ||
|
||
heap->alloc_len = max_t(size_t, new_len, heap->alloc_len << 1); | ||
new_ptrs = kmalloc(heap->alloc_len * sizeof(void *), heap->gfpmask); | ||
if (!new_ptrs) | ||
return -ENOMEM; | ||
if (heap->ptrs) | ||
memcpy(new_ptrs, heap->ptrs, heap->len * sizeof(void *)); | ||
kfree(heap->ptrs); | ||
heap->ptrs = new_ptrs; | ||
return 0; | ||
} | ||
|
||
static | ||
int heap_set_len(struct lttng_ptr_heap *heap, size_t new_len) | ||
{ | ||
int ret; | ||
|
||
ret = heap_grow(heap, new_len); | ||
if (ret) | ||
return ret; | ||
heap->len = new_len; | ||
return 0; | ||
} | ||
|
||
int lttng_heap_init(struct lttng_ptr_heap *heap, size_t alloc_len, | ||
gfp_t gfpmask, int gt(void *a, void *b)) | ||
{ | ||
heap->ptrs = NULL; | ||
heap->len = 0; | ||
heap->alloc_len = 0; | ||
heap->gt = gt; | ||
heap->gfpmask = gfpmask; | ||
/* | ||
* Minimum size allocated is 1 entry to ensure memory allocation | ||
* never fails within heap_replace_max. | ||
*/ | ||
return heap_grow(heap, max_t(size_t, 1, alloc_len)); | ||
} | ||
|
||
void lttng_heap_free(struct lttng_ptr_heap *heap) | ||
{ | ||
kfree(heap->ptrs); | ||
} | ||
|
||
static void heapify(struct lttng_ptr_heap *heap, size_t i) | ||
{ | ||
void **ptrs = heap->ptrs; | ||
size_t l, r, largest; | ||
|
||
for (;;) { | ||
void *tmp; | ||
|
||
l = left(i); | ||
r = right(i); | ||
if (l < heap->len && heap->gt(ptrs[l], ptrs[i])) | ||
largest = l; | ||
else | ||
largest = i; | ||
if (r < heap->len && heap->gt(ptrs[r], ptrs[largest])) | ||
largest = r; | ||
if (largest == i) | ||
break; | ||
tmp = ptrs[i]; | ||
ptrs[i] = ptrs[largest]; | ||
ptrs[largest] = tmp; | ||
i = largest; | ||
} | ||
lttng_check_heap(heap); | ||
} | ||
|
||
void *lttng_heap_replace_max(struct lttng_ptr_heap *heap, void *p) | ||
{ | ||
void *res; | ||
|
||
if (!heap->len) { | ||
(void) heap_set_len(heap, 1); | ||
heap->ptrs[0] = p; | ||
lttng_check_heap(heap); | ||
return NULL; | ||
} | ||
|
||
/* Replace the current max and heapify */ | ||
res = heap->ptrs[0]; | ||
heap->ptrs[0] = p; | ||
heapify(heap, 0); | ||
return res; | ||
} | ||
|
||
int lttng_heap_insert(struct lttng_ptr_heap *heap, void *p) | ||
{ | ||
void **ptrs; | ||
size_t pos; | ||
int ret; | ||
|
||
ret = heap_set_len(heap, heap->len + 1); | ||
if (ret) | ||
return ret; | ||
ptrs = heap->ptrs; | ||
pos = heap->len - 1; | ||
while (pos > 0 && heap->gt(p, ptrs[parent(pos)])) { | ||
/* Move parent down until we find the right spot */ | ||
ptrs[pos] = ptrs[parent(pos)]; | ||
pos = parent(pos); | ||
} | ||
ptrs[pos] = p; | ||
lttng_check_heap(heap); | ||
return 0; | ||
} | ||
|
||
void *lttng_heap_remove(struct lttng_ptr_heap *heap) | ||
{ | ||
switch (heap->len) { | ||
case 0: | ||
return NULL; | ||
case 1: | ||
(void) heap_set_len(heap, 0); | ||
return heap->ptrs[0]; | ||
} | ||
/* Shrink, replace the current max by previous last entry and heapify */ | ||
heap_set_len(heap, heap->len - 1); | ||
/* len changed. previous last entry is at heap->len */ | ||
return lttng_heap_replace_max(heap, heap->ptrs[heap->len]); | ||
} | ||
|
||
void *lttng_heap_cherrypick(struct lttng_ptr_heap *heap, void *p) | ||
{ | ||
size_t pos, len = heap->len; | ||
|
||
for (pos = 0; pos < len; pos++) | ||
if (heap->ptrs[pos] == p) | ||
goto found; | ||
return NULL; | ||
found: | ||
if (heap->len == 1) { | ||
(void) heap_set_len(heap, 0); | ||
lttng_check_heap(heap); | ||
return heap->ptrs[0]; | ||
} | ||
/* Replace p with previous last entry and heapify. */ | ||
heap_set_len(heap, heap->len - 1); | ||
/* len changed. previous last entry is at heap->len */ | ||
heap->ptrs[pos] = heap->ptrs[heap->len]; | ||
heapify(heap, pos); | ||
return p; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#ifndef _LTTNG_PRIO_HEAP_H | ||
#define _LTTNG_PRIO_HEAP_H | ||
|
||
/* | ||
* lttng_prio_heap.h | ||
* | ||
* Priority heap containing pointers. Based on CLRS, chapter 6. | ||
* | ||
* Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
*/ | ||
|
||
#include <linux/gfp.h> | ||
|
||
struct lttng_ptr_heap { | ||
size_t len, alloc_len; | ||
void **ptrs; | ||
int (*gt)(void *a, void *b); | ||
gfp_t gfpmask; | ||
}; | ||
|
||
#ifdef DEBUG_HEAP | ||
void lttng_check_heap(const struct lttng_ptr_heap *heap); | ||
#else | ||
static inline | ||
void lttng_check_heap(const struct lttng_ptr_heap *heap) | ||
{ | ||
} | ||
#endif | ||
|
||
/** | ||
* lttng_heap_maximum - return the largest element in the heap | ||
* @heap: the heap to be operated on | ||
* | ||
* Returns the largest element in the heap, without performing any modification | ||
* to the heap structure. Returns NULL if the heap is empty. | ||
*/ | ||
static inline void *lttng_heap_maximum(const struct lttng_ptr_heap *heap) | ||
{ | ||
lttng_check_heap(heap); | ||
return heap->len ? heap->ptrs[0] : NULL; | ||
} | ||
|
||
/** | ||
* lttng_heap_init - initialize the heap | ||
* @heap: the heap to initialize | ||
* @alloc_len: number of elements initially allocated | ||
* @gfp: allocation flags | ||
* @gt: function to compare the elements | ||
* | ||
* Returns -ENOMEM if out of memory. | ||
*/ | ||
extern int lttng_heap_init(struct lttng_ptr_heap *heap, | ||
size_t alloc_len, gfp_t gfpmask, | ||
int gt(void *a, void *b)); | ||
|
||
/** | ||
* lttng_heap_free - free the heap | ||
* @heap: the heap to free | ||
*/ | ||
extern void lttng_heap_free(struct lttng_ptr_heap *heap); | ||
|
||
/** | ||
* lttng_heap_insert - insert an element into the heap | ||
* @heap: the heap to be operated on | ||
* @p: the element to add | ||
* | ||
* Insert an element into the heap. | ||
* | ||
* Returns -ENOMEM if out of memory. | ||
*/ | ||
extern int lttng_heap_insert(struct lttng_ptr_heap *heap, void *p); | ||
|
||
/** | ||
* lttng_heap_remove - remove the largest element from the heap | ||
* @heap: the heap to be operated on | ||
* | ||
* Returns the largest element in the heap. It removes this element from the | ||
* heap. Returns NULL if the heap is empty. | ||
*/ | ||
extern void *lttng_heap_remove(struct lttng_ptr_heap *heap); | ||
|
||
/** | ||
* lttng_heap_cherrypick - remove a given element from the heap | ||
* @heap: the heap to be operated on | ||
* @p: the element | ||
* | ||
* Remove the given element from the heap. Return the element if present, else | ||
* return NULL. This algorithm has a complexity of O(n), which is higher than | ||
* O(log(n)) provided by the rest of this API. | ||
*/ | ||
extern void *lttng_heap_cherrypick(struct lttng_ptr_heap *heap, void *p); | ||
|
||
/** | ||
* lttng_heap_replace_max - replace the the largest element from the heap | ||
* @heap: the heap to be operated on | ||
* @p: the pointer to be inserted as topmost element replacement | ||
* | ||
* Returns the largest element in the heap. It removes this element from the | ||
* heap. The heap is rebalanced only once after the insertion. Returns NULL if | ||
* the heap is empty. | ||
* | ||
* This is the equivalent of calling heap_remove() and then heap_insert(), but | ||
* it only rebalances the heap once. It never allocates memory. | ||
*/ | ||
extern void *lttng_heap_replace_max(struct lttng_ptr_heap *heap, void *p); | ||
|
||
#endif /* _LTTNG_PRIO_HEAP_H */ |