Coverage Report

Created: 2026-11-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/minmax_heap.c
Line
Count
Source
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
 */
23
RCSID("$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
53
struct fr_minmax_heap_s {
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
65
typedef struct fr_minmax_heap_s minmax_heap_t;
66
67
0
#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
0
#define HEAP_PARENT(_x) ((_x) >> 1)
75
0
#define HEAP_GRANDPARENT(_x)  HEAP_PARENT(HEAP_PARENT(_x))
76
0
#define HEAP_LEFT(_x) (2 * (_x))
77
0
#define HEAP_RIGHT(_x) (2 * (_x) + 1 )
78
0
#define HEAP_SWAP(_a, _b) do { void *_tmp = _a; _a = _b; _b = _tmp; } while (0)
79
80
/**
81
 * @hidecallergraph
82
 */
83
static inline uint8_t depth(fr_minmax_heap_index_t i)
84
0
{
85
0
  return fr_high_bit_pos(i) - 1;
86
0
}
87
88
static inline bool is_min_level_index(fr_minmax_heap_index_t i)
89
0
{
90
0
  return (depth(i) & 1) == 0;
91
0
}
92
93
static inline bool is_descendant(fr_minmax_heap_index_t candidate, fr_minmax_heap_index_t ancestor)
94
0
{
95
0
  fr_minmax_heap_index_t  level_min;
96
0
  uint8_t     candidate_depth = depth(candidate);
97
0
  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
0
  if (unlikely(candidate_depth < ancestor_depth)) return false;
104
105
0
  level_min = ((fr_minmax_heap_index_t) 1) << (candidate_depth - ancestor_depth);
106
0
  return (candidate - level_min) < level_min;
107
0
}
108
109
0
#define is_max_level_index(_i)  (!(is_min_level_index(_i)))
110
111
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)
112
0
{
113
0
  fr_minmax_heap_t *hp;
114
0
  minmax_heap_t *h;
115
116
0
  if (!init) init = INITIAL_CAPACITY;
117
118
0
  hp = talloc(ctx, fr_minmax_heap_t);
119
0
  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
0
  h = (minmax_heap_t *)talloc_array(hp, uint8_t, sizeof(minmax_heap_t) + (sizeof(void *) * (init + 1)));
128
0
  if (unlikely(!h)) {
129
0
    talloc_free(hp);
130
0
    return NULL;
131
0
  }
132
0
  talloc_set_type(h, minmax_heap_t);
133
134
0
  *h = (minmax_heap_t){
135
0
    .size = init,
136
0
    .type = type,
137
0
    .cmp = cmp,
138
0
    .offset = offset
139
0
  };
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
0
  h->p[0] = (void *)UINTPTR_MAX;
148
149
0
  *hp = h;
150
151
0
  return hp;
152
0
}
153
154
static CC_HINT(nonnull) int minmax_heap_expand(fr_minmax_heap_t *hp)
155
0
{
156
0
  minmax_heap_t *h = *hp;
157
0
  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
0
  if (unlikely(h->size > UINT_MAX - h->size)) {
165
0
    if (h->size == UINT_MAX) {
166
0
      fr_strerror_const("Heap is full");
167
0
      return -1;
168
0
    }
169
0
    n_size = UINT_MAX;
170
0
  } else {
171
0
    n_size = 2 * h->size;
172
0
  }
173
174
0
  h = (minmax_heap_t *)talloc_realloc(hp, h, uint8_t, sizeof(minmax_heap_t) + (sizeof(void *) * ((size_t)n_size + 1)));
175
0
  if (unlikely(!h)) {
176
0
    fr_strerror_printf("Failed expanding heap to %u elements (%zu bytes)",
177
0
           n_size, (n_size * sizeof(void *)));
178
0
    return -1;
179
0
  }
180
181
0
  talloc_set_type(h, minmax_heap_t);
182
0
  h->size = n_size;
183
0
  *hp = h;
184
0
  return 0;
185
0
}
186
187
188
static inline CC_HINT(always_inline, nonnull) fr_minmax_heap_index_t index_get(minmax_heap_t *h, void *data)
189
0
{
190
0
  return *((fr_minmax_heap_index_t const *)(((uint8_t const *)data) + h->offset));
191
0
}
192
193
static inline CC_HINT(always_inline, nonnull) void index_set(minmax_heap_t *h, void *data, fr_minmax_heap_index_t idx)
194
0
{
195
0
  *((fr_minmax_heap_index_t *)(((uint8_t *)data) + h->offset)) = idx;
196
0
}
197
198
static inline CC_HINT(always_inline, nonnull) bool has_children(minmax_heap_t *h, fr_minmax_heap_index_t idx)
199
0
{
200
0
  return HEAP_LEFT(idx) <= h->num_elements;
201
0
}
202
203
static inline bool has_grandchildren(minmax_heap_t *h, fr_minmax_heap_index_t i)
204
0
{
205
0
  return HEAP_LEFT(HEAP_LEFT(i)) <= h->num_elements;
206
0
}
207
208
0
#define OFFSET_SET(_heap, _idx) index_set(_heap, _heap->p[_idx], _idx)
209
0
#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
 */
237
static CC_HINT(nonnull) int min_child_or_grandchild(fr_minmax_heap_index_t *out, minmax_heap_t *h, fr_minmax_heap_index_t idx)
238
0
{
239
0
  fr_minmax_heap_index_t  lwb, upb, min;
240
0
  fr_cmp_ret_t    c;
241
0
  int     ret = 0;
242
243
0
  if (is_max_level_index(idx) || !has_grandchildren(h, idx)) {
244
    /* minimum must be a chld */
245
0
    min = HEAP_LEFT(idx);
246
0
    upb = HEAP_RIGHT(idx);
247
0
    if (upb <= h->num_elements) {
248
0
      c = h->cmp(h->p[upb], h->p[min]);
249
0
      if (unlikely(c == CMP_ERR)) ret = -1;
250
0
      if (c == CMP_LT) min = upb;
251
0
    }
252
0
    *out = min;
253
0
    return ret;
254
0
  }
255
256
  /* minimum must be a grandchild, unless the right child is childless */
257
0
  if (!has_children(h, HEAP_RIGHT(idx))) {
258
0
    min = HEAP_RIGHT(idx);
259
0
    lwb = HEAP_LEFT(HEAP_LEFT(idx));
260
0
  } else {
261
0
    min = HEAP_LEFT(HEAP_LEFT(idx));
262
0
    lwb = min + 1;
263
0
  }
264
0
  upb = HEAP_RIGHT(HEAP_RIGHT(idx));
265
266
  /* Some grandchildren may not exist. */
267
0
  if (upb > h->num_elements) upb = h->num_elements;
268
269
0
  for (fr_minmax_heap_index_t i = lwb; i <= upb; i++) {
270
0
    c = h->cmp(h->p[i], h->p[min]);
271
0
    if (unlikely(c == CMP_ERR)) ret = -1;
272
0
    if (c == CMP_LT) min = i;
273
0
  }
274
0
  *out = min;
275
0
  return ret;
276
0
}
277
278
static CC_HINT(nonnull) int max_child_or_grandchild(fr_minmax_heap_index_t *out, minmax_heap_t *h, fr_minmax_heap_index_t idx)
279
0
{
280
0
  fr_minmax_heap_index_t  lwb, upb, max;
281
0
  fr_cmp_ret_t    c;
282
0
  int     ret = 0;
283
284
0
  if (is_min_level_index(idx) || !has_grandchildren(h, idx)) {
285
    /* maximum must be a chld */
286
0
    max = HEAP_LEFT(idx);
287
0
    upb = HEAP_RIGHT(idx);
288
0
    if (upb <= h->num_elements) {
289
0
      c = h->cmp(h->p[upb], h->p[max]);
290
0
      if (unlikely(c == CMP_ERR)) ret = -1;
291
0
      if (c == CMP_GT) max = upb;
292
0
    }
293
0
    *out = max;
294
0
    return ret;
295
0
  }
296
297
  /* minimum must be a grandchild, unless the right child is childless */
298
0
  if (!has_children(h, HEAP_RIGHT(idx))) {
299
0
    max = HEAP_RIGHT(idx);
300
0
    lwb = HEAP_LEFT(HEAP_LEFT(idx));
301
0
  } else {
302
0
    max = HEAP_LEFT(HEAP_LEFT(idx));
303
0
    lwb = max + 1;
304
0
  }
305
0
  upb = HEAP_RIGHT(HEAP_RIGHT(idx));
306
307
  /* Some grandchildren may not exist. */
308
0
  if (upb > h->num_elements) upb = h->num_elements;
309
310
0
  for (fr_minmax_heap_index_t i = lwb; i <= upb; i++) {
311
0
    c = h->cmp(h->p[i], h->p[max]);
312
0
    if (unlikely(c == CMP_ERR)) ret = -1;
313
0
    if (c == CMP_GT) max = i;
314
0
  }
315
0
  *out = max;
316
0
  return ret;
317
0
}
318
319
/**
320
 * precondition: idx is the index of an existing entry on a min level
321
 */
322
static inline CC_HINT(always_inline, nonnull) int push_down_min(minmax_heap_t *h, fr_minmax_heap_index_t idx)
323
0
{
324
0
  int ret = 0;
325
326
0
  while (has_children(h, idx)) {
327
0
    fr_minmax_heap_index_t  m;
328
0
    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
0
    if (unlikely(min_child_or_grandchild(&m, h, idx) < 0)) {
336
0
      ret = -1;
337
0
      break;
338
0
    }
339
0
    c = h->cmp(h->p[m], h->p[idx]);
340
0
    if (unlikely(c == CMP_ERR)) {
341
0
      ret = -1;
342
0
      break;
343
0
    }
344
345
    /*
346
     *  If p[m] doesn't precede p[idx], we're done.
347
     */
348
0
    if (c != CMP_LT) break;
349
350
0
    HEAP_SWAP(h->p[idx], h->p[m]);
351
0
    OFFSET_SET(h, idx);
352
353
    /*
354
     *  The entry now at m may belong where the parent is.
355
     */
356
0
    if (HEAP_GRANDPARENT(m) == idx) {
357
0
      c = h->cmp(h->p[m], h->p[HEAP_PARENT(m)]);
358
0
      if (unlikely(c == CMP_ERR)) {
359
0
        ret = -1;
360
0
        break;
361
0
      }
362
0
      if (c == CMP_GT) {
363
0
        HEAP_SWAP(h->p[HEAP_PARENT(m)], h->p[m]);
364
0
        OFFSET_SET(h, HEAP_PARENT(m));
365
0
      }
366
0
    }
367
0
    idx = m;
368
0
  }
369
0
  OFFSET_SET(h, idx);
370
371
0
  return ret;
372
0
}
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
 */
379
static CC_HINT(nonnull) int push_down_max(minmax_heap_t *h, fr_minmax_heap_index_t idx)
380
0
{
381
0
  int ret = 0;
382
383
0
  while (has_children(h, idx)) {
384
0
    fr_minmax_heap_index_t  m;
385
0
    fr_cmp_ret_t    c;
386
387
0
    if (unlikely(max_child_or_grandchild(&m, h, idx) < 0)) {
388
0
      ret = -1;
389
0
      break;
390
0
    }
391
0
    c = h->cmp(h->p[m], h->p[idx]);
392
0
    if (unlikely(c == CMP_ERR)) {
393
0
      ret = -1;
394
0
      break;
395
0
    }
396
397
0
    if (c != CMP_GT) break;
398
399
0
    HEAP_SWAP(h->p[idx], h->p[m]);
400
0
    OFFSET_SET(h, idx);
401
402
0
    if (HEAP_GRANDPARENT(m) == idx) {
403
0
      c = h->cmp(h->p[m], h->p[HEAP_PARENT(m)]);
404
0
      if (unlikely(c == CMP_ERR)) {
405
0
        ret = -1;
406
0
        break;
407
0
      }
408
0
      if (c == CMP_LT) {
409
0
        HEAP_SWAP(h->p[HEAP_PARENT(m)], h->p[m]);
410
0
        OFFSET_SET(h, HEAP_PARENT(m));
411
0
      }
412
0
    }
413
0
    idx = m;
414
0
  }
415
0
  OFFSET_SET(h, idx);
416
417
0
  return ret;
418
0
}
419
420
static int push_down(minmax_heap_t *h, fr_minmax_heap_index_t idx)
421
0
{
422
0
  if (is_min_level_index(idx)) {
423
0
    return push_down_min(h, idx);
424
0
  } else {
425
0
    return push_down_max(h, idx);
426
0
  }
427
0
}
428
429
static int push_up_min(minmax_heap_t *h, fr_minmax_heap_index_t idx)
430
0
{
431
0
  fr_minmax_heap_index_t  grandparent;
432
0
  int     ret = 0;
433
434
0
  while ((grandparent = HEAP_GRANDPARENT(idx)) > 0) {
435
0
    fr_cmp_ret_t c = h->cmp(h->p[idx], h->p[grandparent]);
436
437
0
    if (unlikely(c == CMP_ERR)) {
438
0
      ret = -1;
439
0
      break;
440
0
    }
441
0
    if (c != CMP_LT) break;
442
443
0
    HEAP_SWAP(h->p[idx], h->p[grandparent]);
444
0
    OFFSET_SET(h, idx);
445
0
    idx = grandparent;
446
0
  }
447
0
  OFFSET_SET(h, idx);
448
449
0
  return ret;
450
0
}
451
452
static int push_up_max(minmax_heap_t *h, fr_minmax_heap_index_t idx)
453
0
{
454
0
  fr_minmax_heap_index_t  grandparent;
455
0
  int     ret = 0;
456
457
0
  while ((grandparent = HEAP_GRANDPARENT(idx)) > 0) {
458
0
    fr_cmp_ret_t c = h->cmp(h->p[idx], h->p[grandparent]);
459
460
0
    if (unlikely(c == CMP_ERR)) {
461
0
      ret = -1;
462
0
      break;
463
0
    }
464
0
    if (c != CMP_GT) break;
465
466
0
    HEAP_SWAP(h->p[idx], h->p[grandparent]);
467
0
    OFFSET_SET(h, idx);
468
0
    idx = grandparent;
469
0
  }
470
0
  OFFSET_SET(h, idx);
471
472
0
  return ret;
473
0
}
474
475
static int push_up(minmax_heap_t *h, fr_minmax_heap_index_t idx)
476
0
{
477
0
  fr_minmax_heap_index_t  parent;
478
0
  fr_cmp_ret_t    order;
479
480
  /*
481
   *  First entry? No need to move; set its index and be done with it.
482
   */
483
0
  if (idx == 1) {
484
0
    OFFSET_SET(h, idx);
485
0
    return 0;
486
0
  }
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
0
  parent = HEAP_PARENT(idx);
494
0
  order = h->cmp(h->p[idx], h->p[parent]);
495
496
0
  if (unlikely(order == CMP_ERR)) {
497
0
    OFFSET_SET(h, idx);
498
0
    return -1;
499
0
  }
500
501
0
  if (is_min_level_index(idx)) {
502
0
    if (order == CMP_GT) {
503
0
      HEAP_SWAP(h->p[idx], h->p[parent]);
504
0
      OFFSET_SET(h, idx);
505
0
      return push_up_max(h, parent);
506
0
    } else {
507
0
      return push_up_min(h, idx);
508
0
    }
509
0
  } else {
510
0
    if (order == CMP_LT) {
511
0
      HEAP_SWAP(h->p[idx], h->p[parent]);
512
0
      OFFSET_SET(h, idx);
513
0
      return push_up_min(h, parent);
514
0
    } else {
515
0
      return push_up_max(h, idx);
516
0
    }
517
0
  }
518
0
}
519
520
int fr_minmax_heap_insert(fr_minmax_heap_t *hp, void *data)
521
0
{
522
0
  minmax_heap_t   *h = *hp;
523
0
  fr_minmax_heap_index_t  child = index_get(h, data);
524
525
0
  if (unlikely(fr_minmax_heap_entry_inserted(child))) {
526
0
    fr_strerror_const("Node is already in a heap");
527
0
    return -1;
528
0
  }
529
530
0
  child = h->num_elements + 1;
531
0
  if (unlikely(child > h->size)) {
532
0
    if (unlikely(minmax_heap_expand(hp) < 0)) return -1;
533
0
    h = *hp;
534
0
  }
535
536
  /*
537
   *  Add it to the end, and move it up as needed.
538
   */
539
0
  h->p[child] = data;
540
0
  h->num_elements++;
541
0
  return push_up(h, child);
542
0
}
543
544
void *fr_minmax_heap_min_peek(fr_minmax_heap_t *hp)
545
0
{
546
0
  minmax_heap_t *h = *hp;
547
548
0
  if (unlikely(h->num_elements == 0)) return NULL;
549
0
  return h->p[1];
550
0
}
551
552
int fr_minmax_heap_min_pop(void **out, fr_minmax_heap_t *hp)
553
0
{
554
0
  void  *data = fr_minmax_heap_min_peek(hp);
555
556
0
  *out = NULL;
557
0
  if (unlikely(!data)) return 0;
558
0
  if (unlikely(fr_minmax_heap_extract(hp, data) < 0)) return -1;
559
0
  *out = data;
560
0
  return 0;
561
0
}
562
563
int fr_minmax_heap_max_peek(void **out, fr_minmax_heap_t *hp)
564
0
{
565
0
  minmax_heap_t   *h = *hp;
566
0
  fr_cmp_ret_t    c;
567
568
0
  *out = NULL;
569
0
  if (unlikely(h->num_elements == 0)) return 0;
570
571
0
  if (h->num_elements < 3) {
572
0
    *out = h->p[h->num_elements];
573
0
    return 0;
574
0
  }
575
576
0
  c = h->cmp(h->p[2], h->p[3]);
577
0
  if (unlikely(c == CMP_ERR)) return -1;
578
579
0
  *out = h->p[2 + (c == CMP_LT)];
580
0
  return 0;
581
0
}
582
583
int fr_minmax_heap_max_pop(void **out, fr_minmax_heap_t *hp)
584
0
{
585
0
  void  *data;
586
587
0
  *out = NULL;
588
0
  if (unlikely(fr_minmax_heap_max_peek(&data, hp) < 0)) return -1;
589
0
  if (unlikely(!data)) return 0;
590
0
  if (unlikely(fr_minmax_heap_extract(hp, data) < 0)) return -1;
591
0
  *out = data;
592
0
  return 0;
593
0
}
594
595
int fr_minmax_heap_extract(fr_minmax_heap_t *hp, void *data)
596
0
{
597
0
  minmax_heap_t   *h = *hp;
598
0
  fr_minmax_heap_index_t  idx = index_get(h, data);
599
0
  int     ret = 0;
600
601
0
  if (unlikely(h->num_elements < idx)) {
602
0
    fr_strerror_printf("data (index %u) exceeds heap size %u", idx, h->num_elements);
603
0
    return -1;
604
0
  }
605
0
  if (unlikely(!fr_minmax_heap_entry_inserted(index_get(h, data)) || h->p[idx] != data)) {
606
0
    fr_strerror_printf("data (index %u) not in heap", idx);
607
0
    return -1;
608
0
  }
609
610
0
  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
0
  if (h->num_elements == idx) {
617
0
    h->num_elements--;
618
0
    return 0;
619
0
  }
620
621
  /*
622
   *  Move the last element into the now-available position,
623
   *  and then move it as needed.
624
   */
625
0
  h->p[idx] = h->p[h->num_elements];
626
0
  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
0
  if (idx > 1 && !is_descendant(h->num_elements, idx)) ret = push_up(h, idx);
635
0
  if (likely(ret == 0)) ret = push_down(h, idx);
636
0
  return ret;
637
0
}
638
639
/** Return the number of elements in the minmax heap
640
 *
641
 * @param[in] hp  to return the number of elements from.
642
 */
643
unsigned int fr_minmax_heap_num_elements(fr_minmax_heap_t *hp)
644
0
{
645
0
  minmax_heap_t *h = *hp;
646
647
0
  return h->num_elements;
648
0
}
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
 */
661
void *fr_minmax_heap_iter_init(fr_minmax_heap_t *hp, fr_minmax_heap_iter_t *iter)
662
0
{
663
0
  minmax_heap_t *h = *hp;
664
665
0
  *iter = 1;
666
667
0
  if (h->num_elements == 0) return NULL;
668
669
0
  return h->p[1];
670
0
}
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
 */
683
void *fr_minmax_heap_iter_next(fr_minmax_heap_t *hp, fr_minmax_heap_iter_t *iter)
684
0
{
685
0
  minmax_heap_t *h = *hp;
686
687
0
  if ((*iter + 1) > h->num_elements) return NULL;
688
0
  *iter += 1;
689
690
0
  return h->p[*iter];
691
0
}
692
693
#ifndef TALLOC_GET_TYPE_ABORT_NOOP
694
void fr_minmax_heap_verify(char const *file, int line, fr_minmax_heap_t const *hp)
695
0
{
696
0
  minmax_heap_t *h;
697
698
  /*
699
   *  The usual start...
700
   */
701
0
  fr_fatal_assert_msg(hp, "CONSISTENCY CHECK FAILED %s[%i]: fr_minmax_heap_t pointer was NULL", file, line);
702
0
  (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
0
  h = *hp;
711
0
  fr_fatal_assert_msg(h, "CONSISTENCY CHECK FAILED %s[%i]: minmax_heap_t pointer was NULL", file, line);
712
0
  (void) talloc_get_type_abort(h, minmax_heap_t);
713
714
0
  fr_fatal_assert_msg(h->num_elements <= h->size,
715
0
          "CONSISTENCY CHECK FAILED %s[%i]: num_elements exceeds size", file, line);
716
717
0
  fr_fatal_assert_msg(h->p[0] == (void *)UINTPTR_MAX,
718
0
          "CONSISTENCY CHECK FAILED %s[%i]: zeroeth element special value overwritten", file, line);
719
720
0
  for (fr_minmax_heap_index_t i = 1; i <= h->num_elements; i++) {
721
0
    void  *data = h->p[i];
722
723
0
    fr_fatal_assert_msg(data, "CONSISTENCY CHECK FAILED %s[%i]: node %u was NULL", file, line, i);
724
0
    if (h->type) (void)_talloc_get_type_abort(data, h->type, __location__);
725
0
    fr_fatal_assert_msg(index_get(h, data) == i,
726
0
            "CONSISTENCY CHECK FAILED %s[%i]: node %u index != %u", file, line, i, i);
727
0
  }
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
0
  for (fr_minmax_heap_index_t i = 1; HEAP_LEFT(i) <= h->num_elements; i++) {
751
0
    bool      on_min_level = is_min_level_index(i);
752
0
    fr_minmax_heap_index_t  others[] = {
753
0
      HEAP_LEFT(i),
754
0
      HEAP_RIGHT(i),
755
0
      HEAP_LEFT(HEAP_LEFT(i)),
756
0
      HEAP_RIGHT(HEAP_LEFT(i)),
757
0
      HEAP_LEFT(HEAP_RIGHT(i)),
758
0
      HEAP_RIGHT(HEAP_RIGHT(i))
759
0
    };
760
761
0
    for (size_t j = 0; j < NUM_ELEMENTS(others) && others[j] <= h->num_elements; j++) {
762
0
      fr_cmp_ret_t  cmp_result = h->cmp(h->p[i], h->p[others[j]]);
763
764
0
      fr_fatal_assert_msg(cmp_result != CMP_ERR,
765
0
          "CONSISTENCY CHECK FAILED %s[%i]: comparator error: %s",
766
0
          file, line, fr_strerror());
767
0
      fr_fatal_assert_msg(on_min_level ? (cmp_result != CMP_GT) : (cmp_result != CMP_LT),
768
0
          "CONSISTENCY CHECK FAILED %s[%i]: node %u violates %s level condition",
769
0
          file, line, i, on_min_level ? "min" : "max");
770
0
    }
771
0
  }
772
0
}
773
#endif