Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/ptra.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/ptra.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,968 @@ +/*====================================================================* + - 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 ptra.c + * <pre> + * + * Ptra creation and destruction + * L_PTRA *ptraCreate() + * void *ptraDestroy() + * + * Add/insert/remove/replace generic ptr object + * l_int32 ptraAdd() + * static l_int32 ptraExtendArray() + * l_int32 ptraInsert() + * void *ptraRemove() + * void *ptraRemoveLast() + * void *ptraReplace() + * l_int32 ptraSwap() + * l_int32 ptraCompactArray() + * + * Other array operations + * l_int32 ptraReverse() + * l_int32 ptraJoin() + * + * Simple Ptra accessors + * l_int32 ptraGetMaxIndex() + * l_int32 ptraGetActualCount() + * void *ptraGetPtrToItem() + * + * Ptraa creation and destruction + * L_PTRAA *ptraaCreate() + * void *ptraaDestroy() + * + * Ptraa accessors + * l_int32 ptraaGetSize() + * l_int32 ptraaInsertPtra() + * L_PTRA *ptraaGetPtra() + * + * Ptraa conversion + * L_PTRA *ptraaFlattenToPtra() + * + * Notes on the Ptra: + * + * (1) The Ptra is a struct, not an array. Always use the accessors + * in this file, never the fields directly. + * (2) Items can be placed anywhere in the allocated ptr array, + * including one index beyond the last ptr (in which case the + * ptr array is realloc'd). + * (3) Thus, the items on the ptr array need not be compacted. In + * general there will be null pointers in the ptr array. + * (4) A compacted array will remain compacted on removal if + * arbitrary items are removed with compaction, or if items + * are removed from the end of the array. + * (5) For addition to and removal from the end of the array, this + * functions exactly like a stack, and with the same O(1) cost. + * (6) This differs from the generic stack in that we allow + * random access for insertion, removal and replacement. + * Removal can be done without compacting the array. + * Insertion into a null ptr in the array has no effect on + * the other pointers, but insertion into a location already + * occupied by an item has a cost proportional to the + * distance to the next null ptr in the array. + * (7) Null ptrs are valid input args for both insertion and + * replacement; this allows arbitrary swapping. + * (8) The item in the array with the largest index is at pa->imax. + * This can be any value from -1 (initialized; all array ptrs + * are null) up to pa->nalloc - 1 (the last ptr in the array). + * (9) In referring to the array: the first ptr is the "top" or + * "beginning"; the last pointer is the "bottom" or "end"; + * items are shifted "up" towards the top when compaction occurs; + * and items are shifted "down" towards the bottom when forced to + * move due to an insertion. + * (10) It should be emphasized that insertion, removal and replacement + * are general: + * * You can insert an item into any ptr location in the + * allocated ptr array, as well as into the next ptr address + * beyond the allocated array (in which case a realloc will occur). + * * You can remove or replace an item from any ptr location + * in the allocated ptr array. + * * When inserting into an occupied location, you have + * three options for downshifting. + * * When removing, you can either leave the ptr null or + * compact the array. + * + * Notes on the Ptraa: + * + * (1) The Ptraa is a fixed size ptr array for holding Ptra. + * In that respect, it is different from other pointer arrays, which + * are extensible and grow using the *Add*() functions. + * (2) In general, the Ptra ptrs in the Ptraa can be randomly occupied. + * A typical usage is to allow an O(n) horizontal sort of Pix, + * where the size of the Ptra array is the width of the image, + * and each Ptra is an array of all the Pix at a specific x location. + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + + /* Bounds on initial array size */ +LEPT_DLL const l_uint32 MaxInitPtraSize = 1000001; +static const l_int32 DefaultInitPtraSize = 20; /*!< n'importe quoi */ + + /* Static function */ +static l_int32 ptraExtendArray(L_PTRA *pa); + +/*--------------------------------------------------------------------------* + * Ptra creation and destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraCreate() + * + * \param[in] n size of ptr array to be alloc'd; use 0 for default + * \return pa, or NULL on error + */ +L_PTRA * +ptraCreate(l_int32 n) +{ +L_PTRA *pa; + + if (n > (l_int32)MaxInitPtraSize) { + L_ERROR("n = %d > maxsize = %d\n", __func__, n, MaxInitPtraSize); + return NULL; + } + if (n <= 0) n = DefaultInitPtraSize; + + pa = (L_PTRA *)LEPT_CALLOC(1, sizeof(L_PTRA)); + if ((pa->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) { + ptraDestroy(&pa, 0, 0); + return (L_PTRA *)ERROR_PTR("ptr array not made", __func__, NULL); + } + pa->nalloc = n; + pa->imax = -1; + pa->nactual = 0; + return pa; +} + + +/*! + * \brief ptraDestroy() + * + * \param[in,out] ppa will be set to null before returning + * \param[in] freeflag TRUE to free each remaining item in the array + * \param[in] warnflag TRUE to warn if any remaining items + * are not destroyed + * \return void + * + * <pre> + * Notes: + * (1) If %freeflag == TRUE, frees each item in the array. + * (2) If %freeflag == FALSE and %warnflag == TRUE, and there are + * items on the array, this gives a warning and destroys the array. + * If these items are not owned elsewhere, this will cause + * a memory leak of all the items that were on the array. + * So if the items are not owned elsewhere and require their + * own destroy function, they must be destroyed before the ptra. + * (3) If %warnflag == FALSE, no warnings will be issued. This is + * useful if the items are owned elsewhere, such as a + * PixMemoryStore(). + * (4) To destroy the ptra, we destroy the ptr array, then + * the ptra, and then null the contents of the input ptr. + * </pre> + */ +void +ptraDestroy(L_PTRA **ppa, + l_int32 freeflag, + l_int32 warnflag) +{ +l_int32 i, nactual; +void *item; +L_PTRA *pa; + + if (ppa == NULL) { + L_WARNING("ptr address is NULL\n", __func__); + return; + } + if ((pa = *ppa) == NULL) + return; + + ptraGetActualCount(pa, &nactual); + if (nactual > 0) { + if (freeflag) { + for (i = 0; i <= pa->imax; i++) { + if ((item = ptraRemove(pa, i, L_NO_COMPACTION)) != NULL) + LEPT_FREE(item); + } + } else if (warnflag) { + L_WARNING("potential memory leak of %d items in ptra\n", + __func__, nactual); + } + } + + LEPT_FREE(pa->array); + LEPT_FREE(pa); + *ppa = NULL; +} + + +/*--------------------------------------------------------------------------* + * Add/insert/remove/replace generic ptr object * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraAdd() + * + * \param[in] pa ptra + * \param[in] item generic ptr to a struct + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This adds the element to the next location beyond imax, + * which is the largest occupied ptr in the array. This is + * what you expect from a stack, where all ptrs up to and + * including imax are occupied, but here the occuption of + * items in the array is entirely arbitrary. + * </pre> + */ +l_ok +ptraAdd(L_PTRA *pa, + void *item) +{ +l_int32 imax; + + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + if (!item) + return ERROR_INT("item not defined", __func__, 1); + + ptraGetMaxIndex(pa, &imax); + if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) + return ERROR_INT("extension failure", __func__, 1); + pa->array[imax + 1] = (void *)item; + pa->imax++; + pa->nactual++; + return 0; +} + + +/*! + * \brief ptraExtendArray() + * + * \param[in] pa + * \return 0 if OK, 1 on error + */ +static l_int32 +ptraExtendArray(L_PTRA *pa) +{ + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + + if ((pa->array = (void **)reallocNew((void **)&pa->array, + sizeof(void *) * pa->nalloc, + 2 * sizeof(void *) * pa->nalloc)) == NULL) + return ERROR_INT("new ptr array not returned", __func__, 1); + + pa->nalloc *= 2; + return 0; +} + + +/*! + * \brief ptraInsert() + * + * \param[in] pa ptra + * \param[in] index location in ptra to insert new value + * \param[in] item generic ptr to a struct; can be null + * \param[in] shiftflag L_AUTO_DOWNSHIFT, L_MIN_DOWNSHIFT, L_FULL_DOWNSHIFT + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This checks first to see if the location is valid, and + * then if there is presently an item there. If there is not, + * it is simply inserted into that location. + * (2) If there is an item at the insert location, items must be + * moved down to make room for the insert. In the downward + * shift there are three options, given by %shiftflag. + * ~ If %shiftflag == L_AUTO_DOWNSHIFT, a decision is made + * whether, in a cascade of items, to downshift a minimum + * amount or for all items above %index. The decision is + * based on the expectation of finding holes (null ptrs) + * between %index and the bottom of the array. + * Assuming the holes are distributed uniformly, if 2 or more + * holes are expected, we do a minimum shift. + * ~ If %shiftflag == L_MIN_DOWNSHIFT, the downward shifting + * cascade of items progresses a minimum amount, until + * the first empty slot is reached. This mode requires + * some computation before the actual shifting is done. + * ~ If %shiftflag == L_FULL_DOWNSHIFT, a shifting cascade is + * performed where pa[i] --> pa[i + 1] for all i >= index. + * Then, the item is inserted at pa[index]. + * (3) If you are not using L_AUTO_DOWNSHIFT, the rule of thumb is + * to use L_FULL_DOWNSHIFT if the array is compacted (each + * element points to an item), and to use L_MIN_DOWNSHIFT + * if there are a significant number of null pointers. + * There is no penalty to using L_MIN_DOWNSHIFT for a + * compacted array, however, because the full shift is required + * and we don't do the O(n) computation to look for holes. + * (4) This should not be used repeatedly on large arrays, + * because the function is generally O(n). + * (5) However, it can be used repeatedly if we start with an empty + * ptr array and insert only once at each location. For example, + * you can support an array of Numa, where at each ptr location + * you store either 0 or 1 Numa, and the Numa can be added + * randomly to the ptr array. + * </pre> + */ +l_ok +ptraInsert(L_PTRA *pa, + l_int32 index, + void *item, + l_int32 shiftflag) +{ +l_int32 i, ihole, imax; +l_float32 nexpected; + + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + if (index < 0 || index > pa->nalloc) + return ERROR_INT("index not in [0 ... nalloc]", __func__, 1); + if (shiftflag != L_AUTO_DOWNSHIFT && shiftflag != L_MIN_DOWNSHIFT && + shiftflag != L_FULL_DOWNSHIFT) + return ERROR_INT("invalid shiftflag", __func__, 1); + + if (item) pa->nactual++; + if (index == pa->nalloc) { /* can happen when index == n */ + if (ptraExtendArray(pa)) + return ERROR_INT("extension failure", __func__, 1); + } + + /* We are inserting into a hole or adding to the end of the array. + * No existing items are moved. */ + ptraGetMaxIndex(pa, &imax); + if (pa->array[index] == NULL) { + pa->array[index] = item; + if (item && index > imax) /* new item put beyond max so far */ + pa->imax = index; + return 0; + } + + /* We are inserting at the location of an existing item, + * forcing the existing item and those below to shift down. + * First, extend the array automatically if the last element + * (nalloc - 1) is occupied (imax). This may not be necessary + * in every situation, but only an anomalous sequence of insertions + * into the array would cause extra ptr allocation. */ + if (imax >= pa->nalloc - 1 && ptraExtendArray(pa)) + return ERROR_INT("extension failure", __func__, 1); + + /* If there are no holes, do a full downshift. + * Otherwise, if L_AUTO_DOWNSHIFT, use the expected number + * of holes between index and n to determine the shift mode */ + if (imax + 1 == pa->nactual) { + shiftflag = L_FULL_DOWNSHIFT; + } else if (shiftflag == L_AUTO_DOWNSHIFT) { + if (imax < 10) { + shiftflag = L_FULL_DOWNSHIFT; /* no big deal */ + } else { + nexpected = (l_float32)(imax - pa->nactual) * + (l_float32)((imax - index) / imax); + shiftflag = (nexpected > 2.0) ? L_MIN_DOWNSHIFT : L_FULL_DOWNSHIFT; + } + } + + if (shiftflag == L_MIN_DOWNSHIFT) { /* run down looking for a hole */ + for (ihole = index + 1; ihole <= imax; ihole++) { + if (pa->array[ihole] == NULL) + break; + } + } else { /* L_FULL_DOWNSHIFT */ + ihole = imax + 1; + } + + for (i = ihole; i > index; i--) + pa->array[i] = pa->array[i - 1]; + pa->array[index] = (void *)item; + if (ihole == imax + 1) /* the last item was shifted down */ + pa->imax++; + + return 0; +} + + +/*! + * \brief ptraRemove() + * + * \param[in] pa ptra + * \param[in] index element to be removed + * \param[in] flag L_NO_COMPACTION, L_COMPACTION + * \return item, or NULL on error + * + * <pre> + * Notes: + * (1) If flag == L_NO_COMPACTION, this removes the item and + * nulls the ptr on the array. If it takes the last item + * in the array, pa->n is reduced to the next item. + * (2) If flag == L_COMPACTION, this compacts the array for + * for all i >= index. It should not be used repeatedly on + * large arrays, because compaction is O(n). + * (3) The ability to remove without automatic compaction allows + * removal with cost O(1). + * </pre> + */ +void * +ptraRemove(L_PTRA *pa, + l_int32 index, + l_int32 flag) +{ +l_int32 i, imax, fromend, icurrent; +void *item; + + if (!pa) + return (void *)ERROR_PTR("pa not defined", __func__, NULL); + ptraGetMaxIndex(pa, &imax); + if (index < 0 || index > imax) + return (void *)ERROR_PTR("index not in [0 ... imax]", __func__, NULL); + + item = pa->array[index]; + if (item) + pa->nactual--; + pa->array[index] = NULL; + + /* If we took the last item, need to reduce pa->n */ + fromend = (index == imax); + if (fromend) { + for (i = index - 1; i >= 0; i--) { + if (pa->array[i]) + break; + } + pa->imax = i; + } + + /* Compact from index to the end of the array */ + if (!fromend && flag == L_COMPACTION) { + for (icurrent = index, i = index + 1; i <= imax; i++) { + if (pa->array[i]) + pa->array[icurrent++] = pa->array[i]; + } + pa->imax = icurrent - 1; + } + return item; +} + + +/*! + * \brief ptraRemoveLast() + * + * \param[in] pa ptra + * \return item, or NULL on error or if the array is empty + */ +void * +ptraRemoveLast(L_PTRA *pa) +{ +l_int32 imax; + + if (!pa) + return (void *)ERROR_PTR("pa not defined", __func__, NULL); + + /* Remove the last item in the array. No compaction is required. */ + ptraGetMaxIndex(pa, &imax); + if (imax >= 0) + return ptraRemove(pa, imax, L_NO_COMPACTION); + else /* empty */ + return NULL; +} + + +/*! + * \brief ptraReplace() + * + * \param[in] pa ptra + * \param[in] index element to be replaced + * \param[in] item new generic ptr to a struct; can be null + * \param[in] freeflag TRUE to free old item; FALSE to return it + * \return item old item, if it exists and is not freed, + * or NULL on error + */ +void * +ptraReplace(L_PTRA *pa, + l_int32 index, + void *item, + l_int32 freeflag) +{ +l_int32 imax; +void *olditem; + + if (!pa) + return (void *)ERROR_PTR("pa not defined", __func__, NULL); + ptraGetMaxIndex(pa, &imax); + if (index < 0 || index > imax) + return (void *)ERROR_PTR("index not in [0 ... imax]", __func__, NULL); + + olditem = pa->array[index]; + pa->array[index] = item; + if (!item && olditem) + pa->nactual--; + else if (item && !olditem) + pa->nactual++; + + if (freeflag == FALSE) + return olditem; + + if (olditem) + LEPT_FREE(olditem); + return NULL; +} + + +/*! + * \brief ptraSwap() + * + * \param[in] pa ptra + * \param[in] index1 + * \param[in] index2 + * \return 0 if OK, 1 on error + */ +l_ok +ptraSwap(L_PTRA *pa, + l_int32 index1, + l_int32 index2) +{ +l_int32 imax; +void *item; + + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + if (index1 == index2) + return 0; + ptraGetMaxIndex(pa, &imax); + if (index1 < 0 || index1 > imax || index2 < 0 || index2 > imax) + return ERROR_INT("invalid index: not in [0 ... imax]", __func__, 1); + + item = ptraRemove(pa, index1, L_NO_COMPACTION); + item = ptraReplace(pa, index2, item, FALSE); + ptraInsert(pa, index1, item, L_MIN_DOWNSHIFT); + return 0; +} + + +/*! + * \brief ptraCompactArray() + * + * \param[in] pa + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This compacts the items on the array, filling any empty ptrs. + * (2) This does not change the size of the array of ptrs. + * </pre> + */ +l_ok +ptraCompactArray(L_PTRA *pa) +{ +l_int32 i, imax, nactual, index; + + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + ptraGetMaxIndex(pa, &imax); + ptraGetActualCount(pa, &nactual); + if (imax + 1 == nactual) return 0; + + /* Compact the array */ + for (i = 0, index = 0; i <= imax; i++) { + if (pa->array[i]) + pa->array[index++] = pa->array[i]; + } + pa->imax = index - 1; + if (nactual != index) + L_ERROR("index = %d; != nactual\n", __func__, index); + + return 0; +} + + +/*----------------------------------------------------------------------* + * Other array operations * + *----------------------------------------------------------------------*/ +/*! + * \brief ptraReverse() + * + * \param[in] pa ptra + * \return 0 if OK, 1 on error + */ +l_ok +ptraReverse(L_PTRA *pa) +{ +l_int32 i, imax; + + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + ptraGetMaxIndex(pa, &imax); + + for (i = 0; i < (imax + 1) / 2; i++) + ptraSwap(pa, i, imax - i); + return 0; +} + + +/*! + * \brief ptraJoin() + * + * \param[in] pa1 add to this one + * \param[in] pa2 appended to pa1, and emptied of items; can be null + * \return 0 if OK, 1 on error + */ +l_ok +ptraJoin(L_PTRA *pa1, + L_PTRA *pa2) +{ +l_int32 i, imax; +void *item; + + if (!pa1) + return ERROR_INT("pa1 not defined", __func__, 1); + if (!pa2) + return 0; + + ptraGetMaxIndex(pa2, &imax); + for (i = 0; i <= imax; i++) { + item = ptraRemove(pa2, i, L_NO_COMPACTION); + ptraAdd(pa1, item); + } + + return 0; +} + + + +/*----------------------------------------------------------------------* + * Simple ptra accessors * + *----------------------------------------------------------------------*/ +/*! + * \brief ptraGetMaxIndex() + * + * \param[in] pa ptra + * \param[out] pmaxindex index of last item in the array; + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The largest index to an item in the array is %maxindex. + * %maxindex is one less than the number of items that would be + * in the array if there were no null pointers between 0 + * and %maxindex - 1. However, because the internal ptr array + * need not be compacted, there may be NULL pointers at + * indices below %maxindex; for example, if items have + * been removed. + * (2) When an item is added to the end of the array, it goes + * into pa->array[maxindex + 1], and maxindex is then + * incremented by 1. + * (3) If there are no items in the array, this returns %maxindex = -1. + * </pre> + */ +l_ok +ptraGetMaxIndex(L_PTRA *pa, + l_int32 *pmaxindex) +{ + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + if (!pmaxindex) + return ERROR_INT("&maxindex not defined", __func__, 1); + *pmaxindex = pa->imax; + return 0; +} + + +/*! + * \brief ptraGetActualCount() + * + * \param[in] pa ptra + * \param[out] pcount actual number of items on the ptr array + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) The actual number of items on the ptr array, pa->nactual, + * will be smaller than pa->n if the array is not compacted. + * </pre> + */ +l_ok +ptraGetActualCount(L_PTRA *pa, + l_int32 *pcount) +{ + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + if (!pcount) + return ERROR_INT("&count not defined", __func__, 1); + *pcount = pa->nactual; + + return 0; +} + + +/*! + * \brief ptraGetPtrToItem() + * + * \param[in] pa ptra + * \param[in] index of element to be retrieved + * \return a ptr to the element, or NULL on error + * + * <pre> + * Notes: + * (1) This returns a ptr to the item. You must cast it to + * the type of item. Do not destroy it; the item belongs + * to the Ptra. + * (2) This can access all possible items on the ptr array. + * If an item doesn't exist, it returns null. + * </pre> + */ +void * +ptraGetPtrToItem(L_PTRA *pa, + l_int32 index) +{ + if (!pa) + return (void *)ERROR_PTR("pa not defined", __func__, NULL); + if (index < 0 || index >= pa->nalloc) + return (void *)ERROR_PTR("index not in [0 ... nalloc-1]", + __func__, NULL); + + return pa->array[index]; +} + + +/*--------------------------------------------------------------------------* + * Ptraa creation and destruction * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraaCreate() + * + * \param[in] n size of ptr array to be alloc'd + * \return paa, or NULL on error + * + * <pre> + * Notes: + * (1) The ptraa is generated with a fixed size, that can not change. + * The ptra can be generated and inserted randomly into this array. + * </pre> + */ +L_PTRAA * +ptraaCreate(l_int32 n) +{ +L_PTRAA *paa; + + if (n <= 0) + return (L_PTRAA *)ERROR_PTR("n must be > 0", __func__, NULL); + + paa = (L_PTRAA *)LEPT_CALLOC(1, sizeof(L_PTRAA)); + if ((paa->ptra = (L_PTRA **)LEPT_CALLOC(n, sizeof(L_PTRA *))) == NULL) { + ptraaDestroy(&paa, 0, 0); + return (L_PTRAA *)ERROR_PTR("ptr array not made", __func__, NULL); + } + paa->nalloc = n; + return paa; +} + + +/*! + * \brief ptraaDestroy() + * + * \param[in,out] ppaa will be set to null before returning + * \param[in] freeflag TRUE to free each remaining item in each ptra + * \param[in] warnflag TRUE to warn if any remaining items + * are not destroyed + * \return void + * + * <pre> + * Notes: + * (1) See ptraDestroy() for use of %freeflag and %warnflag. + * (2) To destroy the ptraa, we destroy each ptra, then the ptr array, + * then the ptraa, and then null the contents of the input ptr. + * </pre> + */ +void +ptraaDestroy(L_PTRAA **ppaa, + l_int32 freeflag, + l_int32 warnflag) +{ +l_int32 i, n; +L_PTRA *pa; +L_PTRAA *paa; + + if (ppaa == NULL) { + L_WARNING("ptr address is NULL\n", __func__); + return; + } + if ((paa = *ppaa) == NULL) + return; + + ptraaGetSize(paa, &n); + for (i = 0; i < n; i++) { + pa = ptraaGetPtra(paa, i, L_REMOVE); + ptraDestroy(&pa, freeflag, warnflag); + } + + LEPT_FREE(paa->ptra); + LEPT_FREE(paa); + *ppaa = NULL; +} + + +/*--------------------------------------------------------------------------* + * Ptraa accessors * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraaGetSize() + * + * \param[in] paa + * \param[out] psize size of ptr array + * \return 0 if OK; 1 on error + */ +l_ok +ptraaGetSize(L_PTRAA *paa, + l_int32 *psize) +{ + if (!paa) + return ERROR_INT("paa not defined", __func__, 1); + if (!psize) + return ERROR_INT("&size not defined", __func__, 1); + *psize = paa->nalloc; + + return 0; +} + + +/*! + * \brief ptraaInsertPtra() + * + * \param[in] paa ptraa + * \param[in] index location in array for insertion + * \param[in] pa to be inserted + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) Caller should check return value. On success, the Ptra + * is inserted in the Ptraa and is owned by it. However, + * on error, the Ptra remains owned by the caller. + * </pre> + */ +l_ok +ptraaInsertPtra(L_PTRAA *paa, + l_int32 index, + L_PTRA *pa) +{ +l_int32 n; + + if (!paa) + return ERROR_INT("paa not defined", __func__, 1); + if (!pa) + return ERROR_INT("pa not defined", __func__, 1); + ptraaGetSize(paa, &n); + if (index < 0 || index >= n) + return ERROR_INT("invalid index", __func__, 1); + if (paa->ptra[index] != NULL) + return ERROR_INT("ptra already stored at index", __func__, 1); + + paa->ptra[index] = pa; + return 0; +} + + +/*! + * \brief ptraaGetPtra() + * + * \param[in] paa ptraa + * \param[in] index location in array + * \param[in] accessflag L_HANDLE_ONLY, L_REMOVE + * \return ptra at index location, or NULL on error or if there + * is no ptra there. + * + * <pre> + * Notes: + * (1) This returns the ptra ptr. If %accessflag == L_HANDLE_ONLY, + * the ptra is left on the ptraa. If %accessflag == L_REMOVE, + * the ptr in the ptraa is set to NULL, and the caller + * is responsible for disposing of the ptra (either putting it + * back on the ptraa, or destroying it). + * (2) This returns NULL if there is no Ptra at the index location. + * </pre> + */ +L_PTRA * +ptraaGetPtra(L_PTRAA *paa, + l_int32 index, + l_int32 accessflag) +{ +l_int32 n; +L_PTRA *pa; + + if (!paa) + return (L_PTRA *)ERROR_PTR("paa not defined", __func__, NULL); + ptraaGetSize(paa, &n); + if (index < 0 || index >= n) + return (L_PTRA *)ERROR_PTR("invalid index", __func__, NULL); + if (accessflag != L_HANDLE_ONLY && accessflag != L_REMOVE) + return (L_PTRA *)ERROR_PTR("invalid accessflag", __func__, NULL); + + pa = paa->ptra[index]; + if (accessflag == L_REMOVE) + paa->ptra[index] = NULL; + return pa; +} + + +/*--------------------------------------------------------------------------* + * Ptraa conversion * + *--------------------------------------------------------------------------*/ +/*! + * \brief ptraaFlattenToPtra() + * + * \param[in] paa ptraa + * \return ptra, or NULL on error + * + * <pre> + * Notes: + * (1) This 'flattens' the ptraa to a ptra, taking the items in + * each ptra, in order, starting with the first ptra, etc. + * (2) As a side-effect, the ptra are all removed from the ptraa + * and destroyed, leaving an empty ptraa. + * </pre> + */ +L_PTRA * +ptraaFlattenToPtra(L_PTRAA *paa) +{ +l_int32 i, n; +L_PTRA *pat, *pad; + + if (!paa) + return (L_PTRA *)ERROR_PTR("paa not defined", __func__, NULL); + + pad = ptraCreate(0); + ptraaGetSize(paa, &n); + for (i = 0; i < n; i++) { + pat = ptraaGetPtra(paa, i, L_REMOVE); + if (!pat) continue; + ptraJoin(pad, pat); + ptraDestroy(&pat, FALSE, FALSE); /* they're all empty */ + } + + return pad; +}
