The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 3dd1951fea6f14a3e5586dea38ab7b7d32fc96b0 $")
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 void 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 or malloc error).
145 */
147{
148 fr_heap_t *h = *hp;
149 fr_heap_index_t child;
150
151 if (unlikely(h == NULL)) {
152 fr_strerror_const("Heap pointer was NULL");
153 return -1;
154 }
155
156 child = index_get(h, data);
157 if (fr_heap_entry_inserted(child)) {
158 fr_strerror_const("Node is already in the heap");
159 return -1;
160 }
161
162 child = h->num_elements + 1; /* Avoid using index 0 */
163
164#ifndef TALLOC_GET_TYPE_ABORT_NOOP
165 if (h->type) (void)_talloc_get_type_abort(data, h->type, __location__);
166#endif
167
168 /*
169 * Heap is full. Double it's size.
170 */
171 if (child > h->size) {
172 unsigned int n_size;
173
174 /*
175 * heap_id is a 32-bit unsigned integer. If the heap will
176 * grow to contain more than 4B elements, disallow
177 * integer overflow. Tho TBH, that should really never
178 * happen.
179 */
180 if (unlikely(h->size > (UINT_MAX - h->size))) {
181 if (h->size == UINT_MAX) {
182 fr_strerror_const("Heap is full");
183 return -1;
184 } else {
185 n_size = UINT_MAX;
186 }
187 } else {
188 n_size = h->size * 2;
189 }
190
191 if (realloc_heap(&h, n_size) < 0) return -1;
192
193 *hp = h;
194 }
195
196 h->p[child] = data;
197 h->num_elements++;
198
199 fr_heap_bubble(h, child);
200
201 return 0;
202}
203
204static inline CC_HINT(always_inline) void fr_heap_bubble(fr_heap_t *h, fr_heap_index_t child)
205{
206 if (!fr_cond_assert(child != FR_HEAP_INDEX_INVALID)) return;
207
208 /*
209 * Bubble up the element.
210 */
211 while (child > 1) {
213
214 /*
215 * Parent is smaller than the child. We're done.
216 */
217 if (h->cmp(h->p[parent], h->p[child]) < 0) break;
218
219 /*
220 * Child is smaller than the parent, repeat.
221 */
222 HEAP_SWAP(h->p[child], h->p[parent]);
223 OFFSET_SET(h, child);
224 child = parent;
225 }
226 OFFSET_SET(h, child);
227}
228
229/** Remove a node from the heap
230 *
231 * @param[in,out] hp The heap to extract an element from.
232 * A new pointer value will be written to hp
233 * if the heap is resized.
234 * @param[in] data Data to extract from the heap.
235 * @return
236 * - 0 on success.
237 * - -1 on failure (no elements or data not found).
238 */
240{
241 fr_heap_t *h = *hp;
242 fr_heap_index_t parent, child, max;
243
244 if (unlikely(h == NULL)) {
245 fr_strerror_const("Heap pointer was NULL");
246 return -1;
247 }
248
249 /*
250 * Extract element.
251 */
252 parent = index_get(h, data);
253
254 /*
255 * Out of bounds.
256 */
257 if (unlikely((parent == 0) || (parent > h->num_elements))) {
258 fr_strerror_printf("Heap parent (%u) out of bounds (0-%u)", parent, h->num_elements);
259 return -1;
260 }
261
262 if (unlikely(data != h->p[parent])) {
263 fr_strerror_printf("Invalid heap index. Expected data %p at offset %u, got %p", data,
264 parent, h->p[parent]);
265 return -1;
266 }
267 max = h->num_elements;
268
269 child = HEAP_LEFT(parent);
271 while (child <= max) {
272 /*
273 * Maybe take the right child.
274 */
275 if ((child != max) &&
276 (h->cmp(h->p[child + 1], h->p[child]) < 0)) {
277 child = child + 1;
278 }
279 h->p[parent] = h->p[child];
280 OFFSET_SET(h, parent);
281 parent = child;
282 child = HEAP_LEFT(child);
283 }
284 h->num_elements--;
285
286 /*
287 * If the number of elements in the heap is half
288 * what we need, shrink the heap back.
289 */
290 if ((h->num_elements * 2) < h->size) {
291 unsigned int n_size = ROUND_UP_DIV(h->size, 2);
292
293 if ((n_size > h->min) && (realloc_heap(&h, n_size)) == 0) *hp = h;
294 }
295
296 /*
297 * We didn't end up at the last element in the heap.
298 * This element has to be re-inserted.
299 */
300 if (parent != max) {
301 /*
302 * Fill hole with last entry and bubble up,
303 * reusing the insert code
304 */
305 h->p[parent] = h->p[max];
306
308 }
309
310 return 0;
311}
312
313/** Remove a node from the heap
314 *
315 * @param[in,out] hp The heap to pop an element from.
316 * A new pointer value will be written to hp
317 * if the heap is resized.
318 * @return
319 * - The item that was popped.
320 * - NULL on error.
321 */
323{
324 fr_heap_t *h = *hp;
325 void *data;
326
327 if (unlikely(h == NULL)) {
328 fr_strerror_const("Heap pointer was NULL");
329 return NULL;
330 }
331
332 if (h->num_elements == 0) return NULL;
333
334 data = h->p[1];
335 if (unlikely(fr_heap_extract(hp, data) < 0)) return NULL;
336
337 return data;
338}
339
340/** Iterate over entries in heap
341 *
342 * @note If the heap is modified the iterator should be considered invalidated.
343 *
344 * @param[in] h to iterate over.
345 * @param[in] iter Pointer to an iterator struct, used to maintain
346 * state between calls.
347 * @return
348 * - User data.
349 * - NULL if at the end of the list.
350 */
352{
353 *iter = 1;
354
355 if (h->num_elements == 0) return NULL;
356
357 return h->p[1];
358}
359
360/** Get the next entry in a heap
361 *
362 * @note If the heap is modified the iterator should be considered invalidated.
363 *
364 * @param[in] h to iterate over.
365 * @param[in] iter Pointer to an iterator struct, used to maintain
366 * state between calls.
367 * @return
368 * - User data.
369 * - NULL if at the end of the list.
370 */
372{
373 if ((*iter + 1) > h->num_elements) return NULL;
374 *iter += 1;
375
376 return h->p[*iter];
377}
378
379#ifndef TALLOC_GET_TYPE_ABORT_NOOP
380void fr_heap_verify(char const *file, int line, fr_heap_t *h)
381{
382 fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: fr_heap_t pointer was NULL", file, line);
383 (void) talloc_get_type_abort(h, fr_heap_t);
384
385 /*
386 * Allocating the heap structure and the array holding the heap as described in data structure
387 * texts together is a respectable savings, but it means adding a level of indirection so the
388 * fr_heap_t * isn't realloc()ed out from under the user, hence the following (and the use of h
389 * rather than hp to access anything in the heap structure).
390 */
391 fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: heap_t pointer was NULL", file, line);
392 (void) talloc_get_type_abort(h, fr_heap_t);
393
395 "CONSISTENCY CHECK FAILED %s[%i]: num_elements exceeds size", file, line);
396
397 fr_fatal_assert_msg(h->p[0] == (void *)UINTPTR_MAX,
398 "CONSISTENCY CHECK FAILED %s[%i]: zeroeth element special value overwritten", file, line);
399
400 for (unsigned int i = 1; i <= h->num_elements; i++) {
401 void *data = h->p[i];
402
403 fr_fatal_assert_msg(data, "CONSISTENCY CHECK FAILED %s[%i]: node %u was NULL", file, line, i);
404 if (h->type) (void)_talloc_get_type_abort(data, h->type, __location__);
406 "CONSISTENCY CHECK FAILED %s[%i]: node %u index != %u", file, line, i, i);
407 }
408 for (unsigned int i = 1; ; i++) {
409 if (HEAP_LEFT(i) > h->num_elements) break;
410 fr_fatal_assert_msg(h->cmp(h->p[i], h->p[HEAP_LEFT(i)]) <= 0,
411 "CONSISTENCY_CHECK_FAILED %s[%i]: node %u > left child", file, line, i);
412 if (HEAP_RIGHT(i) > h->num_elements) break;
413 fr_fatal_assert_msg(h->cmp(h->p[i], h->p[HEAP_RIGHT(i)]) <= 0,
414 "CONSISTENCY_CHECK_FAILED %s[%i]: node %u > right child", file, line, i);
415 }
416}
417#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
static bool init
Definition fuzzer.c:41
#define RCSID(id)
Definition build.h:483
#define unlikely(_x)
Definition build.h:381
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#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:184
void * fr_heap_iter_init(fr_heap_t *h, fr_heap_iter_t *iter)
Iterate over entries in heap.
Definition heap.c:351
#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:371
#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:146
void * fr_heap_pop(fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:322
#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:239
static void fr_heap_bubble(fr_heap_t *h, fr_heap_index_t child)
Definition heap.c:204
#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:380
unsigned int fr_heap_index_t
Definition heap.h:80
char const *_CONST type
Talloc type of elements.
Definition heap.h:74
unsigned int _CONST min
Minimum number of elements we allow the heap to reduce down to.
Definition heap.h:68
unsigned int fr_heap_iter_t
Definition heap.h:81
unsigned int _CONST size
Number of nodes allocated.
Definition heap.h:67
unsigned int _CONST num_elements
Number of nodes used.
Definition heap.h:72
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition heap.h:124
void *_CONST p[]
Array of nodes.
Definition heap.h:77
fr_heap_cmp_t _CONST cmp
Comparator function.
Definition heap.h:75
#define FR_HEAP_INDEX_INVALID
Definition heap.h:83
int8_t(* fr_heap_cmp_t)(void const *a, void const *b)
Comparator to order heap elements.
Definition heap.h:54
size_t _CONST offset
Offset of heap index in element structure.
Definition heap.h:70
The main heap structure.
Definition heap.h:66
#define ROUND_UP_DIV(_x, _y)
Get the ceiling value of integer division.
Definition math.h:153
unsigned char uint8_t
return count
Definition module.c:163
fr_aka_sim_id_type_t type
static fr_slen_t parent
Definition pair.h:851
#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:1265
int nonnull(2, 5))