The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
heap.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Functions for a basic binary heaps
18 *
19 * @file src/lib/util/heap.c
20 *
21 * @copyright 2005,2006 The FreeRADIUS server project
22 */
23RCSID("$Id: 5c21ddb0b840f2aaf33906fc314544be8ad065dd $")
24
25#define _HEAP_PRIVATE 1
26#include <freeradius-devel/util/debug.h>
27#include <freeradius-devel/util/heap.h>
28#include <freeradius-devel/util/misc.h>
29#include <freeradius-devel/util/strerror.h>
30
31#define INITIAL_CAPACITY 2048
32
33/*
34 * First node in a heap is element 1. Children of i are 2i and
35 * 2i+1. These macros wrap the logic, so the code is more
36 * descriptive.
37 */
38#define HEAP_PARENT(_x) ((_x) >> 1)
39#define HEAP_LEFT(_x) (2 * (_x))
40#define HEAP_RIGHT(_x) (2 * (_x) + 1 )
41#define HEAP_SWAP(_a, _b) do { void *_tmp = _a; _a = _b; _b = _tmp; } while (0)
42
43static int fr_heap_bubble(fr_heap_t *h, fr_heap_index_t child);
44
45/** Return how many bytes need to be allocated to hold a heap of a given size
46 *
47 * This is useful for passing to talloc[_zero]_pooled_object to avoid additional mallocs.
48 *
49 * @param[in] count The initial element count.
50 * @return The number of bytes to pre-allocate.
51 */
52size_t fr_heap_pre_alloc_size(unsigned int count)
53{
54 return sizeof(fr_heap_t) + sizeof(void *) * count;
55}
56
57fr_heap_t *_fr_heap_alloc(TALLOC_CTX *ctx, fr_heap_cmp_t cmp, char const *type, size_t offset, unsigned int init)
58{
59 fr_heap_t *h;
60
62
63 /*
64 * For small heaps (< 40 elements) the
65 * increase in memory locality gives us
66 * a 100% performance increase
67 * (talloc headers are big);
68 */
69 h = (fr_heap_t *)talloc_array(ctx, uint8_t, sizeof(fr_heap_t) + (sizeof(void *) * (init + 1)));
70 if (unlikely(!h)) return NULL;
71 talloc_set_type(h, fr_heap_t);
72
73 *h = (fr_heap_t){
74 .size = init,
75 .min = init,
76 .type = type,
77 .cmp = cmp,
78 .offset = offset
79 };
80
81 /*
82 * As we're using unsigned index values
83 * index 0 is a special value meaning
84 * that the data isn't currently inserted
85 * into the heap.
86 */
87 h->p[0] = (void *)UINTPTR_MAX;
88
89 return h;
90}
91
92static inline CC_HINT(always_inline, nonnull) fr_heap_index_t index_get(fr_heap_t *h, void *data)
93{
94 return *((fr_heap_index_t const *)(((uint8_t const *)data) + h->offset));
95}
96
97static inline CC_HINT(always_inline, nonnull) void index_set(fr_heap_t *h, void *data, fr_heap_index_t idx)
98{
99 *((fr_heap_index_t *)(((uint8_t *)data) + h->offset)) = idx;
100}
101
102#define OFFSET_SET(_heap, _idx) index_set(_heap, _heap->p[_idx], _idx)
103#define OFFSET_RESET(_heap, _idx) index_set(_heap, _heap->p[_idx], 0)
104
105static inline CC_HINT(always_inline)
106int realloc_heap(fr_heap_t **hp, unsigned int n_size)
107{
108 fr_heap_t *h = *hp;
109
110 h = (fr_heap_t *)talloc_realloc(hp, h, uint8_t, sizeof(fr_heap_t) + (sizeof(void *) * (n_size + 1)));
111 if (unlikely(!h)) {
112 fr_strerror_printf("Failed expanding heap to %u elements (%u bytes)",
113 n_size, (n_size * (unsigned int)sizeof(void *)));
114 return -1;
115 }
116 talloc_set_type(h, fr_heap_t);
117 h->size = n_size;
118
119 *hp = h;
120
121 return 0;
122}
123
124
125/** Insert a new element into the heap
126 *
127 * Insert element in heap. Normally, p != NULL, we insert p in a
128 * new position and bubble up. If p == NULL, then the element is
129 * already in place, and key is the position where to start the
130 * bubble-up.
131 *
132 * Returns -1 on failure (cannot allocate new heap entry)
133 *
134 * If offset > 0 the position (index, int) of the element in the
135 * heap is also stored in the element itself at the given offset
136 * in bytes.
137 *
138 * @param[in,out] hp The heap to extract an element from.
139 * A new pointer value will be written to hp
140 * if the heap is resized.
141 * @param[in] data Data to insert into the heap.
142 * @return
143 * - 0 on success.
144 * - -1 on failure (heap full, malloc error, or comparator error,
145 * retrieve the error with fr_strerror). On comparator error the element
146 * is in the heap but its position, and therefore the heap's
147 * ordering, is undefined.
148 */
150{
151 fr_heap_t *h = *hp;
152 fr_heap_index_t child;
153
154 if (unlikely(h == NULL)) {
155 fr_strerror_const("Heap pointer was NULL");
156 return -1;
157 }
158
159 child = index_get(h, data);
160 if (fr_heap_entry_inserted(child)) {
161 fr_strerror_const("Node is already in the heap");
162 return -1;
163 }
164
165 child = h->num_elements + 1; /* Avoid using index 0 */
166
167#ifndef TALLOC_GET_TYPE_ABORT_NOOP
168 if (h->type) (void)_talloc_get_type_abort(data, h->type, __location__);
169#endif
170
171 /*
172 * Heap is full. Double it's size.
173 */
174 if (child > h->size) {
175 unsigned int n_size;
176
177 /*
178 * heap_id is a 32-bit unsigned integer. If the heap will
179 * grow to contain more than 4B elements, disallow
180 * integer overflow. Tho TBH, that should really never
181 * happen.
182 */
183 if (unlikely(h->size > (UINT_MAX - h->size))) {
184 if (h->size == UINT_MAX) {
185 fr_strerror_const("Heap is full");
186 return -1;
187 } else {
188 n_size = UINT_MAX;
189 }
190 } else {
191 n_size = h->size * 2;
192 }
193
194 if (realloc_heap(&h, n_size) < 0) return -1;
195
196 *hp = h;
197 }
198
199 h->p[child] = data;
200 h->num_elements++;
201
202 if (unlikely(fr_heap_bubble(h, child) < 0)) return -1;
203
204 return 0;
205}
206
207static inline CC_HINT(always_inline) int fr_heap_bubble(fr_heap_t *h, fr_heap_index_t child)
208{
209 int ret = 0;
210
211 if (!fr_cond_assert(child != FR_HEAP_INDEX_INVALID)) return 0;
212
213 /*
214 * Bubble up the element.
215 */
216 while (child > 1) {
218 fr_cmp_ret_t c = h->cmp(h->p[parent], h->p[child]);
219
220 /*
221 * Stop sifting on error. The element stays where the
222 * walk stopped: the heap remains structurally valid,
223 * only its ordering is undefined.
224 */
225 if (unlikely(c == CMP_ERR)) {
226 ret = -1;
227 break;
228 }
229
230 /*
231 * Parent is smaller than the child. We're done.
232 */
233 if (c == CMP_LT) break;
234
235 /*
236 * Child is smaller than the parent, repeat.
237 */
238 HEAP_SWAP(h->p[child], h->p[parent]);
239 OFFSET_SET(h, child);
240 child = parent;
241 }
242 OFFSET_SET(h, child);
243
244 return ret;
245}
246
247/** Remove a node from the heap
248 *
249 * @param[in,out] hp The heap to extract an element from.
250 * A new pointer value will be written to hp
251 * if the heap is resized.
252 * @param[in] data Data to extract from the heap.
253 * @return
254 * - 0 on success.
255 * - -1 on failure (no elements, data not found, or comparator error,
256 * retrieve the error with fr_strerror). On comparator error the element
257 * has still been extracted, but the heap's ordering is undefined.
258 */
260{
261 fr_heap_t *h = *hp;
262 fr_heap_index_t parent, child, max;
263 int ret = 0;
264
265 if (unlikely(h == NULL)) {
266 fr_strerror_const("Heap pointer was NULL");
267 return -1;
268 }
269
270 /*
271 * Extract element.
272 */
273 parent = index_get(h, data);
274
275 /*
276 * Out of bounds.
277 */
278 if (unlikely((parent == 0) || (parent > h->num_elements))) {
279 fr_strerror_printf("Heap parent (%u) out of bounds (0-%u)", parent, h->num_elements);
280 return -1;
281 }
282
283 if (unlikely(data != h->p[parent])) {
284 fr_strerror_printf("Invalid heap index. Expected data %p at offset %u, got %p", data,
285 parent, h->p[parent]);
286 return -1;
287 }
288 max = h->num_elements;
289
290 child = HEAP_LEFT(parent);
292 while (child <= max) {
293 /*
294 * Maybe take the right child.
295 *
296 * On comparator error keep taking the left child so the
297 * hole still walks to the bottom: extraction completes
298 * structurally, only the ordering is undefined.
299 */
300 if (child != max) {
301 fr_cmp_ret_t c = h->cmp(h->p[child + 1], h->p[child]);
302
303 if (unlikely(c == CMP_ERR)) {
304 ret = -1;
305 } else if (c == CMP_LT) {
306 child = child + 1;
307 }
308 }
309 h->p[parent] = h->p[child];
310 OFFSET_SET(h, parent);
311 parent = child;
312 child = HEAP_LEFT(child);
313 }
314 h->num_elements--;
315
316 /*
317 * We didn't end up at the last element in the heap.
318 * This element has to be re-inserted.
319 */
320 if (parent != max) {
321 /*
322 * Fill hole with last entry and bubble up,
323 * reusing the insert code
324 */
325 h->p[parent] = h->p[max];
326
327 if (unlikely(fr_heap_bubble(h, parent) < 0)) ret = -1;
328 }
329 if (unlikely(ret < 0)) return -1;
330
331 /*
332 * After re-building the heap, check the new size. If
333 * the heap is less than a third full, shrink it by half.
334 *
335 * Note that we don't check for half full, in order to
336 * avoid hysteresis around doubling / halving the heap.
337 */
338 if ((h->num_elements * 3) < h->size) {
339 unsigned int n_size = ROUND_UP_DIV(h->size, 2);
340
341 if ((n_size > h->min) && (realloc_heap(&h, n_size)) == 0) *hp = h;
342 }
343
344 return 0;
345}
346
347/** Remove a node from the heap
348 *
349 * @param[out] out the head element, or NULL if the heap is empty.
350 * @param[in,out] hp The heap to pop an element from.
351 * A new pointer value will be written to hp
352 * if the heap is resized.
353 * @return
354 * - 0 on success, check out for the popped element.
355 * - -1 on comparator error, retrieve the error with fr_strerror. The head
356 * element has still been popped, but the heap's ordering is
357 * undefined.
358 */
359int fr_heap_pop(void **out, fr_heap_t **hp)
360{
361 fr_heap_t *h = *hp;
362 void *data;
363
364 *out = NULL;
365
366 if (unlikely(h == NULL)) {
367 fr_strerror_const("Heap pointer was NULL");
368 return -1;
369 }
370
371 if (h->num_elements == 0) return 0;
372
373 data = h->p[1];
374 if (unlikely(fr_heap_extract(hp, data) < 0)) return -1;
375
376 *out = data;
377 return 0;
378}
379
380/** Iterate over entries in heap
381 *
382 * @note If the heap is modified the iterator should be considered invalidated.
383 *
384 * @param[in] h to iterate over.
385 * @param[in] iter Pointer to an iterator struct, used to maintain
386 * state between calls.
387 * @return
388 * - User data.
389 * - NULL if at the end of the list.
390 */
392{
393 *iter = 1;
394
395 if (h->num_elements == 0) return NULL;
396
397 return h->p[1];
398}
399
400/** Get the next entry in a heap
401 *
402 * @note If the heap is modified the iterator should be considered invalidated.
403 *
404 * @param[in] h to iterate over.
405 * @param[in] iter Pointer to an iterator struct, used to maintain
406 * state between calls.
407 * @return
408 * - User data.
409 * - NULL if at the end of the list.
410 */
412{
413 if ((*iter + 1) > h->num_elements) return NULL;
414 *iter += 1;
415
416 return h->p[*iter];
417}
418
419#ifndef TALLOC_GET_TYPE_ABORT_NOOP
420void fr_heap_verify(char const *file, int line, fr_heap_t *h)
421{
422 fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: fr_heap_t pointer was NULL", file, line);
423 (void) talloc_get_type_abort(h, fr_heap_t);
424
425 /*
426 * Allocating the heap structure and the array holding the heap as described in data structure
427 * texts together is a respectable savings, but it means adding a level of indirection so the
428 * fr_heap_t * isn't realloc()ed out from under the user, hence the following (and the use of h
429 * rather than hp to access anything in the heap structure).
430 */
431 fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: heap_t pointer was NULL", file, line);
432 (void) talloc_get_type_abort(h, fr_heap_t);
433
435 "CONSISTENCY CHECK FAILED %s[%i]: num_elements exceeds size", file, line);
436
437 fr_fatal_assert_msg(h->p[0] == (void *)UINTPTR_MAX,
438 "CONSISTENCY CHECK FAILED %s[%i]: zeroeth element special value overwritten", file, line);
439
440 for (unsigned int i = 1; i <= h->num_elements; i++) {
441 void *data = h->p[i];
442
443 fr_fatal_assert_msg(data, "CONSISTENCY CHECK FAILED %s[%i]: node %u was NULL", file, line, i);
444 if (h->type) (void)_talloc_get_type_abort(data, h->type, __location__);
446 "CONSISTENCY CHECK FAILED %s[%i]: node %u index != %u", file, line, i, i);
447 }
448 for (unsigned int i = 1; ; i++) {
449 fr_cmp_ret_t c;
450
451 if (HEAP_LEFT(i) > h->num_elements) break;
452 c = h->cmp(h->p[i], h->p[HEAP_LEFT(i)]);
454 "CONSISTENCY_CHECK_FAILED %s[%i]: comparator error: %s", file, line, fr_strerror());
456 "CONSISTENCY_CHECK_FAILED %s[%i]: node %u > left child", file, line, i);
457 if (HEAP_RIGHT(i) > h->num_elements) break;
458 c = h->cmp(h->p[i], h->p[HEAP_RIGHT(i)]);
460 "CONSISTENCY_CHECK_FAILED %s[%i]: comparator error: %s", file, line, fr_strerror());
462 "CONSISTENCY_CHECK_FAILED %s[%i]: node %u > right child", file, line, i);
463 }
464}
465#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
#define RCSID(id)
Definition build.h:560
#define unlikely(_x)
Definition build.h:455
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
#define fr_fatal_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:176
void * fr_heap_iter_init(fr_heap_t *h, fr_heap_iter_t *iter)
Iterate over entries in heap.
Definition heap.c:391
#define HEAP_PARENT(_x)
Definition heap.c:38
void * fr_heap_iter_next(fr_heap_t *h, fr_heap_iter_t *iter)
Get the next entry in a heap.
Definition heap.c:411
#define OFFSET_SET(_heap, _idx)
Definition heap.c:102
static void index_set(fr_heap_t *h, void *data, fr_heap_index_t idx)
Definition heap.c:97
static fr_heap_index_t index_get(fr_heap_t *h, void *data)
Definition heap.c:92
#define INITIAL_CAPACITY
Definition heap.c:31
#define OFFSET_RESET(_heap, _idx)
Definition heap.c:103
static int realloc_heap(fr_heap_t **hp, unsigned int n_size)
Definition heap.c:106
size_t fr_heap_pre_alloc_size(unsigned int count)
Return how many bytes need to be allocated to hold a heap of a given size.
Definition heap.c:52
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition heap.c:149
int fr_heap_pop(void **out, fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:359
#define HEAP_SWAP(_a, _b)
Definition heap.c:41
#define HEAP_LEFT(_x)
Definition heap.c:39
int fr_heap_extract(fr_heap_t **hp, void *data)
Remove a node from the heap.
Definition heap.c:259
static int fr_heap_bubble(fr_heap_t *h, fr_heap_index_t child)
Definition heap.c:207
#define HEAP_RIGHT(_x)
Definition heap.c:40
fr_heap_t * _fr_heap_alloc(TALLOC_CTX *ctx, fr_heap_cmp_t cmp, char const *type, size_t offset, unsigned int init)
Definition heap.c:57
void fr_heap_verify(char const *file, int line, fr_heap_t *h)
Definition heap.c:420
unsigned int fr_heap_index_t
Definition heap.h:82
char const *_CONST type
Talloc type of elements.
Definition heap.h:76
unsigned int _CONST min
Minimum number of elements we allow the heap to reduce down to.
Definition heap.h:70
unsigned int fr_heap_iter_t
Definition heap.h:83
unsigned int _CONST size
Number of nodes allocated.
Definition heap.h:69
unsigned int _CONST num_elements
Number of nodes used.
Definition heap.h:74
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition heap.h:126
void *_CONST p[]
Array of nodes.
Definition heap.h:79
fr_heap_cmp_t _CONST cmp
Comparator function.
Definition heap.h:77
fr_cmp_ret_t(* fr_heap_cmp_t)(void const *a, void const *b)
Comparator to order heap elements.
Definition heap.h:56
#define FR_HEAP_INDEX_INVALID
Definition heap.h:85
size_t _CONST offset
Offset of heap index in element structure.
Definition heap.h:72
The main heap structure.
Definition heap.h:68
#define ROUND_UP_DIV(_x, _y)
Get the ceiling value of integer division.
Definition math.h:211
unsigned char uint8_t
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
@ CMP_GT
a > b
Definition misc.h:54
@ CMP_LT
a < b
Definition misc.h:52
@ CMP_ERR
comparison failed
Definition misc.h:51
return count
Definition module.c:155
init
Enter the EAP-IDENTITY state.
fr_aka_sim_id_type_t type
static fr_slen_t parent
Definition pair.h:858
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1340
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1030