diff options
author | Thomas Deutschmann <whissi@gentoo.org> | 2021-03-30 10:59:39 +0200 |
---|---|---|
committer | Thomas Deutschmann <whissi@gentoo.org> | 2021-04-01 00:04:14 +0200 |
commit | 5ff1d6955496b3cf9a35042c9ac35db43bc336b1 (patch) | |
tree | 6d470f7eb448f59f53e8df1010aec9dad8ce1f72 /leptonica/src/heap.c | |
parent | Import Ghostscript 9.53.1 (diff) | |
download | ghostscript-gpl-patches-5ff1d6955496b3cf9a35042c9ac35db43bc336b1.tar.gz ghostscript-gpl-patches-5ff1d6955496b3cf9a35042c9ac35db43bc336b1.tar.bz2 ghostscript-gpl-patches-5ff1d6955496b3cf9a35042c9ac35db43bc336b1.zip |
Import Ghostscript 9.54ghostscript-9.54
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'leptonica/src/heap.c')
-rw-r--r-- | leptonica/src/heap.c | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/leptonica/src/heap.c b/leptonica/src/heap.c new file mode 100644 index 00000000..99f80c4d --- /dev/null +++ b/leptonica/src/heap.c @@ -0,0 +1,589 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + +/*! + * \file heap.c + * <pre> + * + * Create/Destroy L_Heap + * L_HEAP *lheapCreate() + * void lheapDestroy() + * + * Operations to add/remove to/from the heap + * l_int32 lheapAdd() + * static l_int32 lheapExtendArray() + * void *lheapRemove() + * + * Other accessors + * l_int32 lheapGetCount() + * void *lheapGetElement() + * + * Heap sort + * l_int32 lheapSort() + * l_int32 lheapSortStrictOrder() + * + * Low-level heap operations + * static l_int32 lheapSwapUp() + * static l_int32 lheapSwapDown() + * + * Debug output + * l_int32 lheapPrint() + * + * The L_Heap is useful to implement a priority queue, that is sorted + * on a key in each element of the heap. The heap is an array + * of nearly arbitrary structs, with a l_float32 the first field. + * This field is the key on which the heap is sorted. + * + * Internally, we keep track of the heap size, n. The item at the + * root of the heap is at the head of the array. Items are removed + * from the head of the array and added to the end of the array. + * When an item is removed from the head, the item at the end + * of the array is moved to the head. When items are either + * added or removed, it is usually necessary to swap array items + * to restore the heap order. It is guaranteed that the number + * of swaps does not exceed log(n). + * + * -------------------------- N.B. ------------------------------ + * The items on the heap (or, equivalently, in the array) are cast + * to void*. Their key is a l_float32, and it is REQUIRED that the + * key be the first field in the struct. That allows us to get the + * key by simply dereferencing the struct. Alternatively, we could + * choose (but don't) to pass an application-specific comparison + * function into the heap operation functions. + * -------------------------- N.B. ------------------------------ + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include "allheaders.h" + + /* Bounds on initial array size */ +static const l_uint32 MaxPtrArraySize = 100000; +static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ + +#define SWAP_ITEMS(i, j) { void *tempitem = lh->array[(i)]; \ + lh->array[(i)] = lh->array[(j)]; \ + lh->array[(j)] = tempitem; } + + /* Static functions */ +static l_int32 lheapExtendArray(L_HEAP *lh); +static l_ok lheapSwapUp(L_HEAP *lh, l_int32 index); +static l_ok lheapSwapDown(L_HEAP *lh); + + +/*--------------------------------------------------------------------------* + * L_Heap create/destroy * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapCreate() + * + * \param[in] n size of ptr array to be alloc'd; use 0 for default + * \param[in] direction L_SORT_INCREASING, L_SORT_DECREASING + * \return lheap, or NULL on error + */ +L_HEAP * +lheapCreate(l_int32 n, + l_int32 direction) +{ +L_HEAP *lh; + + PROCNAME("lheapCreate"); + + if (n < InitialPtrArraySize || n > MaxPtrArraySize) + n = InitialPtrArraySize; + + /* Allocate ptr array and initialize counters. */ + lh = (L_HEAP *)LEPT_CALLOC(1, sizeof(L_HEAP)); + if ((lh->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) { + lheapDestroy(&lh, FALSE); + return (L_HEAP *)ERROR_PTR("ptr array not made", procName, NULL); + } + lh->nalloc = n; + lh->n = 0; + lh->direction = direction; + return lh; +} + + +/*! + * \brief lheapDestroy() + * + * \param[in,out] plh will be set to null before returning + * \param[in] freeflag TRUE to free each remaining struct in the array + * \return void + * + * <pre> + * Notes: + * (1) Use %freeflag == TRUE when the items in the array can be + * simply destroyed using free. If those items require their + * own destroy function, they must be destroyed before + * calling this function, and then this function is called + * with %freeflag == FALSE. + * (2) To destroy the lheap, we destroy the ptr array, then + * the lheap, and then null the contents of the input ptr. + * </pre> + */ +void +lheapDestroy(L_HEAP **plh, + l_int32 freeflag) +{ +l_int32 i; +L_HEAP *lh; + + PROCNAME("lheapDestroy"); + + if (plh == NULL) { + L_WARNING("ptr address is NULL\n", procName); + return; + } + if ((lh = *plh) == NULL) + return; + + if (freeflag) { /* free each struct in the array */ + for (i = 0; i < lh->n; i++) + LEPT_FREE(lh->array[i]); + } else if (lh->n > 0) { /* freeflag == FALSE but elements exist on array */ + L_WARNING("memory leak of %d items in lheap!\n", procName, lh->n); + } + + if (lh->array) + LEPT_FREE(lh->array); + LEPT_FREE(lh); + *plh = NULL; +} + +/*--------------------------------------------------------------------------* + * Operations to add/remove to/from the heap * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapAdd() + * + * \param[in] lh heap + * \param[in] item to be added to the tail of the heap + * \return 0 if OK, 1 on error + */ +l_ok +lheapAdd(L_HEAP *lh, + void *item) +{ + PROCNAME("lheapAdd"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + if (!item) + return ERROR_INT("item not defined", procName, 1); + + /* If necessary, expand the allocated array by a factor of 2 */ + if (lh->n >= lh->nalloc) { + if (lheapExtendArray(lh)) + return ERROR_INT("extension failed", procName, 1); + } + + /* Add the item */ + lh->array[lh->n] = item; + lh->n++; + + /* Restore the heap */ + lheapSwapUp(lh, lh->n - 1); + return 0; +} + + +/*! + * \brief lheapExtendArray() + * + * \param[in] lh heap + * \return 0 if OK, 1 on error + */ +static l_int32 +lheapExtendArray(L_HEAP *lh) +{ + PROCNAME("lheapExtendArray"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + if ((lh->array = (void **)reallocNew((void **)&lh->array, + sizeof(void *) * lh->nalloc, + 2 * sizeof(void *) * lh->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", procName, 1); + + lh->nalloc = 2 * lh->nalloc; + return 0; +} + + +/*! + * \brief lheapRemove() + * + * \param[in] lh heap + * \return ptr to item popped from the root of the heap, + * or NULL if the heap is empty or on error + */ +void * +lheapRemove(L_HEAP *lh) +{ +void *item; + + PROCNAME("lheapRemove"); + + if (!lh) + return (void *)ERROR_PTR("lh not defined", procName, NULL); + + if (lh->n == 0) + return NULL; + + item = lh->array[0]; + lh->array[0] = lh->array[lh->n - 1]; /* move last to the head */ + lh->array[lh->n - 1] = NULL; /* set ptr to null */ + lh->n--; + + lheapSwapDown(lh); /* restore the heap */ + return item; +} + + +/*--------------------------------------------------------------------------* + * Other accessors * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapGetCount() + * + * \param[in] lh heap + * \return count, or 0 on error + */ +l_int32 +lheapGetCount(L_HEAP *lh) +{ + PROCNAME("lheapGetCount"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 0); + + return lh->n; +} + + +/*! + * \brief lheapGetElement() + * + * \param[in] lh heap + * \param[in] index into the internal heap array + * \return ptr to the element at array[index], or NULL on error + * + * <pre> + * Notes: + * (1) This is useful for retrieving an arbitrary element in the + * heap array without disturbing the heap. It allows all the + * elements on the heap to be queried in linear time; for + * example, to find the min or max of some value. + * (2) Tbe retrieved element is owned by the heap. Do not destroy it. + * </pre> + */ +void * +lheapGetElement(L_HEAP *lh, + l_int32 index) +{ + PROCNAME("lheapGetElement"); + + if (!lh) + return ERROR_PTR("lh not defined", procName, NULL); + if (index < 0 || index >= lh->n) + return ERROR_PTR("invalid index", procName, NULL); + + return (void *)lh->array[index]; +} + + +/*--------------------------------------------------------------------------* + * Heap sort * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapSort() + * + * \param[in] lh heap, with internal array + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This sorts an array into heap order. If the heap is already + * in heap order for the direction given, this has no effect. + * </pre> + */ +l_ok +lheapSort(L_HEAP *lh) +{ +l_int32 i; + + PROCNAME("lheapSort"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + for (i = 0; i < lh->n; i++) + lheapSwapUp(lh, i); + + return 0; +} + + +/*! + * \brief lheapSortStrictOrder() + * + * \param[in] lh heap, with internal array + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This sorts a heap into strict order. + * (2) For each element, starting at the end of the array and + * working forward, the element is swapped with the head + * element and then allowed to swap down onto a heap of + * size reduced by one. The result is that the heap is + * reversed but in strict order. The array elements are + * then reversed to put it in the original order. + * </pre> + */ +l_ok +lheapSortStrictOrder(L_HEAP *lh) +{ +l_int32 i, index, size; + + PROCNAME("lheapSortStrictOrder"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + /* Start from a sorted heap */ + lheapSort(lh); + + size = lh->n; /* save the actual size */ + for (i = 0; i < size; i++) { + index = size - i; + SWAP_ITEMS(0, index - 1); + lh->n--; /* reduce the apparent heap size by 1 */ + lheapSwapDown(lh); + } + lh->n = size; /* restore the size */ + + for (i = 0; i < size / 2; i++) /* reverse */ + SWAP_ITEMS(i, size - i - 1); + + return 0; +} + + +/*--------------------------------------------------------------------------* + * Low-level heap operations * + *--------------------------------------------------------------------------*/ +/*! + * \brief lheapSwapUp() + * + * \param[in] lh heap + * \param[in] index of array corresponding to node to be swapped up + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is called after a new item is put on the heap, at the + * bottom of a complete tree. + * (2) To regain the heap order, we let it bubble up, + * iteratively swapping with its parent, until it either + * reaches the root of the heap or it finds a parent that + * is in the correct position already vis-a-vis the child. + * </pre> + */ +static l_ok +lheapSwapUp(L_HEAP *lh, + l_int32 index) +{ +l_int32 ip; /* index to heap for parent; 1 larger than array index */ +l_int32 ic; /* index into heap for child */ +l_float32 valp, valc; + + PROCNAME("lheapSwapUp"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + if (index < 0 || index >= lh->n) + return ERROR_INT("invalid index", procName, 1); + + ic = index + 1; /* index into heap: add 1 to array index */ + if (lh->direction == L_SORT_INCREASING) { + while (1) { + if (ic == 1) /* root of heap */ + break; + ip = ic / 2; + valc = *(l_float32 *)(lh->array[ic - 1]); + valp = *(l_float32 *)(lh->array[ip - 1]); + if (valp <= valc) + break; + SWAP_ITEMS(ip - 1, ic - 1); + ic = ip; + } + } else { /* lh->direction == L_SORT_DECREASING */ + while (1) { + if (ic == 1) /* root of heap */ + break; + ip = ic / 2; + valc = *(l_float32 *)(lh->array[ic - 1]); + valp = *(l_float32 *)(lh->array[ip - 1]); + if (valp >= valc) + break; + SWAP_ITEMS(ip - 1, ic - 1); + ic = ip; + } + } + return 0; +} + + +/*! + * \brief lheapSwapDown() + * + * \param[in] lh heap + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This is called after an item has been popped off the + * root of the heap, and the last item in the heap has + * been placed at the root. + * (2) To regain the heap order, we let it bubble down, + * iteratively swapping with one of its children. For a + * decreasing sort, it swaps with the largest child; for + * an increasing sort, the smallest. This continues until + * it either reaches the lowest level in the heap, or the + * parent finds that neither child should swap with it + * (e.g., for a decreasing heap, the parent is larger + * than or equal to both children). + * </pre> + */ +static l_ok +lheapSwapDown(L_HEAP *lh) +{ +l_int32 ip; /* index to heap for parent; 1 larger than array index */ +l_int32 icr, icl; /* index into heap for left/right children */ +l_float32 valp, valcl, valcr; + + PROCNAME("lheapSwapDown"); + + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + if (lheapGetCount(lh) < 1) + return 0; + + ip = 1; /* index into top of heap: corresponds to array[0] */ + if (lh->direction == L_SORT_INCREASING) { + while (1) { + icl = 2 * ip; + if (icl > lh->n) + break; + valp = *(l_float32 *)(lh->array[ip - 1]); + valcl = *(l_float32 *)(lh->array[icl - 1]); + icr = icl + 1; + if (icr > lh->n) { /* only a left child; no iters below */ + if (valp > valcl) + SWAP_ITEMS(ip - 1, icl - 1); + break; + } else { /* both children exist; swap with the smallest if bigger */ + valcr = *(l_float32 *)(lh->array[icr - 1]); + if (valp <= valcl && valp <= valcr) /* smaller than both */ + break; + if (valcl <= valcr) { /* left smaller; swap */ + SWAP_ITEMS(ip - 1, icl - 1); + ip = icl; + } else { /* right smaller; swap */ + SWAP_ITEMS(ip - 1, icr - 1); + ip = icr; + } + } + } + } else { /* lh->direction == L_SORT_DECREASING */ + while (1) { + icl = 2 * ip; + if (icl > lh->n) + break; + valp = *(l_float32 *)(lh->array[ip - 1]); + valcl = *(l_float32 *)(lh->array[icl - 1]); + icr = icl + 1; + if (icr > lh->n) { /* only a left child; no iters below */ + if (valp < valcl) + SWAP_ITEMS(ip - 1, icl - 1); + break; + } else { /* both children exist; swap with the biggest if smaller */ + valcr = *(l_float32 *)(lh->array[icr - 1]); + if (valp >= valcl && valp >= valcr) /* bigger than both */ + break; + if (valcl >= valcr) { /* left bigger; swap */ + SWAP_ITEMS(ip - 1, icl - 1); + ip = icl; + } else { /* right bigger; swap */ + SWAP_ITEMS(ip - 1, icr - 1); + ip = icr; + } + } + } + } + + return 0; +} + + +/*---------------------------------------------------------------------* + * Debug output * + *---------------------------------------------------------------------*/ +/*! + * \brief lheapPrint() + * + * \param[in] fp file stream + * \param[in] lh heap + * \return 0 if OK; 1 on error + */ +l_ok +lheapPrint(FILE *fp, + L_HEAP *lh) +{ +l_int32 i; + + PROCNAME("lheapPrint"); + + if (!fp) + return ERROR_INT("stream not defined", procName, 1); + if (!lh) + return ERROR_INT("lh not defined", procName, 1); + + fprintf(fp, "\n L_Heap: nalloc = %d, n = %d, array = %p\n", + lh->nalloc, lh->n, lh->array); + for (i = 0; i < lh->n; i++) + fprintf(fp, "keyval[%d] = %f\n", i, *(l_float32 *)lh->array[i]); + + return 0; +} |