3
* Copyright (C) 2014 Christian Hergert <christian@hergert.me>
5
* This file is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
10
* This file is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#define G_LOG_DOMAIN "egg-heap"
28
* @short_description: efficient priority queues using min/max heaps
30
* Heaps are similar to a partially sorted tree but implemented as an
31
* array. They allow for efficient O(1) lookup of the highest priority
32
* item as it will always be the first item of the array.
34
* To create a new heap use egg_heap_new().
36
* To add items to the heap, use egg_heap_insert_val() or
37
* egg_heap_insert_vals() to insert in bulk.
39
* To access an item in the heap, use egg_heap_index().
41
* To remove an arbitrary item from the heap, use egg_heap_extract_index().
43
* To remove the highest priority item in the heap, use egg_heap_extract().
45
* To free a heap, use egg_heap_unref().
47
* Here is an example that stores integers in a #EggHeap:
48
* |[<!-- language="C" -->
50
* cmpint (gconstpointer a,
53
* return *(const gint *)a - *(const gint *)b;
64
* heap = egg_heap_new (sizeof (gint), cmpint);
66
* for (i = 0; i < 10000; i++)
67
* egg_heap_insert_val (heap, i);
68
* for (i = 0; i < 10000; i++)
69
* egg_heap_extract (heap, &v);
71
* egg_heap_unref (heap);
76
#define MIN_HEAP_SIZE 16
79
* Based upon Mastering Algorithms in C by Kyle Loudon.
80
* Section 10 - Heaps and Priority Queues.
83
typedef struct _EggHeapReal EggHeapReal;
89
volatile gint ref_count;
96
#define heap_parent(npos) (((npos)-1)/2)
97
#define heap_left(npos) (((npos)*2)+1)
98
#define heap_right(npos) (((npos)*2)+2)
99
#define heap_index(h,i) ((h)->data + (i * (h)->element_size))
100
#define heap_compare(h,a,b) ((h)->compare(heap_index(h,a), heap_index(h,b)))
101
#define heap_swap(h,a,b) \
103
memcpy ((h)->tmp, heap_index (h, a), (h)->element_size); \
104
memcpy (heap_index (h, a), heap_index (h, b), (h)->element_size); \
105
memcpy (heap_index (h, b), (h)->tmp, (h)->element_size); \
109
egg_heap_new (guint element_size,
110
GCompareFunc compare_func)
114
g_return_val_if_fail (element_size, NULL);
115
g_return_val_if_fail (compare_func, NULL);
117
real = g_malloc_n (1, sizeof (EggHeapReal) + element_size);
121
real->element_size = element_size;
122
real->allocated_len = 0;
123
real->compare = compare_func;
125
return (EggHeap *)real;
129
egg_heap_ref (EggHeap *heap)
131
EggHeapReal *real = (EggHeapReal *)heap;
133
g_return_val_if_fail (heap, NULL);
134
g_return_val_if_fail (real->ref_count, NULL);
136
g_atomic_int_inc (&real->ref_count);
142
egg_heap_real_free (EggHeapReal *real)
145
g_assert_cmpint (real->ref_count, ==, 0);
152
egg_heap_unref (EggHeap *heap)
154
EggHeapReal *real = (EggHeapReal *)heap;
156
g_return_if_fail (heap);
157
g_return_if_fail (real->ref_count);
159
if (g_atomic_int_dec_and_test (&real->ref_count))
160
egg_heap_real_free (real);
164
egg_heap_real_grow (EggHeapReal *real)
167
g_assert_cmpint (real->allocated_len, <, G_MAXSIZE / 2);
169
real->allocated_len = MAX (MIN_HEAP_SIZE, (real->allocated_len * 2));
170
real->data = g_realloc_n (real->data,
176
egg_heap_real_shrink (EggHeapReal *real)
179
g_assert ((real->allocated_len / 2) >= real->len);
181
real->allocated_len = MAX (MIN_HEAP_SIZE, real->allocated_len / 2);
182
real->data = g_realloc_n (real->data,
188
egg_heap_real_insert_val (EggHeapReal *real,
197
if (G_UNLIKELY (real->len == real->allocated_len))
198
egg_heap_real_grow (real);
200
memcpy (real->data + (real->element_size * real->len),
205
ppos = heap_parent (ipos);
207
while ((ipos > 0) && (heap_compare (real, ppos, ipos) < 0))
209
heap_swap (real, ppos, ipos);
211
ppos = heap_parent (ipos);
218
egg_heap_insert_vals (EggHeap *heap,
222
EggHeapReal *real = (EggHeapReal *)heap;
223
const gchar *ptr = data;
226
g_return_if_fail (heap);
227
g_return_if_fail (data);
228
g_return_if_fail (len);
230
for (i = 0; i < len; i++, ptr += real->element_size)
231
egg_heap_real_insert_val (real, ptr);
235
egg_heap_extract (EggHeap *heap,
238
EggHeapReal *real = (EggHeapReal *)heap;
244
g_return_val_if_fail (heap, FALSE);
250
memcpy (result, heap_index (real, 0), real->element_size);
255
heap_index (real, real->len),
262
lpos = heap_left (ipos);
263
rpos = heap_right (ipos);
265
if ((lpos < real->len) && (heap_compare (real, lpos, ipos) > 0))
270
if ((rpos < real->len) && (heap_compare (real, rpos, mpos) > 0))
276
heap_swap (real, mpos, ipos);
282
if ((real->len > MIN_HEAP_SIZE) && (real->allocated_len / 2) >= real->len)
283
egg_heap_real_shrink (real);
289
egg_heap_extract_index (EggHeap *heap,
293
EggHeapReal *real = (EggHeapReal *)heap;
300
g_return_val_if_fail (heap, FALSE);
306
memcpy (result, heap_index (real, index_), real->element_size);
310
if (real->len && index_ != real->len)
312
memcpy (heap_index (real, index_),
313
heap_index (real, real->len),
317
ppos = heap_parent (ipos);
319
while (heap_compare (real, ipos, ppos) > 0)
321
heap_swap (real, ipos, ppos);
323
ppos = heap_parent (ppos);
330
lpos = heap_left (ipos);
331
rpos = heap_right (ipos);
333
if ((lpos < real->len) && (heap_compare (real, lpos, ipos) > 0))
338
if ((rpos < real->len) && (heap_compare (real, rpos, mpos) > 0))
344
heap_swap (real, mpos, ipos);
351
if ((real->len > MIN_HEAP_SIZE) && (real->allocated_len / 2) >= real->len)
352
egg_heap_real_shrink (real);