comparison mupdf-source/thirdparty/leptonica/src/queue.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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 queue.c
29 * <pre>
30 *
31 * Create/Destroy L_Queue
32 * L_QUEUE *lqueueCreate()
33 * void *lqueueDestroy()
34 *
35 * Operations to add/remove to/from a L_Queue
36 * l_int32 lqueueAdd()
37 * static l_int32 lqueueExtendArray()
38 * void *lqueueRemove()
39 *
40 * Accessors
41 * l_int32 lqueueGetCount()
42 *
43 * Debug output
44 * l_int32 lqueuePrint()
45 *
46 * The lqueue is a fifo that implements a queue of void* pointers.
47 * It can be used to hold a queue of any type of struct.
48 * Internally, it maintains two counters:
49 * nhead: location of head (in ptrs) from the beginning
50 * of the buffer
51 * nelem: number of ptr elements stored in the queue
52 * As items are added to the queue, nelem increases.
53 * As items are removed, nhead increases and nelem decreases.
54 * Any time the tail reaches the end of the allocated buffer,
55 * all the pointers are shifted to the left, so that the head
56 * is at the beginning of the array.
57 * If the buffer becomes more than 3/4 full, it doubles in size.
58 *
59 * [A circular queue would allow us to skip the shifting and
60 * to resize only when the buffer is full. For most applications,
61 * the extra work we do for a linear queue is not significant.]
62 * </pre>
63 */
64
65 #ifdef HAVE_CONFIG_H
66 #include <config_auto.h>
67 #endif /* HAVE_CONFIG_H */
68
69 #include <string.h>
70 #include "allheaders.h"
71
72 static const l_int32 MIN_BUFFER_SIZE = 20; /* n'importe quoi */
73 static const l_int32 INITIAL_BUFFER_ARRAYSIZE = 1024; /* n'importe quoi */
74
75 /* Static function */
76 static l_int32 lqueueExtendArray(L_QUEUE *lq);
77
78 /*--------------------------------------------------------------------------*
79 * L_Queue create/destroy *
80 *--------------------------------------------------------------------------*/
81 /*!
82 * \brief lqueueCreate()
83 *
84 * \param[in] nalloc size of ptr array to be alloc'd; 0 for default
85 * \return lqueue, or NULL on error
86 *
87 * <pre>
88 * Notes:
89 * (1) Allocates a ptr array of given size, and initializes counters.
90 * </pre>
91 */
92 L_QUEUE *
93 lqueueCreate(l_int32 nalloc)
94 {
95 L_QUEUE *lq;
96
97 if (nalloc < MIN_BUFFER_SIZE)
98 nalloc = INITIAL_BUFFER_ARRAYSIZE;
99
100 lq = (L_QUEUE *)LEPT_CALLOC(1, sizeof(L_QUEUE));
101 if ((lq->array = (void **)LEPT_CALLOC(nalloc, sizeof(void *))) == NULL) {
102 lqueueDestroy(&lq, 0);
103 return (L_QUEUE *)ERROR_PTR("ptr array not made", __func__, NULL);
104 }
105 lq->nalloc = nalloc;
106 lq->nhead = lq->nelem = 0;
107 return lq;
108 }
109
110
111 /*!
112 * \brief lqueueDestroy()
113 *
114 * \param[in,out] plq will be set to null before returning
115 * \param[in] freeflag TRUE to free each remaining struct in the array
116 * \return void
117 *
118 * <pre>
119 * Notes:
120 * (1) If freeflag is TRUE, frees each struct in the array.
121 * (2) If freeflag is FALSE but there are elements on the array,
122 * gives a warning and destroys the array. This will
123 * cause a memory leak of all the items that were on the queue.
124 * So if the items require their own destroy function, they
125 * must be destroyed before the queue. The same applies to the
126 * auxiliary stack, if it is used.
127 * (3) To destroy the L_Queue, we destroy the ptr array, then
128 * the lqueue, and then null the contents of the input ptr.
129 * </pre>
130 */
131 void
132 lqueueDestroy(L_QUEUE **plq,
133 l_int32 freeflag)
134 {
135 void *item;
136 L_QUEUE *lq;
137
138 if (plq == NULL) {
139 L_WARNING("ptr address is NULL\n", __func__);
140 return;
141 }
142 if ((lq = *plq) == NULL)
143 return;
144
145 if (freeflag) {
146 while(lq->nelem > 0) {
147 item = lqueueRemove(lq);
148 LEPT_FREE(item);
149 }
150 } else if (lq->nelem > 0) {
151 L_WARNING("memory leak of %d items in lqueue!\n", __func__, lq->nelem);
152 }
153
154 if (lq->array)
155 LEPT_FREE(lq->array);
156 if (lq->stack)
157 lstackDestroy(&lq->stack, freeflag);
158 LEPT_FREE(lq);
159 *plq = NULL;
160 }
161
162
163 /*--------------------------------------------------------------------------*
164 * Accessors *
165 *--------------------------------------------------------------------------*/
166 /*!
167 * \brief lqueueAdd()
168 *
169 * \param[in] lq lqueue
170 * \param[in] item to be added to the tail of the queue
171 * \return 0 if OK, 1 on error
172 *
173 * <pre>
174 * Notes:
175 * (1) The algorithm is as follows. If the queue is populated
176 * to the end of the allocated array, shift all ptrs toward
177 * the beginning of the array, so that the head of the queue
178 * is at the beginning of the array. Then, if the array is
179 * more than 0.75 full, realloc with double the array size.
180 * Finally, add the item to the tail of the queue.
181 * </pre>
182 */
183 l_ok
184 lqueueAdd(L_QUEUE *lq,
185 void *item)
186 {
187 if (!lq)
188 return ERROR_INT("lq not defined", __func__, 1);
189 if (!item)
190 return ERROR_INT("item not defined", __func__, 1);
191
192 /* If filled to the end and the ptrs can be shifted to the left,
193 * shift them. */
194 if ((lq->nhead + lq->nelem >= lq->nalloc) && (lq->nhead != 0)) {
195 memmove(lq->array, lq->array + lq->nhead, sizeof(void *) * lq->nelem);
196 lq->nhead = 0;
197 }
198
199 /* If necessary, expand the allocated array by a factor of 2 */
200 if (lq->nelem > 0.75 * lq->nalloc) {
201 if (lqueueExtendArray(lq))
202 return ERROR_INT("extension failed", __func__, 1);
203 }
204
205 /* Now add the item */
206 lq->array[lq->nhead + lq->nelem] = (void *)item;
207 lq->nelem++;
208
209 return 0;
210 }
211
212
213 /*!
214 * \brief lqueueExtendArray()
215 *
216 * \param[in] lq lqueue
217 * \return 0 if OK, 1 on error
218 */
219 static l_int32
220 lqueueExtendArray(L_QUEUE *lq)
221 {
222 if (!lq)
223 return ERROR_INT("lq not defined", __func__, 1);
224
225 if ((lq->array = (void **)reallocNew((void **)&lq->array,
226 sizeof(void *) * lq->nalloc,
227 2 * sizeof(void *) * lq->nalloc)) == NULL)
228 return ERROR_INT("new ptr array not returned", __func__, 1);
229
230 lq->nalloc = 2 * lq->nalloc;
231 return 0;
232 }
233
234
235 /*!
236 * \brief lqueueRemove()
237 *
238 * \param[in] lq lqueue
239 * \return ptr to item popped from the head of the queue,
240 * or NULL if the queue is empty or on error
241 *
242 * <pre>
243 * Notes:
244 * (1) If this is the last item on the queue, so that the queue
245 * becomes empty, nhead is reset to the beginning of the array.
246 * </pre>
247 */
248 void *
249 lqueueRemove(L_QUEUE *lq)
250 {
251 void *item;
252
253 if (!lq)
254 return (void *)ERROR_PTR("lq not defined", __func__, NULL);
255
256 if (lq->nelem == 0)
257 return NULL;
258 item = lq->array[lq->nhead];
259 lq->array[lq->nhead] = NULL;
260 if (lq->nelem == 1)
261 lq->nhead = 0; /* reset head ptr */
262 else
263 (lq->nhead)++; /* can't go off end of array because nelem > 1 */
264 lq->nelem--;
265 return item;
266 }
267
268
269 /*!
270 * \brief lqueueGetCount()
271 *
272 * \param[in] lq lqueue
273 * \return count, or 0 on error
274 */
275 l_int32
276 lqueueGetCount(L_QUEUE *lq)
277 {
278 if (!lq)
279 return ERROR_INT("lq not defined", __func__, 0);
280
281 return lq->nelem;
282 }
283
284
285 /*---------------------------------------------------------------------*
286 * Debug output *
287 *---------------------------------------------------------------------*/
288 /*!
289 * \brief lqueuePrint()
290 *
291 * \param[in] fp file stream
292 * \param[in] lq lqueue
293 * \return 0 if OK; 1 on error
294 */
295 l_ok
296 lqueuePrint(FILE *fp,
297 L_QUEUE *lq)
298 {
299 l_int32 i;
300
301 if (!fp)
302 return ERROR_INT("stream not defined", __func__, 1);
303 if (!lq)
304 return ERROR_INT("lq not defined", __func__, 1);
305
306 fprintf(fp, "\n L_Queue: nalloc = %d, nhead = %d, nelem = %d, array = %p\n",
307 lq->nalloc, lq->nhead, lq->nelem, lq->array);
308 for (i = lq->nhead; i < lq->nhead + lq->nelem; i++)
309 fprintf(fp, "array[%d] = %p\n", i, lq->array[i]);
310
311 return 0;
312 }