comparison mupdf-source/thirdparty/leptonica/src/pixalloc.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 pixalloc.c
29 * <pre>
30 *
31 * Custom memory storage with allocator and deallocator
32 *
33 * l_int32 pmsCreate()
34 * void pmsDestroy()
35 * void *pmsCustomAlloc()
36 * void pmsCustomDealloc()
37 * void *pmsGetAlloc()
38 * l_int32 pmsGetLevelForAlloc()
39 * l_int32 pmsGetLevelForDealloc()
40 * void pmsLogInfo()
41 * </pre>
42 */
43
44 #ifdef HAVE_CONFIG_H
45 #include <config_auto.h>
46 #endif /* HAVE_CONFIG_H */
47
48 #include "allheaders.h"
49
50 /*-------------------------------------------------------------------------*
51 * Pix Memory Storage *
52 * *
53 * This is a simple utility for handling pix memory storage. It is *
54 * enabled by setting the PixMemoryManager allocators to the functions *
55 * that are defined here *
56 * pmsCustomAlloc() *
57 * pmsCustomDealloc() *
58 * Use pmsCreate() at the beginning to do the pre-allocation, and *
59 * pmsDestroy() at the end to clean it up. *
60 *-------------------------------------------------------------------------*/
61 /*
62 * In the following, the "memory" refers to the image data
63 * field that is used within the pix. The memory store is a
64 * continuous block of memory, that is logically divided into
65 * smaller "chunks" starting with a set at a minimum size, and
66 * followed by sets of increasing size that are a power of 2 larger
67 * than the minimum size. You must specify the number of chunks
68 * of each size.
69 *
70 * A requested data chunk, if it exists, is borrowed from the memory
71 * storage, and returned after use. If the chunk is too small, or
72 * too large, or if all chunks in the appropriate size range are
73 * in use, the memory is allocated dynamically and freed after use.
74 *
75 * There are four parameters that determine the use of pre-allocated memory:
76 *
77 * minsize: any requested chunk smaller than this is allocated
78 * dynamically and destroyed after use. No preallocated
79 * memory is used.
80 * smallest: the size of the smallest pre-allocated memory chunk.
81 * nlevels: the number of different sizes of data chunks, each a
82 * power of 2 larger than 'smallest'.
83 * numalloc: a Numa of size 'nlevels' containing the number of data
84 * chunks for each size that are in the memory store.
85 *
86 * As an example, suppose:
87 * minsize = 0.5MB
88 * smallest = 1.0MB
89 * nlevels = 4
90 * numalloc = {10, 5, 5, 5}
91 * Then the total amount of allocated memory (in MB) is
92 * 10 * 1 + 5 * 2 + 5 * 4 + 5 * 8 = 80 MB
93 * Any pix requiring less than 0.5 MB or more than 8 MB of memory will
94 * not come from the memory store. Instead, it will be dynamically
95 * allocated and freed after use.
96 *
97 * How is this implemented?
98 *
99 * At setup, the full data block size is computed and allocated.
100 * The addresses of the individual chunks are found, and the pointers
101 * are stored in a set of Ptra (generic pointer arrays), using one Ptra
102 * for each of the sizes of the chunks. When returning a chunk after
103 * use, it is necessary to determine from the address which size level
104 * (ptra) the chunk belongs to. This is done by comparing the address
105 * of the associated chunk.
106 *
107 * In the event that memory chunks need to be dynamically allocated,
108 * either (1) because they are too small or too large for the memory
109 * store or (2) because all the pix of that size (i.e., in the
110 * appropriate level) in the memory store are in use, the
111 * addresses generated will be outside the pre-allocated block.
112 * After use they won't be returned to a ptra; instead the deallocator
113 * will free them.
114 */
115
116 /*! Pix memory storage */
117 struct PixMemoryStore
118 {
119 struct L_Ptraa *paa; /*!< Holds ptrs to allocated memory */
120 size_t minsize; /*!< Pix smaller than this (in bytes) */
121 /*!< are allocated dynamically */
122 size_t smallest; /*!< Smallest mem (in bytes) alloc'd */
123 size_t largest; /*!< Larest mem (in bytes) alloc'd */
124 size_t nbytes; /*!< Size of allocated block w/ all chunks */
125 l_int32 nlevels; /*!< Num of power-of-2 sizes pre-alloc'd */
126 size_t *sizes; /*!< Mem sizes at each power-of-2 level */
127 l_int32 *allocarray; /*!< Number of mem alloc'd at each size */
128 l_uint32 *baseptr; /*!< ptr to allocated array */
129 l_uint32 *maxptr; /*!< ptr just beyond allocated memory */
130 l_uint32 **firstptr; /*!< array of ptrs to first chunk in size */
131 l_int32 *memused; /*!< log: total # of pix used (by level) */
132 l_int32 *meminuse; /*!< log: # of pix in use (by level) */
133 l_int32 *memmax; /*!< log: max # of pix in use (by level) */
134 l_int32 *memempty; /*!< log: # of pix alloc'd because */
135 /*!< the store was empty (by level) */
136 char *logfile; /*!< log: set to null if no logging */
137 };
138 typedef struct PixMemoryStore L_PIX_MEM_STORE;
139
140 static L_PIX_MEM_STORE *CustomPMS = NULL;
141
142
143 /*!
144 * \brief pmsCreate()
145 *
146 * \param[in] minsize of data chunk that can be supplied by pms
147 * \param[in] smallest bytes of the smallest pre-allocated data chunk.
148 * \param[in] numalloc array with the number of data chunks for each
149 * size that are in the memory store
150 * \param[in] logfile use for debugging; null otherwise
151 * \return 0 if OK, 1 on error
152 *
153 * <pre>
154 * Notes:
155 * (1) This computes the size of the block of memory required
156 * and allocates it. Each chunk starts on a 32-bit word boundary.
157 * The chunk sizes are in powers of 2, starting at %smallest,
158 * and the number of levels and chunks at each level is
159 * specified by %numalloc.
160 * (2) This is intended to manage the image data for a small number
161 * of relatively large pix. The system malloc is expected to
162 * handle very large numbers of small chunks efficiently.
163 * (3) Important: set the allocators and call this function
164 * before any pix have been allocated. Destroy all the pix
165 * in the normal way before calling pmsDestroy().
166 * (4) The pms struct is stored in a static global, so this function
167 * is not thread-safe. When used, there must be only one thread
168 * per process.
169 * </pre>
170 */
171 l_ok
172 pmsCreate(size_t minsize,
173 size_t smallest,
174 NUMA *numalloc,
175 const char *logfile)
176 {
177 l_int32 nlevels, i, j, nbytes;
178 l_int32 *alloca;
179 l_float32 nchunks;
180 l_uint32 *baseptr, *data;
181 l_uint32 **firstptr;
182 size_t *sizes;
183 L_PIX_MEM_STORE *pms;
184 L_PTRA *pa;
185 L_PTRAA *paa;
186
187 if (!numalloc)
188 return ERROR_INT("numalloc not defined", __func__, 1);
189 numaGetSum(numalloc, &nchunks);
190 if (nchunks > 1000.0)
191 L_WARNING("There are %.0f chunks\n", __func__, nchunks);
192
193 pms = (L_PIX_MEM_STORE *)LEPT_CALLOC(1, sizeof(L_PIX_MEM_STORE));
194 CustomPMS = pms;
195
196 /* Make sure that minsize and smallest are multiples of 32 bit words */
197 if (minsize % 4 != 0)
198 minsize -= minsize % 4;
199 pms->minsize = minsize;
200 nlevels = numaGetCount(numalloc);
201 pms->nlevels = nlevels;
202
203 if ((sizes = (size_t *)LEPT_CALLOC(nlevels, sizeof(size_t))) == NULL)
204 return ERROR_INT("sizes not made", __func__, 1);
205 pms->sizes = sizes;
206 if (smallest % 4 != 0)
207 smallest += 4 - (smallest % 4);
208 pms->smallest = smallest;
209 for (i = 0; i < nlevels; i++)
210 sizes[i] = smallest * ((size_t)1 << i);
211 pms->largest = sizes[nlevels - 1];
212
213 alloca = numaGetIArray(numalloc);
214 pms->allocarray = alloca;
215 if ((paa = ptraaCreate(nlevels)) == NULL)
216 return ERROR_INT("paa not made", __func__, 1);
217 pms->paa = paa;
218
219 for (i = 0, nbytes = 0; i < nlevels; i++)
220 nbytes += alloca[i] * sizes[i];
221 pms->nbytes = nbytes;
222
223 if ((baseptr = (l_uint32 *)LEPT_CALLOC(nbytes / 4, sizeof(l_uint32)))
224 == NULL)
225 return ERROR_INT("calloc fail for baseptr", __func__, 1);
226 pms->baseptr = baseptr;
227 pms->maxptr = baseptr + nbytes / 4; /* just beyond the memory store */
228 if ((firstptr = (l_uint32 **)LEPT_CALLOC(nlevels, sizeof(l_uint32 *)))
229 == NULL)
230 return ERROR_INT("calloc fail for firstptr", __func__, 1);
231 pms->firstptr = firstptr;
232
233 data = baseptr;
234 for (i = 0; i < nlevels; i++) {
235 if ((pa = ptraCreate(alloca[i])) == NULL)
236 return ERROR_INT("pa not made", __func__, 1);
237 ptraaInsertPtra(paa, i, pa);
238 firstptr[i] = data;
239 for (j = 0; j < alloca[i]; j++) {
240 ptraAdd(pa, data);
241 data += sizes[i] / 4;
242 }
243 }
244
245 if (logfile) {
246 pms->memused = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32));
247 pms->meminuse = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32));
248 pms->memmax = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32));
249 pms->memempty = (l_int32 *)LEPT_CALLOC(nlevels, sizeof(l_int32));
250 pms->logfile = stringNew(logfile);
251 }
252
253 return 0;
254 }
255
256
257 /*!
258 * \brief pmsDestroy()
259 *
260 * <pre>
261 * Notes:
262 * (1) Important: call this function at the end of the program, after
263 * the last pix has been destroyed.
264 * </pre>
265 */
266 void
267 pmsDestroy(void)
268 {
269 L_PIX_MEM_STORE *pms;
270
271 if ((pms = CustomPMS) == NULL)
272 return;
273
274 ptraaDestroy(&pms->paa, FALSE, FALSE); /* don't touch the ptrs */
275 LEPT_FREE(pms->baseptr); /* free the memory */
276
277 if (pms->logfile) {
278 pmsLogInfo();
279 LEPT_FREE(pms->logfile);
280 LEPT_FREE(pms->memused);
281 LEPT_FREE(pms->meminuse);
282 LEPT_FREE(pms->memmax);
283 LEPT_FREE(pms->memempty);
284 }
285
286 LEPT_FREE(pms->sizes);
287 LEPT_FREE(pms->allocarray);
288 LEPT_FREE(pms->firstptr);
289 LEPT_FREE(pms);
290 CustomPMS = NULL;
291 }
292
293
294 /*!
295 * \brief pmsCustomAlloc()
296 *
297 * \param[in] nbytes min number of bytes in the chunk to be retrieved
298 * \return data ptr to chunk
299 *
300 * <pre>
301 * Notes:
302 * (1) This attempts to find a suitable pre-allocated chunk.
303 * If not found, it dynamically allocates the chunk.
304 * (2) If logging is turned on, the allocations that are not taken
305 * from the memory store, and are at least as large as the
306 * minimum size the store can handle, are logged to file.
307 * </pre>
308 */
309 void *
310 pmsCustomAlloc(size_t nbytes)
311 {
312 l_int32 level;
313 void *data;
314 L_PIX_MEM_STORE *pms;
315 L_PTRA *pa;
316
317 if ((pms = CustomPMS) == NULL)
318 return (void *)ERROR_PTR("pms not defined", __func__, NULL);
319
320 pmsGetLevelForAlloc(nbytes, &level);
321
322 if (level < 0) { /* size range invalid; must alloc */
323 if ((data = pmsGetAlloc(nbytes)) == NULL)
324 return (void *)ERROR_PTR("data not made", __func__, NULL);
325 } else { /* get from store */
326 pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY);
327 data = ptraRemoveLast(pa);
328 if (data && pms->logfile) {
329 pms->memused[level]++;
330 pms->meminuse[level]++;
331 if (pms->meminuse[level] > pms->memmax[level])
332 pms->memmax[level]++;
333 }
334 if (!data) { /* none left at this level */
335 data = pmsGetAlloc(nbytes);
336 if (pms->logfile)
337 pms->memempty[level]++;
338 }
339 }
340
341 return data;
342 }
343
344
345 /*!
346 * \brief pmsCustomDealloc()
347 *
348 * \param[in] data to be freed or returned to the storage
349 * \return void
350 */
351 void
352 pmsCustomDealloc(void *data)
353 {
354 l_int32 level;
355 L_PIX_MEM_STORE *pms;
356 L_PTRA *pa;
357
358 if ((pms = CustomPMS) == NULL) {
359 L_ERROR("pms not defined\n", __func__);
360 return;
361 }
362
363 if (pmsGetLevelForDealloc(data, &level) == 1) {
364 L_ERROR("level not found\n", __func__);
365 return;
366 }
367
368 if (level < 0) { /* no logging; just free the data */
369 LEPT_FREE(data);
370 } else { /* return the data to the store */
371 pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY);
372 ptraAdd(pa, data);
373 if (pms->logfile)
374 pms->meminuse[level]--;
375 }
376 }
377
378
379 /*!
380 * \brief pmsGetAlloc()
381 *
382 * \param[in] nbytes
383 * \return data
384 *
385 * <pre>
386 * Notes:
387 * (1) This is called when a request for pix data cannot be
388 * obtained from the preallocated memory store. After use it
389 * is freed like normal memory.
390 * (2) If logging is on, only write out allocs that are as large as
391 * the minimum size handled by the memory store.
392 * (3) The C99 platform-independent format specifier for size_t is %zu.
393 * Windows since at least VC-2015 is conforming; we can now use %zu.
394 * </pre>
395 */
396 void *
397 pmsGetAlloc(size_t nbytes)
398 {
399 void *data;
400 FILE *fp;
401 L_PIX_MEM_STORE *pms;
402
403 if ((pms = CustomPMS) == NULL)
404 return (void *)ERROR_PTR("pms not defined", __func__, NULL);
405
406 if ((data = (void *)LEPT_CALLOC(nbytes, sizeof(char))) == NULL)
407 return (void *)ERROR_PTR("data not made", __func__, NULL);
408 if (pms->logfile && nbytes >= pms->smallest) {
409 if ((fp = fopenWriteStream(pms->logfile, "a")) != NULL) {
410 fprintf(fp, "Alloc %zu bytes at %p\n", nbytes, data);
411 fclose(fp);
412 } else {
413 L_ERROR("failed to open stream for %s\n", __func__, pms->logfile);
414 }
415 }
416 return data;
417 }
418
419
420 /*!
421 * \brief pmsGetLevelForAlloc()
422 *
423 * \param[in] nbytes min number of bytes in the chunk to be retrieved
424 * \param[out] plevel -1 if either too small or too large
425 * \return 0 if OK, 1 on error
426 */
427 l_ok
428 pmsGetLevelForAlloc(size_t nbytes,
429 l_int32 *plevel)
430 {
431 l_int32 i;
432 l_float64 ratio;
433 L_PIX_MEM_STORE *pms;
434
435 if (!plevel)
436 return ERROR_INT("&level not defined", __func__, 1);
437 *plevel = -1;
438 if ((pms = CustomPMS) == NULL)
439 return ERROR_INT("pms not defined", __func__, 1);
440
441 if (nbytes < pms->minsize || nbytes > pms->largest)
442 return 0; /* -1 */
443
444 ratio = (l_float64)nbytes / (l_float64)(pms->smallest);
445 for (i = 0; i < pms->nlevels; i++) {
446 if (ratio <= 1.0)
447 break;
448 ratio /= 2.0;
449 }
450 *plevel = i;
451
452 return 0;
453 }
454
455
456 /*!
457 * \brief pmsGetLevelForDealloc()
458 *
459 * \param[in] data ptr to memory chunk
460 * \param[out] plevel level in memory store; -1 if allocated
461 * outside the store
462 * \return 0 if OK, 1 on error
463 */
464 l_ok
465 pmsGetLevelForDealloc(void *data,
466 l_int32 *plevel)
467 {
468 l_int32 i;
469 l_uint32 *first;
470 L_PIX_MEM_STORE *pms;
471
472 if (!plevel)
473 return ERROR_INT("&level not defined", __func__, 1);
474 *plevel = -1;
475 if (!data)
476 return ERROR_INT("data not defined", __func__, 1);
477 if ((pms = CustomPMS) == NULL)
478 return ERROR_INT("pms not defined", __func__, 1);
479
480 if (data < (void *)pms->baseptr || data >= (void *)pms->maxptr)
481 return 0; /* -1 */
482
483 for (i = 1; i < pms->nlevels; i++) {
484 first = pms->firstptr[i];
485 if (data < (void *)first)
486 break;
487 }
488 *plevel = i - 1;
489
490 return 0;
491 }
492
493
494 /*!
495 * \brief pmsLogInfo()
496 */
497 void
498 pmsLogInfo(void)
499 {
500 l_int32 i;
501 L_PIX_MEM_STORE *pms;
502
503 if ((pms = CustomPMS) == NULL)
504 return;
505
506 lept_stderr("Total number of pix used at each level\n");
507 for (i = 0; i < pms->nlevels; i++)
508 lept_stderr(" Level %d (%zu bytes): %d\n", i,
509 pms->sizes[i], pms->memused[i]);
510
511 lept_stderr("Max number of pix in use at any time in each level\n");
512 for (i = 0; i < pms->nlevels; i++)
513 lept_stderr(" Level %d (%zu bytes): %d\n", i,
514 pms->sizes[i], pms->memmax[i]);
515
516 lept_stderr("Number of pix alloc'd because none were available\n");
517 for (i = 0; i < pms->nlevels; i++)
518 lept_stderr(" Level %d (%zu bytes): %d\n", i,
519 pms->sizes[i], pms->memempty[i]);
520 }