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: ee1f6e1bd54c22765f4e2d5c7504a15507c22da9 $")
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 * We didn't end up at the last element in the heap.
288 * This element has to be re-inserted.
289 */
290 if (parent != max) {
291 /*
292 * Fill hole with last entry and bubble up,
293 * reusing the insert code
294 */
295 h->p[parent] = h->p[max];
296
298 }
299
300 /*
301 * After re-building the heap, check the new size. If
302 * the heap is less than a third full, shrink it by half.
303 *
304 * Note that we don't check for half full, in order to
305 * avoid hysteresis around doubling / halving the heap.
306 */
307 if ((h->num_elements * 3) < h->size) {
308 unsigned int n_size = ROUND_UP_DIV(h->size, 2);
309
310 if ((n_size > h->min) && (realloc_heap(&h, n_size)) == 0) *hp = h;
311 }
312
313 return 0;
314}
315
316/** Remove a node from the heap
317 *
318 * @param[in,out] hp The heap to pop an element from.
319 * A new pointer value will be written to hp
320 * if the heap is resized.
321 * @return
322 * - The item that was popped.
323 * - NULL on error.
324 */
326{
327 fr_heap_t *h = *hp;
328 void *data;
329
330 if (unlikely(h == NULL)) {
331 fr_strerror_const("Heap pointer was NULL");
332 return NULL;
333 }
334
335 if (h->num_elements == 0) return NULL;
336
337 data = h->p[1];
338 if (unlikely(fr_heap_extract(hp, data) < 0)) return NULL;
339
340 return data;
341}
342
343/** Iterate over entries in heap
344 *
345 * @note If the heap is modified the iterator should be considered invalidated.
346 *
347 * @param[in] h to iterate over.
348 * @param[in] iter Pointer to an iterator struct, used to maintain
349 * state between calls.
350 * @return
351 * - User data.
352 * - NULL if at the end of the list.
353 */
355{
356 *iter = 1;
357
358 if (h->num_elements == 0) return NULL;
359
360 return h->p[1];
361}
362
363/** Get the next entry in a heap
364 *
365 * @note If the heap is modified the iterator should be considered invalidated.
366 *
367 * @param[in] h to iterate over.
368 * @param[in] iter Pointer to an iterator struct, used to maintain
369 * state between calls.
370 * @return
371 * - User data.
372 * - NULL if at the end of the list.
373 */
375{
376 if ((*iter + 1) > h->num_elements) return NULL;
377 *iter += 1;
378
379 return h->p[*iter];
380}
381
382#ifndef TALLOC_GET_TYPE_ABORT_NOOP
383void fr_heap_verify(char const *file, int line, fr_heap_t *h)
384{
385 fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: fr_heap_t pointer was NULL", file, line);
386 (void) talloc_get_type_abort(h, fr_heap_t);
387
388 /*
389 * Allocating the heap structure and the array holding the heap as described in data structure
390 * texts together is a respectable savings, but it means adding a level of indirection so the
391 * fr_heap_t * isn't realloc()ed out from under the user, hence the following (and the use of h
392 * rather than hp to access anything in the heap structure).
393 */
394 fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: heap_t pointer was NULL", file, line);
395 (void) talloc_get_type_abort(h, fr_heap_t);
396
398 "CONSISTENCY CHECK FAILED %s[%i]: num_elements exceeds size", file, line);
399
400 fr_fatal_assert_msg(h->p[0] == (void *)UINTPTR_MAX,
401 "CONSISTENCY CHECK FAILED %s[%i]: zeroeth element special value overwritten", file, line);
402
403 for (unsigned int i = 1; i <= h->num_elements; i++) {
404 void *data = h->p[i];
405
406 fr_fatal_assert_msg(data, "CONSISTENCY CHECK FAILED %s[%i]: node %u was NULL", file, line, i);
407 if (h->type) (void)_talloc_get_type_abort(data, h->type, __location__);
409 "CONSISTENCY CHECK FAILED %s[%i]: node %u index != %u", file, line, i, i);
410 }
411 for (unsigned int i = 1; ; i++) {
412 if (HEAP_LEFT(i) > h->num_elements) break;
413 fr_fatal_assert_msg(h->cmp(h->p[i], h->p[HEAP_LEFT(i)]) <= 0,
414 "CONSISTENCY_CHECK_FAILED %s[%i]: node %u > left child", file, line, i);
415 if (HEAP_RIGHT(i) > h->num_elements) break;
416 fr_fatal_assert_msg(h->cmp(h->p[i], h->p[HEAP_RIGHT(i)]) <= 0,
417 "CONSISTENCY_CHECK_FAILED %s[%i]: node %u > right child", file, line, i);
418 }
419}
420#endif
int const char * file
Definition acutest.h:704
int const char int line
Definition acutest.h:704
static bool init
Definition fuzzer.c:42
#define RCSID(id)
Definition build.h:487
#define unlikely(_x)
Definition build.h:383
#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:354
#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:374
#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:325
#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:383
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:211
unsigned char uint8_t
return count
Definition module.c:155
fr_aka_sim_id_type_t type
static fr_slen_t parent
Definition pair.h:858
#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:1334
int nonnull(2, 5))