/src/leptonica/src/heap.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file heap.c |
29 | | * <pre> |
30 | | * |
31 | | * Create/Destroy L_Heap |
32 | | * L_HEAP *lheapCreate() |
33 | | * void lheapDestroy() |
34 | | * |
35 | | * Operations to add/remove to/from the heap |
36 | | * l_int32 lheapAdd() |
37 | | * static l_int32 lheapExtendArray() |
38 | | * void *lheapRemove() |
39 | | * |
40 | | * Other accessors |
41 | | * l_int32 lheapGetCount() |
42 | | * void *lheapGetElement() |
43 | | * |
44 | | * Heap sort |
45 | | * l_int32 lheapSort() |
46 | | * l_int32 lheapSortStrictOrder() |
47 | | * |
48 | | * Low-level heap operations |
49 | | * static l_int32 lheapSwapUp() |
50 | | * static l_int32 lheapSwapDown() |
51 | | * |
52 | | * Debug output |
53 | | * l_int32 lheapPrint() |
54 | | * |
55 | | * The L_Heap is useful to implement a priority queue, that is sorted |
56 | | * on a key in each element of the heap. The heap is an array |
57 | | * of nearly arbitrary structs, with a l_float32 the first field. |
58 | | * This field is the key on which the heap is sorted. |
59 | | * |
60 | | * Internally, we keep track of the heap size, n. The item at the |
61 | | * root of the heap is at the head of the array. Items are removed |
62 | | * from the head of the array and added to the end of the array. |
63 | | * When an item is removed from the head, the item at the end |
64 | | * of the array is moved to the head. When items are either |
65 | | * added or removed, it is usually necessary to swap array items |
66 | | * to restore the heap order. It is guaranteed that the number |
67 | | * of swaps does not exceed log(n). |
68 | | * |
69 | | * -------------------------- N.B. ------------------------------ |
70 | | * The items on the heap (or, equivalently, in the array) are cast |
71 | | * to void*. Their key is a l_float32, and it is REQUIRED that the |
72 | | * key be the first field in the struct. That allows us to get the |
73 | | * key by simply dereferencing the struct. Alternatively, we could |
74 | | * choose (but don't) to pass an application-specific comparison |
75 | | * function into the heap operation functions. |
76 | | * -------------------------- N.B. ------------------------------ |
77 | | * </pre> |
78 | | */ |
79 | | |
80 | | #ifdef HAVE_CONFIG_H |
81 | | #include <config_auto.h> |
82 | | #endif /* HAVE_CONFIG_H */ |
83 | | |
84 | | #include <string.h> |
85 | | #include "allheaders.h" |
86 | | |
87 | | /* Bounds on initial array size */ |
88 | | static const l_uint32 MaxPtrArraySize = 100000; |
89 | | static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ |
90 | | |
91 | 0 | #define SWAP_ITEMS(i, j) { void *tempitem = lh->array[(i)]; \ |
92 | 0 | lh->array[(i)] = lh->array[(j)]; \ |
93 | 0 | lh->array[(j)] = tempitem; } |
94 | | |
95 | | /* Static functions */ |
96 | | static l_int32 lheapExtendArray(L_HEAP *lh); |
97 | | static l_ok lheapSwapUp(L_HEAP *lh, l_int32 index); |
98 | | static l_ok lheapSwapDown(L_HEAP *lh); |
99 | | |
100 | | |
101 | | /*--------------------------------------------------------------------------* |
102 | | * L_Heap create/destroy * |
103 | | *--------------------------------------------------------------------------*/ |
104 | | /*! |
105 | | * \brief lheapCreate() |
106 | | * |
107 | | * \param[in] n size of ptr array to be alloc'd; use 0 for default |
108 | | * \param[in] direction L_SORT_INCREASING, L_SORT_DECREASING |
109 | | * \return lheap, or NULL on error |
110 | | */ |
111 | | L_HEAP * |
112 | | lheapCreate(l_int32 n, |
113 | | l_int32 direction) |
114 | 0 | { |
115 | 0 | L_HEAP *lh; |
116 | |
|
117 | 0 | if (n < InitialPtrArraySize || n > MaxPtrArraySize) |
118 | 0 | n = InitialPtrArraySize; |
119 | | |
120 | | /* Allocate ptr array and initialize counters. */ |
121 | 0 | lh = (L_HEAP *)LEPT_CALLOC(1, sizeof(L_HEAP)); |
122 | 0 | if ((lh->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) { |
123 | 0 | lheapDestroy(&lh, FALSE); |
124 | 0 | return (L_HEAP *)ERROR_PTR("ptr array not made", __func__, NULL); |
125 | 0 | } |
126 | 0 | lh->nalloc = n; |
127 | 0 | lh->n = 0; |
128 | 0 | lh->direction = direction; |
129 | 0 | return lh; |
130 | 0 | } |
131 | | |
132 | | |
133 | | /*! |
134 | | * \brief lheapDestroy() |
135 | | * |
136 | | * \param[in,out] plh will be set to null before returning |
137 | | * \param[in] freeflag TRUE to free each remaining struct in the array |
138 | | * \return void |
139 | | * |
140 | | * <pre> |
141 | | * Notes: |
142 | | * (1) Use %freeflag == TRUE when the items in the array can be |
143 | | * simply destroyed using free. If those items require their |
144 | | * own destroy function, they must be destroyed before |
145 | | * calling this function, and then this function is called |
146 | | * with %freeflag == FALSE. |
147 | | * (2) To destroy the lheap, we destroy the ptr array, then |
148 | | * the lheap, and then null the contents of the input ptr. |
149 | | * </pre> |
150 | | */ |
151 | | void |
152 | | lheapDestroy(L_HEAP **plh, |
153 | | l_int32 freeflag) |
154 | 0 | { |
155 | 0 | l_int32 i; |
156 | 0 | L_HEAP *lh; |
157 | |
|
158 | 0 | if (plh == NULL) { |
159 | 0 | L_WARNING("ptr address is NULL\n", __func__); |
160 | 0 | return; |
161 | 0 | } |
162 | 0 | if ((lh = *plh) == NULL) |
163 | 0 | return; |
164 | | |
165 | 0 | if (freeflag) { /* free each struct in the array */ |
166 | 0 | for (i = 0; i < lh->n; i++) |
167 | 0 | LEPT_FREE(lh->array[i]); |
168 | 0 | } else if (lh->n > 0) { /* freeflag == FALSE but elements exist on array */ |
169 | 0 | L_WARNING("memory leak of %d items in lheap!\n", __func__, lh->n); |
170 | 0 | } |
171 | |
|
172 | 0 | if (lh->array) |
173 | 0 | LEPT_FREE(lh->array); |
174 | 0 | LEPT_FREE(lh); |
175 | 0 | *plh = NULL; |
176 | 0 | } |
177 | | |
178 | | /*--------------------------------------------------------------------------* |
179 | | * Operations to add/remove to/from the heap * |
180 | | *--------------------------------------------------------------------------*/ |
181 | | /*! |
182 | | * \brief lheapAdd() |
183 | | * |
184 | | * \param[in] lh heap |
185 | | * \param[in] item to be added to the tail of the heap |
186 | | * \return 0 if OK, 1 on error |
187 | | */ |
188 | | l_ok |
189 | | lheapAdd(L_HEAP *lh, |
190 | | void *item) |
191 | 0 | { |
192 | 0 | if (!lh) |
193 | 0 | return ERROR_INT("lh not defined", __func__, 1); |
194 | 0 | if (!item) |
195 | 0 | return ERROR_INT("item not defined", __func__, 1); |
196 | | |
197 | | /* If necessary, expand the allocated array by a factor of 2 */ |
198 | 0 | if (lh->n >= lh->nalloc) { |
199 | 0 | if (lheapExtendArray(lh)) |
200 | 0 | return ERROR_INT("extension failed", __func__, 1); |
201 | 0 | } |
202 | | |
203 | | /* Add the item */ |
204 | 0 | lh->array[lh->n] = item; |
205 | 0 | lh->n++; |
206 | | |
207 | | /* Restore the heap */ |
208 | 0 | lheapSwapUp(lh, lh->n - 1); |
209 | 0 | return 0; |
210 | 0 | } |
211 | | |
212 | | |
213 | | /*! |
214 | | * \brief lheapExtendArray() |
215 | | * |
216 | | * \param[in] lh heap |
217 | | * \return 0 if OK, 1 on error |
218 | | */ |
219 | | static l_int32 |
220 | | lheapExtendArray(L_HEAP *lh) |
221 | 0 | { |
222 | 0 | if (!lh) |
223 | 0 | return ERROR_INT("lh not defined", __func__, 1); |
224 | | |
225 | 0 | if ((lh->array = (void **)reallocNew((void **)&lh->array, |
226 | 0 | sizeof(void *) * lh->nalloc, |
227 | 0 | 2 * sizeof(void *) * lh->nalloc)) == NULL) |
228 | 0 | return ERROR_INT("new ptr array not returned", __func__, 1); |
229 | | |
230 | 0 | lh->nalloc = 2 * lh->nalloc; |
231 | 0 | return 0; |
232 | 0 | } |
233 | | |
234 | | |
235 | | /*! |
236 | | * \brief lheapRemove() |
237 | | * |
238 | | * \param[in] lh heap |
239 | | * \return ptr to item popped from the root of the heap, |
240 | | * or NULL if the heap is empty or on error |
241 | | */ |
242 | | void * |
243 | | lheapRemove(L_HEAP *lh) |
244 | 0 | { |
245 | 0 | void *item; |
246 | |
|
247 | 0 | if (!lh) |
248 | 0 | return (void *)ERROR_PTR("lh not defined", __func__, NULL); |
249 | | |
250 | 0 | if (lh->n == 0) |
251 | 0 | return NULL; |
252 | | |
253 | 0 | item = lh->array[0]; |
254 | 0 | lh->array[0] = lh->array[lh->n - 1]; /* move last to the head */ |
255 | 0 | lh->array[lh->n - 1] = NULL; /* set ptr to null */ |
256 | 0 | lh->n--; |
257 | |
|
258 | 0 | lheapSwapDown(lh); /* restore the heap */ |
259 | 0 | return item; |
260 | 0 | } |
261 | | |
262 | | |
263 | | /*--------------------------------------------------------------------------* |
264 | | * Other accessors * |
265 | | *--------------------------------------------------------------------------*/ |
266 | | /*! |
267 | | * \brief lheapGetCount() |
268 | | * |
269 | | * \param[in] lh heap |
270 | | * \return count, or 0 on error |
271 | | */ |
272 | | l_int32 |
273 | | lheapGetCount(L_HEAP *lh) |
274 | 0 | { |
275 | 0 | if (!lh) |
276 | 0 | return ERROR_INT("lh not defined", __func__, 0); |
277 | | |
278 | 0 | return lh->n; |
279 | 0 | } |
280 | | |
281 | | |
282 | | /*! |
283 | | * \brief lheapGetElement() |
284 | | * |
285 | | * \param[in] lh heap |
286 | | * \param[in] index into the internal heap array |
287 | | * \return ptr to the element at array[index], or NULL on error |
288 | | * |
289 | | * <pre> |
290 | | * Notes: |
291 | | * (1) This is useful for retrieving an arbitrary element in the |
292 | | * heap array without disturbing the heap. It allows all the |
293 | | * elements on the heap to be queried in linear time; for |
294 | | * example, to find the min or max of some value. |
295 | | * (2) The retrieved element is owned by the heap. Do not destroy it. |
296 | | * </pre> |
297 | | */ |
298 | | void * |
299 | | lheapGetElement(L_HEAP *lh, |
300 | | l_int32 index) |
301 | 0 | { |
302 | 0 | if (!lh) |
303 | 0 | return ERROR_PTR("lh not defined", __func__, NULL); |
304 | 0 | if (index < 0 || index >= lh->n) |
305 | 0 | return ERROR_PTR("invalid index", __func__, NULL); |
306 | | |
307 | 0 | return (void *)lh->array[index]; |
308 | 0 | } |
309 | | |
310 | | |
311 | | /*--------------------------------------------------------------------------* |
312 | | * Heap sort * |
313 | | *--------------------------------------------------------------------------*/ |
314 | | /*! |
315 | | * \brief lheapSort() |
316 | | * |
317 | | * \param[in] lh heap, with internal array |
318 | | * \return 0 if OK, 1 on error |
319 | | * |
320 | | * <pre> |
321 | | * Notes: |
322 | | * (1) This sorts an array into heap order. If the heap is already |
323 | | * in heap order for the direction given, this has no effect. |
324 | | * </pre> |
325 | | */ |
326 | | l_ok |
327 | | lheapSort(L_HEAP *lh) |
328 | 0 | { |
329 | 0 | l_int32 i; |
330 | |
|
331 | 0 | if (!lh) |
332 | 0 | return ERROR_INT("lh not defined", __func__, 1); |
333 | | |
334 | 0 | for (i = 0; i < lh->n; i++) |
335 | 0 | lheapSwapUp(lh, i); |
336 | |
|
337 | 0 | return 0; |
338 | 0 | } |
339 | | |
340 | | |
341 | | /*! |
342 | | * \brief lheapSortStrictOrder() |
343 | | * |
344 | | * \param[in] lh heap, with internal array |
345 | | * \return 0 if OK, 1 on error |
346 | | * |
347 | | * <pre> |
348 | | * Notes: |
349 | | * (1) This sorts a heap into strict order. |
350 | | * (2) For each element, starting at the end of the array and |
351 | | * working forward, the element is swapped with the head |
352 | | * element and then allowed to swap down onto a heap of |
353 | | * size reduced by one. The result is that the heap is |
354 | | * reversed but in strict order. The array elements are |
355 | | * then reversed to put it in the original order. |
356 | | * </pre> |
357 | | */ |
358 | | l_ok |
359 | | lheapSortStrictOrder(L_HEAP *lh) |
360 | 0 | { |
361 | 0 | l_int32 i, index, size; |
362 | |
|
363 | 0 | if (!lh) |
364 | 0 | return ERROR_INT("lh not defined", __func__, 1); |
365 | | |
366 | | /* Start from a sorted heap */ |
367 | 0 | lheapSort(lh); |
368 | |
|
369 | 0 | size = lh->n; /* save the actual size */ |
370 | 0 | for (i = 0; i < size; i++) { |
371 | 0 | index = size - i; |
372 | 0 | SWAP_ITEMS(0, index - 1); |
373 | 0 | lh->n--; /* reduce the apparent heap size by 1 */ |
374 | 0 | lheapSwapDown(lh); |
375 | 0 | } |
376 | 0 | lh->n = size; /* restore the size */ |
377 | |
|
378 | 0 | for (i = 0; i < size / 2; i++) /* reverse */ |
379 | 0 | SWAP_ITEMS(i, size - i - 1); |
380 | |
|
381 | 0 | return 0; |
382 | 0 | } |
383 | | |
384 | | |
385 | | /*--------------------------------------------------------------------------* |
386 | | * Low-level heap operations * |
387 | | *--------------------------------------------------------------------------*/ |
388 | | /*! |
389 | | * \brief lheapSwapUp() |
390 | | * |
391 | | * \param[in] lh heap |
392 | | * \param[in] index of array corresponding to node to be swapped up |
393 | | * \return 0 if OK, 1 on error |
394 | | * |
395 | | * <pre> |
396 | | * Notes: |
397 | | * (1) This is called after a new item is put on the heap, at the |
398 | | * bottom of a complete tree. |
399 | | * (2) To regain the heap order, we let it bubble up, |
400 | | * iteratively swapping with its parent, until it either |
401 | | * reaches the root of the heap or it finds a parent that |
402 | | * is in the correct position already vis-a-vis the child. |
403 | | * </pre> |
404 | | */ |
405 | | static l_ok |
406 | | lheapSwapUp(L_HEAP *lh, |
407 | | l_int32 index) |
408 | 0 | { |
409 | 0 | l_int32 ip; /* index to heap for parent; 1 larger than array index */ |
410 | 0 | l_int32 ic; /* index into heap for child */ |
411 | 0 | l_float32 valp, valc; |
412 | |
|
413 | 0 | if (!lh) |
414 | 0 | return ERROR_INT("lh not defined", __func__, 1); |
415 | 0 | if (index < 0 || index >= lh->n) |
416 | 0 | return ERROR_INT("invalid index", __func__, 1); |
417 | | |
418 | 0 | ic = index + 1; /* index into heap: add 1 to array index */ |
419 | 0 | if (lh->direction == L_SORT_INCREASING) { |
420 | 0 | while (1) { |
421 | 0 | if (ic == 1) /* root of heap */ |
422 | 0 | break; |
423 | 0 | ip = ic / 2; |
424 | 0 | valc = *(l_float32 *)(lh->array[ic - 1]); |
425 | 0 | valp = *(l_float32 *)(lh->array[ip - 1]); |
426 | 0 | if (valp <= valc) |
427 | 0 | break; |
428 | 0 | SWAP_ITEMS(ip - 1, ic - 1); |
429 | 0 | ic = ip; |
430 | 0 | } |
431 | 0 | } else { /* lh->direction == L_SORT_DECREASING */ |
432 | 0 | while (1) { |
433 | 0 | if (ic == 1) /* root of heap */ |
434 | 0 | break; |
435 | 0 | ip = ic / 2; |
436 | 0 | valc = *(l_float32 *)(lh->array[ic - 1]); |
437 | 0 | valp = *(l_float32 *)(lh->array[ip - 1]); |
438 | 0 | if (valp >= valc) |
439 | 0 | break; |
440 | 0 | SWAP_ITEMS(ip - 1, ic - 1); |
441 | 0 | ic = ip; |
442 | 0 | } |
443 | 0 | } |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | |
448 | | /*! |
449 | | * \brief lheapSwapDown() |
450 | | * |
451 | | * \param[in] lh heap |
452 | | * \return 0 if OK, 1 on error |
453 | | * |
454 | | * <pre> |
455 | | * Notes: |
456 | | * (1) This is called after an item has been popped off the |
457 | | * root of the heap, and the last item in the heap has |
458 | | * been placed at the root. |
459 | | * (2) To regain the heap order, we let it bubble down, |
460 | | * iteratively swapping with one of its children. For a |
461 | | * decreasing sort, it swaps with the largest child; for |
462 | | * an increasing sort, the smallest. This continues until |
463 | | * it either reaches the lowest level in the heap, or the |
464 | | * parent finds that neither child should swap with it |
465 | | * (e.g., for a decreasing heap, the parent is larger |
466 | | * than or equal to both children). |
467 | | * </pre> |
468 | | */ |
469 | | static l_ok |
470 | | lheapSwapDown(L_HEAP *lh) |
471 | 0 | { |
472 | 0 | l_int32 ip; /* index to heap for parent; 1 larger than array index */ |
473 | 0 | l_int32 icr, icl; /* index into heap for left/right children */ |
474 | 0 | l_float32 valp, valcl, valcr; |
475 | |
|
476 | 0 | if (!lh) |
477 | 0 | return ERROR_INT("lh not defined", __func__, 1); |
478 | 0 | if (lheapGetCount(lh) < 1) |
479 | 0 | return 0; |
480 | | |
481 | 0 | ip = 1; /* index into top of heap: corresponds to array[0] */ |
482 | 0 | if (lh->direction == L_SORT_INCREASING) { |
483 | 0 | while (1) { |
484 | 0 | icl = 2 * ip; |
485 | 0 | if (icl > lh->n) |
486 | 0 | break; |
487 | 0 | valp = *(l_float32 *)(lh->array[ip - 1]); |
488 | 0 | valcl = *(l_float32 *)(lh->array[icl - 1]); |
489 | 0 | icr = icl + 1; |
490 | 0 | if (icr > lh->n) { /* only a left child; no iters below */ |
491 | 0 | if (valp > valcl) |
492 | 0 | SWAP_ITEMS(ip - 1, icl - 1); |
493 | 0 | break; |
494 | 0 | } else { /* both children exist; swap with the smallest if bigger */ |
495 | 0 | valcr = *(l_float32 *)(lh->array[icr - 1]); |
496 | 0 | if (valp <= valcl && valp <= valcr) /* smaller than both */ |
497 | 0 | break; |
498 | 0 | if (valcl <= valcr) { /* left smaller; swap */ |
499 | 0 | SWAP_ITEMS(ip - 1, icl - 1); |
500 | 0 | ip = icl; |
501 | 0 | } else { /* right smaller; swap */ |
502 | 0 | SWAP_ITEMS(ip - 1, icr - 1); |
503 | 0 | ip = icr; |
504 | 0 | } |
505 | 0 | } |
506 | 0 | } |
507 | 0 | } else { /* lh->direction == L_SORT_DECREASING */ |
508 | 0 | while (1) { |
509 | 0 | icl = 2 * ip; |
510 | 0 | if (icl > lh->n) |
511 | 0 | break; |
512 | 0 | valp = *(l_float32 *)(lh->array[ip - 1]); |
513 | 0 | valcl = *(l_float32 *)(lh->array[icl - 1]); |
514 | 0 | icr = icl + 1; |
515 | 0 | if (icr > lh->n) { /* only a left child; no iters below */ |
516 | 0 | if (valp < valcl) |
517 | 0 | SWAP_ITEMS(ip - 1, icl - 1); |
518 | 0 | break; |
519 | 0 | } else { /* both children exist; swap with the biggest if smaller */ |
520 | 0 | valcr = *(l_float32 *)(lh->array[icr - 1]); |
521 | 0 | if (valp >= valcl && valp >= valcr) /* bigger than both */ |
522 | 0 | break; |
523 | 0 | if (valcl >= valcr) { /* left bigger; swap */ |
524 | 0 | SWAP_ITEMS(ip - 1, icl - 1); |
525 | 0 | ip = icl; |
526 | 0 | } else { /* right bigger; swap */ |
527 | 0 | SWAP_ITEMS(ip - 1, icr - 1); |
528 | 0 | ip = icr; |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } |
532 | 0 | } |
533 | |
|
534 | 0 | return 0; |
535 | 0 | } |
536 | | |
537 | | |
538 | | /*---------------------------------------------------------------------* |
539 | | * Debug output * |
540 | | *---------------------------------------------------------------------*/ |
541 | | /*! |
542 | | * \brief lheapPrint() |
543 | | * |
544 | | * \param[in] fp file stream |
545 | | * \param[in] lh heap |
546 | | * \return 0 if OK; 1 on error |
547 | | */ |
548 | | l_ok |
549 | | lheapPrint(FILE *fp, |
550 | | L_HEAP *lh) |
551 | 0 | { |
552 | 0 | l_int32 i; |
553 | |
|
554 | 0 | if (!fp) |
555 | 0 | return ERROR_INT("stream not defined", __func__, 1); |
556 | 0 | if (!lh) |
557 | 0 | return ERROR_INT("lh not defined", __func__, 1); |
558 | | |
559 | 0 | fprintf(fp, "\n L_Heap: nalloc = %d, n = %d, array = %p\n", |
560 | 0 | lh->nalloc, lh->n, lh->array); |
561 | 0 | for (i = 0; i < lh->n; i++) |
562 | 0 | fprintf(fp, "keyval[%d] = %f\n", i, *(l_float32 *)lh->array[i]); |
563 | |
|
564 | 0 | return 0; |
565 | 0 | } |