The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
23 RCSID("$Id: 2e4c500dc484a79b6673fbd84284744521ecd5e3 $")
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 
43 static 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  */
52 size_t fr_heap_pre_alloc_size(unsigned int count)
53 {
54  return sizeof(fr_heap_t) + sizeof(void *) * count;
55 }
56 
57 fr_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 
61  if (!init) init = INITIAL_CAPACITY;
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 
92 static 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 
97 static 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 
105 static inline CC_HINT(always_inline)
106 int 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  */
146 int fr_heap_insert(fr_heap_t **hp, void *data)
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 
204 static 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 (%i) out of bounds (0-%i)", 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 %i, got %p", data,
264  parent, h->p[parent]);
265  return -1;
266  }
267  max = h->num_elements;
268 
269  child = HEAP_LEFT(parent);
270  OFFSET_RESET(h, 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
380 void 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
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
#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:182
#define HEAP_PARENT(_x)
Definition: heap.c:38
#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
void * fr_heap_pop(fr_heap_t **hp)
Remove a node from the heap.
Definition: heap.c:322
static fr_heap_index_t index_get(fr_heap_t *h, void *data)
Definition: heap.c:92
#define INITIAL_CAPACITY
Definition: heap.c:31
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
#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_iter_init(fr_heap_t *h, fr_heap_iter_t *iter)
Iterate over entries in heap.
Definition: heap.c:351
#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
void fr_heap_verify(char const *file, int line, fr_heap_t *h)
Definition: heap.c:380
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
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
Definition: merged_model.c:30
return count
Definition: module.c:175
init
Enter the EAP-IDENTITY state.
Definition: state_machine.c:90
fr_aka_sim_id_type_t type
static fr_slen_t parent
Definition: pair.h:844
#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:1259
int nonnull(2, 5))