The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
minmax_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 minmax heap
18 *
19 * @file src/lib/util/minmax_heap.c
20 *
21 * @copyright 2021 Network RADIUS SAS (legal@networkradius.com)
22 */
23RCSID("$Id: 4ca3c1edec829ae89977fdfc0c3a459cdd92d7b9 $")
24
25#include <freeradius-devel/util/minmax_heap.h>
26#include <freeradius-devel/util/strerror.h>
27#include <freeradius-devel/util/debug.h>
28#include <freeradius-devel/util/misc.h>
29
30/*
31 * The internal representation of minmax heaps is that of plain
32 * binary heaps. They differ in where entries are placed, and how
33 * the operations are done. Also, minmax heaps allow peeking or
34 * popping the maximum value as well as the minimum.
35 *
36 * The heap itself is an array of pointers to objects, each of which
37 * contains a key and an fr_minmax_heap_index_t value indicating the
38 * location in the array holding the pointer to it. To allow 0 to
39 * represent objects not in a heap, the pointers start at element
40 * one of the array rather than element zero. The offset of that
41 * fr_minmax_heap_index_t value is held inside the heap structure.
42 *
43 * Minmax heaps are trees, like binary heaps, but the levels (all
44 * values at the same depth) alternate between "min" (starting at
45 * depth 0, i.e. the root) and "max" levels. The operations preserve
46 * these properties:
47 * - A node on a min level will compare as less than or equal to any
48 * of its descendants.
49 * - A node on a max level will compare as greater than or equal to
50 * any of its descendants.
51 */
52
54 unsigned int size; //!< Number of nodes allocated.
55 size_t offset; //!< Offset of heap index in element structure.
56
57 unsigned int num_elements; //!< Number of nodes used.
58
59 char const *type; //!< Talloc type of elements.
60 fr_minmax_heap_cmp_t cmp; //!< Comparator function.
61
62 void *p[]; //!< Array of nodes.
63};
64
66
67#define INITIAL_CAPACITY 2048
68
69/*
70 * First node in a heap is element 1. Children of i are 2i and
71 * 2i+1. These macros wrap the logic, so the code is more
72 * descriptive.
73 */
74#define HEAP_PARENT(_x) ((_x) >> 1)
75#define HEAP_GRANDPARENT(_x) HEAP_PARENT(HEAP_PARENT(_x))
76#define HEAP_LEFT(_x) (2 * (_x))
77#define HEAP_RIGHT(_x) (2 * (_x) + 1 )
78#define HEAP_SWAP(_a, _b) do { void *_tmp = _a; _a = _b; _b = _tmp; } while (0)
79
80/**
81 * @hidecallergraph
82 */
84{
85 return fr_high_bit_pos(i) - 1;
86}
87
89{
90 return (depth(i) & 1) == 0;
91}
92
93static inline bool is_descendant(fr_minmax_heap_index_t candidate, fr_minmax_heap_index_t ancestor)
94{
95 fr_minmax_heap_index_t level_min;
96 uint8_t candidate_depth = depth(candidate);
97 uint8_t ancestor_depth = depth(ancestor);
98
99 /*
100 * This will never happen given the its use by fr_minmax_heap_extract(),
101 * but it's here for safety and to make static analysis happy.
102 */
103 if (unlikely(candidate_depth < ancestor_depth)) return false;
104
105 level_min = ((fr_minmax_heap_index_t) 1) << (candidate_depth - ancestor_depth);
106 return (candidate - level_min) < level_min;
107}
108
109#define is_max_level_index(_i) (!(is_min_level_index(_i)))
110
111fr_minmax_heap_t *_fr_minmax_heap_alloc(TALLOC_CTX *ctx, fr_minmax_heap_cmp_t cmp, char const *type, size_t offset, unsigned int init)
112{
114 minmax_heap_t *h;
115
116 if (!init) init = INITIAL_CAPACITY;
117
118 hp = talloc(ctx, fr_minmax_heap_t);
119 if (unlikely(!hp)) return NULL;
120
121 /*
122 * For small heaps (< 40 elements) the
123 * increase in memory locality gives us
124 * a 100% performance increase
125 * (talloc headers are big);
126 */
127 h = (minmax_heap_t *)talloc_array(hp, uint8_t, sizeof(minmax_heap_t) + (sizeof(void *) * (init + 1)));
128 if (unlikely(!h)) {
129 talloc_free(hp);
130 return NULL;
131 }
132 talloc_set_type(h, minmax_heap_t);
133
134 *h = (minmax_heap_t){
135 .size = init,
136 .type = type,
137 .cmp = cmp,
138 .offset = offset
139 };
140
141 /*
142 * As we're using unsigned index values
143 * index 0 is a special value meaning
144 * that the data isn't currently inserted
145 * into the heap.
146 */
147 h->p[0] = (void *)UINTPTR_MAX;
148
149 *hp = h;
150
151 return hp;
152}
153
155{
156 minmax_heap_t *h = *hp;
157 unsigned int n_size;
158
159 /*
160 * One will almost certainly run out of RAM first,
161 * but the size must be representable. This form
162 * of the check avoids overflow.
163 */
164 if (unlikely(h->size > UINT_MAX - h->size)) {
165 if (h->size == UINT_MAX) {
166 fr_strerror_const("Heap is full");
167 return -1;
168 }
169 n_size = UINT_MAX;
170 } else {
171 n_size = 2 * h->size;
172 }
173
174 h = (minmax_heap_t *)talloc_realloc(hp, h, uint8_t, sizeof(minmax_heap_t) + (sizeof(void *) * ((size_t)n_size + 1)));
175 if (unlikely(!h)) {
176 fr_strerror_printf("Failed expanding heap to %u elements (%zu bytes)",
177 n_size, (n_size * sizeof(void *)));
178 return -1;
179 }
180
181 talloc_set_type(h, minmax_heap_t);
182 h->size = n_size;
183 *hp = h;
184 return 0;
185}
186
187
188static inline CC_HINT(always_inline, nonnull) fr_minmax_heap_index_t index_get(minmax_heap_t *h, void *data)
189{
190 return *((fr_minmax_heap_index_t const *)(((uint8_t const *)data) + h->offset));
191}
192
193static inline CC_HINT(always_inline, nonnull) void index_set(minmax_heap_t *h, void *data, fr_minmax_heap_index_t idx)
194{
195 *((fr_minmax_heap_index_t *)(((uint8_t *)data) + h->offset)) = idx;
196}
197
198static inline CC_HINT(always_inline, nonnull) bool has_children(minmax_heap_t *h, fr_minmax_heap_index_t idx)
199{
200 return HEAP_LEFT(idx) <= h->num_elements;
201}
202
204{
205 return HEAP_LEFT(HEAP_LEFT(i)) <= h->num_elements;
206}
207
208#define OFFSET_SET(_heap, _idx) index_set(_heap, _heap->p[_idx], _idx)
209#define OFFSET_RESET(_heap, _idx) index_set(_heap, _heap->p[_idx], 0)
210
211/*
212 * The minmax heap has the same basic idea as binary heaps:
213 * 1. To insert a value, put it at the bottom and push it up to where it should be.
214 * 2. To remove a value, take it out; if it's not at the bottom, move what is at the
215 * bottom up to fill the hole, and push it down to where it should be.
216 * The difference is how you push, and the invariants to preserve.
217 *
218 * Since we store the index in the item (or zero if it's not in the heap), when we
219 * move an item around, we have to set its index. The general principle is that we
220 * set it when we put the item in the place it will ultimately be when the push_down()
221 * or push_up() is finished.
222 */
223
224/** Find the index of the minimum child or grandchild of the entry at a given index.
225 * precondition: has_children(h, idx), i.e. there is stuff in the heap below
226 * idx.
227 *
228 * These functions are called by push_down_{min, max}() with idx the index of
229 * an element moved into that position but which may or may not be where it
230 * should ultimately go. The minmax heap property still holds for its (positional,
231 * at least) descendants, though. That lets us cut down on the number of
232 * comparisons over brute force iteration over every child and grandchild.
233 *
234 * In the case where the desired item must be a child, there are at most two,
235 * so we just do it inlne; no loop needed.
236 */
238{
239 fr_minmax_heap_index_t lwb, upb, min;
240 fr_cmp_ret_t c;
241 int ret = 0;
242
243 if (is_max_level_index(idx) || !has_grandchildren(h, idx)) {
244 /* minimum must be a chld */
245 min = HEAP_LEFT(idx);
246 upb = HEAP_RIGHT(idx);
247 if (upb <= h->num_elements) {
248 c = h->cmp(h->p[upb], h->p[min]);
249 if (unlikely(c == CMP_ERR)) ret = -1;
250 if (c == CMP_LT) min = upb;
251 }
252 *out = min;
253 return ret;
254 }
255
256 /* minimum must be a grandchild, unless the right child is childless */
257 if (!has_children(h, HEAP_RIGHT(idx))) {
258 min = HEAP_RIGHT(idx);
259 lwb = HEAP_LEFT(HEAP_LEFT(idx));
260 } else {
261 min = HEAP_LEFT(HEAP_LEFT(idx));
262 lwb = min + 1;
263 }
264 upb = HEAP_RIGHT(HEAP_RIGHT(idx));
265
266 /* Some grandchildren may not exist. */
267 if (upb > h->num_elements) upb = h->num_elements;
268
269 for (fr_minmax_heap_index_t i = lwb; i <= upb; i++) {
270 c = h->cmp(h->p[i], h->p[min]);
271 if (unlikely(c == CMP_ERR)) ret = -1;
272 if (c == CMP_LT) min = i;
273 }
274 *out = min;
275 return ret;
276}
277
279{
280 fr_minmax_heap_index_t lwb, upb, max;
281 fr_cmp_ret_t c;
282 int ret = 0;
283
284 if (is_min_level_index(idx) || !has_grandchildren(h, idx)) {
285 /* maximum must be a chld */
286 max = HEAP_LEFT(idx);
287 upb = HEAP_RIGHT(idx);
288 if (upb <= h->num_elements) {
289 c = h->cmp(h->p[upb], h->p[max]);
290 if (unlikely(c == CMP_ERR)) ret = -1;
291 if (c == CMP_GT) max = upb;
292 }
293 *out = max;
294 return ret;
295 }
296
297 /* minimum must be a grandchild, unless the right child is childless */
298 if (!has_children(h, HEAP_RIGHT(idx))) {
299 max = HEAP_RIGHT(idx);
300 lwb = HEAP_LEFT(HEAP_LEFT(idx));
301 } else {
302 max = HEAP_LEFT(HEAP_LEFT(idx));
303 lwb = max + 1;
304 }
305 upb = HEAP_RIGHT(HEAP_RIGHT(idx));
306
307 /* Some grandchildren may not exist. */
308 if (upb > h->num_elements) upb = h->num_elements;
309
310 for (fr_minmax_heap_index_t i = lwb; i <= upb; i++) {
311 c = h->cmp(h->p[i], h->p[max]);
312 if (unlikely(c == CMP_ERR)) ret = -1;
313 if (c == CMP_GT) max = i;
314 }
315 *out = max;
316 return ret;
317}
318
319/**
320 * precondition: idx is the index of an existing entry on a min level
321 */
322static inline CC_HINT(always_inline, nonnull) int push_down_min(minmax_heap_t *h, fr_minmax_heap_index_t idx)
323{
324 int ret = 0;
325
326 while (has_children(h, idx)) {
328 fr_cmp_ret_t c;
329
330 /*
331 * Stop sifting on error. The element stays where the
332 * walk stopped: the heap remains structurally valid,
333 * only its ordering is undefined.
334 */
335 if (unlikely(min_child_or_grandchild(&m, h, idx) < 0)) {
336 ret = -1;
337 break;
338 }
339 c = h->cmp(h->p[m], h->p[idx]);
340 if (unlikely(c == CMP_ERR)) {
341 ret = -1;
342 break;
343 }
344
345 /*
346 * If p[m] doesn't precede p[idx], we're done.
347 */
348 if (c != CMP_LT) break;
349
350 HEAP_SWAP(h->p[idx], h->p[m]);
351 OFFSET_SET(h, idx);
352
353 /*
354 * The entry now at m may belong where the parent is.
355 */
356 if (HEAP_GRANDPARENT(m) == idx) {
357 c = h->cmp(h->p[m], h->p[HEAP_PARENT(m)]);
358 if (unlikely(c == CMP_ERR)) {
359 ret = -1;
360 break;
361 }
362 if (c == CMP_GT) {
363 HEAP_SWAP(h->p[HEAP_PARENT(m)], h->p[m]);
364 OFFSET_SET(h, HEAP_PARENT(m));
365 }
366 }
367 idx = m;
368 }
369 OFFSET_SET(h, idx);
370
371 return ret;
372}
373
374/**
375 * precondition: idx is the index of an existing entry on a max level
376 * (Just like push_down_min() save for reversal of ordering, so comments there apply,
377 * mutatis mutandis.)
378 */
380{
381 int ret = 0;
382
383 while (has_children(h, idx)) {
385 fr_cmp_ret_t c;
386
387 if (unlikely(max_child_or_grandchild(&m, h, idx) < 0)) {
388 ret = -1;
389 break;
390 }
391 c = h->cmp(h->p[m], h->p[idx]);
392 if (unlikely(c == CMP_ERR)) {
393 ret = -1;
394 break;
395 }
396
397 if (c != CMP_GT) break;
398
399 HEAP_SWAP(h->p[idx], h->p[m]);
400 OFFSET_SET(h, idx);
401
402 if (HEAP_GRANDPARENT(m) == idx) {
403 c = h->cmp(h->p[m], h->p[HEAP_PARENT(m)]);
404 if (unlikely(c == CMP_ERR)) {
405 ret = -1;
406 break;
407 }
408 if (c == CMP_LT) {
409 HEAP_SWAP(h->p[HEAP_PARENT(m)], h->p[m]);
410 OFFSET_SET(h, HEAP_PARENT(m));
411 }
412 }
413 idx = m;
414 }
415 OFFSET_SET(h, idx);
416
417 return ret;
418}
419
421{
422 if (is_min_level_index(idx)) {
423 return push_down_min(h, idx);
424 } else {
425 return push_down_max(h, idx);
426 }
427}
428
430{
431 fr_minmax_heap_index_t grandparent;
432 int ret = 0;
433
434 while ((grandparent = HEAP_GRANDPARENT(idx)) > 0) {
435 fr_cmp_ret_t c = h->cmp(h->p[idx], h->p[grandparent]);
436
437 if (unlikely(c == CMP_ERR)) {
438 ret = -1;
439 break;
440 }
441 if (c != CMP_LT) break;
442
443 HEAP_SWAP(h->p[idx], h->p[grandparent]);
444 OFFSET_SET(h, idx);
445 idx = grandparent;
446 }
447 OFFSET_SET(h, idx);
448
449 return ret;
450}
451
453{
454 fr_minmax_heap_index_t grandparent;
455 int ret = 0;
456
457 while ((grandparent = HEAP_GRANDPARENT(idx)) > 0) {
458 fr_cmp_ret_t c = h->cmp(h->p[idx], h->p[grandparent]);
459
460 if (unlikely(c == CMP_ERR)) {
461 ret = -1;
462 break;
463 }
464 if (c != CMP_GT) break;
465
466 HEAP_SWAP(h->p[idx], h->p[grandparent]);
467 OFFSET_SET(h, idx);
468 idx = grandparent;
469 }
470 OFFSET_SET(h, idx);
471
472 return ret;
473}
474
476{
478 fr_cmp_ret_t order;
479
480 /*
481 * First entry? No need to move; set its index and be done with it.
482 */
483 if (idx == 1) {
484 OFFSET_SET(h, idx);
485 return 0;
486 }
487
488 /*
489 * Otherwise, move to the next level up if need be.
490 * Once it's positioned appropriately on an even or odd layer,
491 * it can percolate up two at a time.
492 */
493 parent = HEAP_PARENT(idx);
494 order = h->cmp(h->p[idx], h->p[parent]);
495
496 if (unlikely(order == CMP_ERR)) {
497 OFFSET_SET(h, idx);
498 return -1;
499 }
500
501 if (is_min_level_index(idx)) {
502 if (order == CMP_GT) {
503 HEAP_SWAP(h->p[idx], h->p[parent]);
504 OFFSET_SET(h, idx);
505 return push_up_max(h, parent);
506 } else {
507 return push_up_min(h, idx);
508 }
509 } else {
510 if (order == CMP_LT) {
511 HEAP_SWAP(h->p[idx], h->p[parent]);
512 OFFSET_SET(h, idx);
513 return push_up_min(h, parent);
514 } else {
515 return push_up_max(h, idx);
516 }
517 }
518}
519
521{
522 minmax_heap_t *h = *hp;
524
526 fr_strerror_const("Node is already in a heap");
527 return -1;
528 }
529
530 child = h->num_elements + 1;
531 if (unlikely(child > h->size)) {
532 if (unlikely(minmax_heap_expand(hp) < 0)) return -1;
533 h = *hp;
534 }
535
536 /*
537 * Add it to the end, and move it up as needed.
538 */
539 h->p[child] = data;
540 h->num_elements++;
541 return push_up(h, child);
542}
543
545{
546 minmax_heap_t *h = *hp;
547
548 if (unlikely(h->num_elements == 0)) return NULL;
549 return h->p[1];
550}
551
553{
554 void *data = fr_minmax_heap_min_peek(hp);
555
556 *out = NULL;
557 if (unlikely(!data)) return 0;
558 if (unlikely(fr_minmax_heap_extract(hp, data) < 0)) return -1;
559 *out = data;
560 return 0;
561}
562
564{
565 minmax_heap_t *h = *hp;
566 fr_cmp_ret_t c;
567
568 *out = NULL;
569 if (unlikely(h->num_elements == 0)) return 0;
570
571 if (h->num_elements < 3) {
572 *out = h->p[h->num_elements];
573 return 0;
574 }
575
576 c = h->cmp(h->p[2], h->p[3]);
577 if (unlikely(c == CMP_ERR)) return -1;
578
579 *out = h->p[2 + (c == CMP_LT)];
580 return 0;
581}
582
584{
585 void *data;
586
587 *out = NULL;
588 if (unlikely(fr_minmax_heap_max_peek(&data, hp) < 0)) return -1;
589 if (unlikely(!data)) return 0;
590 if (unlikely(fr_minmax_heap_extract(hp, data) < 0)) return -1;
591 *out = data;
592 return 0;
593}
594
596{
597 minmax_heap_t *h = *hp;
599 int ret = 0;
600
601 if (unlikely(h->num_elements < idx)) {
602 fr_strerror_printf("data (index %u) exceeds heap size %u", idx, h->num_elements);
603 return -1;
604 }
605 if (unlikely(!fr_minmax_heap_entry_inserted(index_get(h, data)) || h->p[idx] != data)) {
606 fr_strerror_printf("data (index %u) not in heap", idx);
607 return -1;
608 }
609
610 OFFSET_RESET(h, idx);
611
612 /*
613 * Removing the last element can't break the minmax heap property, so
614 * decrement the number of elements and be done with it.
615 */
616 if (h->num_elements == idx) {
617 h->num_elements--;
618 return 0;
619 }
620
621 /*
622 * Move the last element into the now-available position,
623 * and then move it as needed.
624 */
625 h->p[idx] = h->p[h->num_elements];
626 h->num_elements--;
627 /*
628 * If the new position is the root, that's as far up as it gets.
629 * If the old position is a descendant of the new position,
630 * the entry itself remains a descendant of the new position's
631 * parent, and hence by minmax heap property is in the proper
632 * relation to the parent and doesn't need to move up.
633 */
634 if (idx > 1 && !is_descendant(h->num_elements, idx)) ret = push_up(h, idx);
635 if (likely(ret == 0)) ret = push_down(h, idx);
636 return ret;
637}
638
639/** Return the number of elements in the minmax heap
640 *
641 * @param[in] hp to return the number of elements from.
642 */
644{
645 minmax_heap_t *h = *hp;
646
647 return h->num_elements;
648}
649
650/** Iterate over entries in a minmax heap
651 *
652 * @note If the heap is modified the iterator should be considered invalidated.
653 *
654 * @param[in] hp to iterate over.
655 * @param[in] iter Pointer to an iterator struct, used to maintain
656 * state between calls.
657 * @return
658 * - User data.
659 * - NULL if at the end of the list.
660 */
662{
663 minmax_heap_t *h = *hp;
664
665 *iter = 1;
666
667 if (h->num_elements == 0) return NULL;
668
669 return h->p[1];
670}
671
672/** Get the next entry in a minmax heap
673 *
674 * @note If the heap is modified the iterator should be considered invalidated.
675 *
676 * @param[in] hp to iterate over.
677 * @param[in] iter Pointer to an iterator struct, used to maintain
678 * state between calls.
679 * @return
680 * - User data.
681 * - NULL if at the end of the list.
682 */
684{
685 minmax_heap_t *h = *hp;
686
687 if ((*iter + 1) > h->num_elements) return NULL;
688 *iter += 1;
689
690 return h->p[*iter];
691}
692
693#ifndef TALLOC_GET_TYPE_ABORT_NOOP
694void fr_minmax_heap_verify(char const *file, int line, fr_minmax_heap_t const *hp)
695{
696 minmax_heap_t *h;
697
698 /*
699 * The usual start...
700 */
701 fr_fatal_assert_msg(hp, "CONSISTENCY CHECK FAILED %s[%i]: fr_minmax_heap_t pointer was NULL", file, line);
702 (void) talloc_get_type_abort(hp, fr_minmax_heap_t);
703
704 /*
705 * Allocating the heap structure and the array holding the heap as described in data structure
706 * texts together is a respectable savings, but it means adding a level of indirection so the
707 * fr_heap_t * isn't realloc()ed out from under the user, hence the following (and the use of h
708 * rather than hp to access anything in the heap structure).
709 */
710 h = *hp;
711 fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: minmax_heap_t pointer was NULL", file, line);
712 (void) talloc_get_type_abort(h, minmax_heap_t);
713
715 "CONSISTENCY CHECK FAILED %s[%i]: num_elements exceeds size", file, line);
716
717 fr_fatal_assert_msg(h->p[0] == (void *)UINTPTR_MAX,
718 "CONSISTENCY CHECK FAILED %s[%i]: zeroeth element special value overwritten", file, line);
719
720 for (fr_minmax_heap_index_t i = 1; i <= h->num_elements; i++) {
721 void *data = h->p[i];
722
723 fr_fatal_assert_msg(data, "CONSISTENCY CHECK FAILED %s[%i]: node %u was NULL", file, line, i);
724 if (h->type) (void)_talloc_get_type_abort(data, h->type, __location__);
726 "CONSISTENCY CHECK FAILED %s[%i]: node %u index != %u", file, line, i, i);
727 }
728
729 /*
730 * Verify minmax heap property, which is:
731 * A node in a min level precedes all its descendants;
732 * a node in a max level follows all its descencdants.
733 * (if equal keys are allowed, that should be "doesn't follow" and
734 * "doesn't precede" respectively)
735 *
736 * We claim looking at one's children and grandchildren (if any)
737 * suffices. Why? Induction on floor(depth / 2):
738 *
739 * Base case:
740 * If the depth of the tree is <= 2, that *is* all the
741 * descendants, so we're done.
742 * Induction step:
743 * Suppose you're on a min level and the check passes.
744 * If the test works on the next min level down, transitivity
745 * of <= means the level you're on satisfies the property
746 * two levels further down.
747 * For max level, >= is transitive, too, so you're good.
748 */
749
750 for (fr_minmax_heap_index_t i = 1; HEAP_LEFT(i) <= h->num_elements; i++) {
751 bool on_min_level = is_min_level_index(i);
752 fr_minmax_heap_index_t others[] = {
753 HEAP_LEFT(i),
754 HEAP_RIGHT(i),
759 };
760
761 for (size_t j = 0; j < NUM_ELEMENTS(others) && others[j] <= h->num_elements; j++) {
762 fr_cmp_ret_t cmp_result = h->cmp(h->p[i], h->p[others[j]]);
763
764 fr_fatal_assert_msg(cmp_result != CMP_ERR,
765 "CONSISTENCY CHECK FAILED %s[%i]: comparator error: %s",
766 file, line, fr_strerror());
767 fr_fatal_assert_msg(on_min_level ? (cmp_result != CMP_GT) : (cmp_result != CMP_LT),
768 "CONSISTENCY CHECK FAILED %s[%i]: node %u violates %s level condition",
769 file, line, i, on_min_level ? "min" : "max");
770 }
771 }
772}
773#endif
int const char * file
Definition acutest.h:702
#define RCSID(id)
Definition build.h:560
#define unlikely(_x)
Definition build.h:455
#define NUM_ELEMENTS(_t)
Definition build.h:406
static size_t min(size_t x, size_t y)
Definition dbuff.c:66
#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:217
Definition dwarf.c:563
talloc_free(hp)
static uint8_t fr_high_bit_pos(uint64_t num)
Find the highest order high bit in an unsigned 64 bit integer.
Definition math.h:94
unsigned char uint8_t
static int max_child_or_grandchild(fr_minmax_heap_index_t *out, minmax_heap_t *h, fr_minmax_heap_index_t idx)
int fr_minmax_heap_insert(fr_minmax_heap_t *hp, void *data)
static bool has_grandchildren(minmax_heap_t *h, fr_minmax_heap_index_t i)
#define HEAP_PARENT(_x)
Definition minmax_heap.c:74
static int min_child_or_grandchild(fr_minmax_heap_index_t *out, minmax_heap_t *h, fr_minmax_heap_index_t idx)
Find the index of the minimum child or grandchild of the entry at a given index.
static void index_set(minmax_heap_t *h, void *data, fr_minmax_heap_index_t idx)
static int push_up_max(minmax_heap_t *h, fr_minmax_heap_index_t idx)
#define OFFSET_SET(_heap, _idx)
static int push_down_min(minmax_heap_t *h, fr_minmax_heap_index_t idx)
precondition: idx is the index of an existing entry on a min level
static int minmax_heap_expand(fr_minmax_heap_t *hp)
void * fr_minmax_heap_iter_next(fr_minmax_heap_t *hp, fr_minmax_heap_iter_t *iter)
Get the next entry in a minmax heap.
#define INITIAL_CAPACITY
Definition minmax_heap.c:67
int fr_minmax_heap_min_pop(void **out, fr_minmax_heap_t *hp)
void * p[]
Array of nodes.
Definition minmax_heap.c:62
static int push_up_min(minmax_heap_t *h, fr_minmax_heap_index_t idx)
int fr_minmax_heap_max_pop(void **out, fr_minmax_heap_t *hp)
static int push_up(minmax_heap_t *h, fr_minmax_heap_index_t idx)
static bool has_children(minmax_heap_t *h, fr_minmax_heap_index_t idx)
void * fr_minmax_heap_min_peek(fr_minmax_heap_t *hp)
static int push_down_max(minmax_heap_t *h, fr_minmax_heap_index_t idx)
precondition: idx is the index of an existing entry on a max level (Just like push_down_min() save fo...
#define OFFSET_RESET(_heap, _idx)
#define is_max_level_index(_i)
#define HEAP_GRANDPARENT(_x)
Definition minmax_heap.c:75
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
static int push_down(minmax_heap_t *h, fr_minmax_heap_index_t idx)
fr_minmax_heap_cmp_t cmp
Comparator function.
Definition minmax_heap.c:60
unsigned int fr_minmax_heap_num_elements(fr_minmax_heap_t *hp)
Return the number of elements in the minmax heap.
#define HEAP_SWAP(_a, _b)
Definition minmax_heap.c:78
#define HEAP_LEFT(_x)
Definition minmax_heap.c:76
struct fr_minmax_heap_s minmax_heap_t
Definition minmax_heap.c:65
fr_minmax_heap_t * _fr_minmax_heap_alloc(TALLOC_CTX *ctx, fr_minmax_heap_cmp_t cmp, char const *type, size_t offset, unsigned int init)
static bool is_min_level_index(fr_minmax_heap_index_t i)
Definition minmax_heap.c:88
static bool is_descendant(fr_minmax_heap_index_t candidate, fr_minmax_heap_index_t ancestor)
Definition minmax_heap.c:93
size_t offset
Offset of heap index in element structure.
Definition minmax_heap.c:55
void * fr_minmax_heap_iter_init(fr_minmax_heap_t *hp, fr_minmax_heap_iter_t *iter)
Iterate over entries in a minmax heap.
int fr_minmax_heap_extract(fr_minmax_heap_t *hp, void *data)
#define HEAP_RIGHT(_x)
Definition minmax_heap.c:77
int fr_minmax_heap_max_peek(void **out, fr_minmax_heap_t *hp)
unsigned int size
Number of nodes allocated.
Definition minmax_heap.c:54
char const * type
Talloc type of elements.
Definition minmax_heap.c:59
static fr_minmax_heap_index_t index_get(minmax_heap_t *h, void *data)
void fr_minmax_heap_verify(char const *file, int line, fr_minmax_heap_t const *hp)
unsigned int num_elements
Number of nodes used.
Definition minmax_heap.c:57
static bool fr_minmax_heap_entry_inserted(fr_minmax_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition minmax_heap.h:95
fr_cmp_ret_t(* fr_minmax_heap_cmp_t)(void const *a, void const *b)
Comparator to order elements.
Definition minmax_heap.h:52
unsigned int fr_minmax_heap_iter_t
Definition minmax_heap.h:39
unsigned int fr_minmax_heap_index_t
Definition minmax_heap.h:38
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
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