Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * pixalloc.c 00018 * 00019 * Custom memory storage with allocator and deallocator 00020 * 00021 * l_int32 pmsCreate() 00022 * void pmsDestroy() 00023 * void *pmsCustomAlloc() 00024 * void pmsCustomDealloc() 00025 * void *pmsGetAlloc() 00026 * l_int32 pmsGetLevelForAlloc() 00027 * l_int32 pmsGetLevelForDealloc() 00028 * void pmsLogInfo() 00029 */ 00030 00031 #include "allheaders.h" 00032 00033 /*-------------------------------------------------------------------------* 00034 * Pix Memory Storage * 00035 * * 00036 * This is a simple utility for handling pix memory storage. It is * 00037 * enabled by setting the PixMemoryManager allocators to the functions * 00038 * that are defined here * 00039 * pmsCustomAlloc() * 00040 * pmsCustomDealloc() * 00041 * Use pmsCreate() at the beginning to do the pre-allocation, and * 00042 * pmsDestroy() at the end to clean it up. * 00043 *-------------------------------------------------------------------------*/ 00044 /* 00045 * In the following, the "memory" refers to the image data 00046 * field that is used within the pix. The memory store is a 00047 * continuous block of memory, that is logically divided into 00048 * smaller "chunks" starting with a set at a minimum size, and 00049 * followed by sets of increasing size that are a power of 2 larger 00050 * than the minimum size. You must specify the number of chunks 00051 * of each size. 00052 * 00053 * A requested data chunk, if it exists, is borrowed from the memory 00054 * storage, and returned after use. If the chunk is too small, or 00055 * too large, or if all chunks in the appropriate size range are 00056 * in use, the memory is allocated dynamically and freed after use. 00057 * 00058 * There are four parameters that determine the use of pre-allocated memory: 00059 * 00060 * minsize: any requested chunk smaller than this is allocated 00061 * dynamically and destroyed after use. No preallocated 00062 * memory is used. 00063 * smallest: the size of the smallest pre-allocated memory chunk. 00064 * nlevels: the number of different sizes of data chunks, each a 00065 * power of 2 larger than 'smallest'. 00066 * numalloc: a Numa of size 'nlevels' containing the number of data 00067 * chunks for each size that are in the memory store. 00068 * 00069 * As an example, suppose: 00070 * minsize = 0.5MB 00071 * smallest = 1.0MB 00072 * nlevels = 4 00073 * numalloc = {10, 5, 5, 5} 00074 * Then the total amount of allocated memory (in MB) is 00075 * 10 * 1 + 5 * 2 + 5 * 4 + 5 * 8 = 80 MB 00076 * Any pix requiring less than 0.5 MB or more than 8 MB of memory will 00077 * not come from the memory store. Instead, it will be dynamically 00078 * allocated and freed after use. 00079 * 00080 * How is this implemented? 00081 * 00082 * At setup, the full data block size is computed and allocated. 00083 * The addresses of the individual chunks are found, and the pointers 00084 * are stored in a set of Ptra (generic pointer arrays), using one Ptra 00085 * for each of the sizes of the chunks. When returning a chunk after 00086 * use, it is necessary to determine from the address which size level 00087 * (ptra) the chunk belongs to. This is done by comparing the address 00088 * of the associated chunk. 00089 * 00090 * In the event that memory chunks need to be dynamically allocated, 00091 * either (1) because they are too small or too large for the memory 00092 * store or (2) because all the pix of that size (i.e., in the 00093 * appropriate level) in the memory store are in use, the 00094 * addresses generated will be outside the pre-allocated block. 00095 * After use they won't be returned to a ptra; instead the deallocator 00096 * will free them. 00097 */ 00098 00099 00100 struct PixMemoryStore 00101 { 00102 struct L_Ptraa *paa; /* Holds ptrs to allocated memory */ 00103 size_t minsize; /* Pix smaller than this (in bytes) */ 00104 /* are allocated dynamically */ 00105 size_t smallest; /* Smallest mem (in bytes) alloc'd */ 00106 size_t largest; /* Larest mem (in bytes) alloc'd */ 00107 size_t nbytes; /* Size of allocated block w/ all chunks */ 00108 l_int32 nlevels; /* Num of power-of-2 sizes pre-alloc'd */ 00109 size_t *sizes; /* Mem sizes at each power-of-2 level */ 00110 l_int32 *allocarray; /* Number of mem alloc'd at each size */ 00111 l_uint32 *baseptr; /* ptr to allocated array */ 00112 l_uint32 *maxptr; /* ptr just beyond allocated memory */ 00113 l_uint32 **firstptr; /* array of ptrs to first chunk in size */ 00114 l_int32 *memused; /* log: total # of pix used (by level) */ 00115 l_int32 *meminuse; /* log: # of pix in use (by level) */ 00116 l_int32 *memmax; /* log: max # of pix in use (by level) */ 00117 l_int32 *memempty; /* log: # of pix alloc'd because */ 00118 /* the store was empty (by level) */ 00119 char *logfile; /* log: set to null if no logging */ 00120 }; 00121 typedef struct PixMemoryStore L_PIX_MEM_STORE; 00122 00123 static L_PIX_MEM_STORE *CustomPMS = NULL; 00124 00125 00126 /*! 00127 * pmsCreate() 00128 * 00129 * Input: minsize (of data chunk that can be supplied by pms) 00130 * smallest (bytes of the smallest pre-allocated data chunk. 00131 * numalloc (array with the number of data chunks for each 00132 * size that are in the memory store) 00133 * logfile (use for debugging; null otherwise) 00134 * Return: 0 if OK, 1 on error 00135 * 00136 * Notes: 00137 * (1) This computes the size of the block of memory required 00138 * and allocates it. Each chunk starts on a 32-bit word boundary. 00139 * The chunk sizes are in powers of 2, starting at @smallest, 00140 * and the number of levels and chunks at each level is 00141 * specified by @numalloc. 00142 * (2) This is intended to manage the image data for a small number 00143 * of relatively large pix. The system malloc is expected to 00144 * handle very large numbers of small chunks efficiently. 00145 * (3) Important: set the allocators and call this function 00146 * before any pix have been allocated. Destroy all the pix 00147 * in the normal way before calling pmsDestroy(). 00148 * (4) The pms struct is stored in a static global, so this function 00149 * is not thread-safe. When used, there must be only one thread 00150 * per process. 00151 */ 00152 l_int32 00153 pmsCreate(size_t minsize, 00154 size_t smallest, 00155 NUMA *numalloc, 00156 const char *logfile) 00157 { 00158 l_int32 nlevels, i, j, nbytes; 00159 l_int32 *alloca; 00160 l_float32 nchunks; 00161 l_uint32 *baseptr, *data; 00162 l_uint32 **firstptr; 00163 size_t *sizes; 00164 L_PIX_MEM_STORE *pms; 00165 L_PTRA *pa; 00166 L_PTRAA *paa; 00167 00168 PROCNAME("createPMS"); 00169 00170 if (!numalloc) 00171 return ERROR_INT("numalloc not defined", procName, 1); 00172 numaGetSum(numalloc, &nchunks); 00173 if (nchunks > 1000.0) 00174 L_WARNING_FLOAT("There are %.0f chunks", procName, nchunks); 00175 00176 if ((pms = (L_PIX_MEM_STORE *)CALLOC(1, sizeof(L_PIX_MEM_STORE))) == NULL) 00177 return ERROR_INT("pms not made", procName, 1); 00178 CustomPMS = pms; 00179 00180 /* Make sure that minsize and smallest are multiples of 32 bit words */ 00181 if (minsize % 4 != 0) 00182 minsize -= minsize % 4; 00183 pms->minsize = minsize; 00184 nlevels = numaGetCount(numalloc); 00185 pms->nlevels = nlevels; 00186 00187 if ((sizes = (size_t *)CALLOC(nlevels, sizeof(size_t))) == NULL) 00188 return ERROR_INT("sizes not made", procName, 1); 00189 pms->sizes = sizes; 00190 if (smallest % 4 != 0) 00191 smallest += 4 - (smallest % 4); 00192 pms->smallest = smallest; 00193 for (i = 0; i < nlevels; i++) 00194 sizes[i] = smallest * (1 << i); 00195 pms->largest = sizes[nlevels - 1]; 00196 00197 alloca = numaGetIArray(numalloc); 00198 pms->allocarray = alloca; 00199 if ((paa = ptraaCreate(nlevels)) == NULL) 00200 return ERROR_INT("paa not made", procName, 1); 00201 pms->paa = paa; 00202 00203 for (i = 0, nbytes = 0; i < nlevels; i++) 00204 nbytes += alloca[i] * sizes[i]; 00205 pms->nbytes = nbytes; 00206 00207 if ((baseptr = (l_uint32 *)CALLOC(nbytes / 4, sizeof(l_uint32))) == NULL) 00208 return ERROR_INT("calloc fail for baseptr", procName, 1); 00209 pms->baseptr = baseptr; 00210 pms->maxptr = baseptr + nbytes / 4; /* just beyond the memory store */ 00211 if ((firstptr = (l_uint32 **)CALLOC(nlevels, sizeof(l_uint32 *))) == NULL) 00212 return ERROR_INT("calloc fail for firstptr", procName, 1); 00213 pms->firstptr = firstptr; 00214 00215 data = baseptr; 00216 for (i = 0; i < nlevels; i++) { 00217 if ((pa = ptraCreate(alloca[i])) == NULL) 00218 return ERROR_INT("pa not made", procName, 1); 00219 ptraaInsertPtra(paa, i, pa); 00220 firstptr[i] = data; 00221 for (j = 0; j < alloca[i]; j++) { 00222 ptraAdd(pa, data); 00223 data += sizes[i] / 4; 00224 } 00225 } 00226 00227 if (logfile) { 00228 pms->memused = (l_int32 *)CALLOC(nlevels, sizeof(l_int32)); 00229 pms->meminuse = (l_int32 *)CALLOC(nlevels, sizeof(l_int32)); 00230 pms->memmax = (l_int32 *)CALLOC(nlevels, sizeof(l_int32)); 00231 pms->memempty = (l_int32 *)CALLOC(nlevels, sizeof(l_int32)); 00232 pms->logfile = stringNew(logfile); 00233 } 00234 00235 return 0; 00236 } 00237 00238 00239 /*! 00240 * pmsDestroy() 00241 * 00242 * Input: (none) 00243 * Return: void 00244 * 00245 * Notes: 00246 * (1) Important: call this function at the end of the program, after 00247 * the last pix has been destroyed. 00248 */ 00249 void 00250 pmsDestroy() 00251 { 00252 L_PIX_MEM_STORE *pms; 00253 00254 if ((pms = CustomPMS) == NULL) 00255 return; 00256 00257 ptraaDestroy(&pms->paa, FALSE, FALSE); /* don't touch the ptrs */ 00258 FREE(pms->baseptr); /* free the memory */ 00259 00260 if (pms->logfile) { 00261 pmsLogInfo(); 00262 FREE(pms->logfile); 00263 FREE(pms->memused); 00264 FREE(pms->meminuse); 00265 FREE(pms->memmax); 00266 FREE(pms->memempty); 00267 } 00268 00269 FREE(pms->sizes); 00270 FREE(pms->allocarray); 00271 FREE(pms->firstptr); 00272 FREE(pms); 00273 CustomPMS = NULL; 00274 return; 00275 } 00276 00277 00278 /*! 00279 * pmsCustomAlloc() 00280 * 00281 * Input: nbytes (min number of bytes in the chunk to be retrieved) 00282 * Return: data (ptr to chunk) 00283 * 00284 * Notes: 00285 * (1) This attempts to find a suitable pre-allocated chunk. 00286 * If not found, it dynamically allocates the chunk. 00287 * (2) If logging is turned on, the allocations that are not taken 00288 * from the memory store, and are at least as large as the 00289 * minimum size the store can handle, are logged to file. 00290 */ 00291 void * 00292 pmsCustomAlloc(size_t nbytes) 00293 { 00294 l_int32 level; 00295 void *data; 00296 L_PIX_MEM_STORE *pms; 00297 L_PTRA *pa; 00298 00299 PROCNAME("pmsCustomAlloc"); 00300 00301 if ((pms = CustomPMS) == NULL) 00302 return (void *)ERROR_PTR("pms not defined", procName, NULL); 00303 00304 pmsGetLevelForAlloc(nbytes, &level); 00305 00306 if (level < 0) { /* size range invalid; must alloc */ 00307 if ((data = pmsGetAlloc(nbytes)) == NULL) 00308 return (void *)ERROR_PTR("data not made", procName, NULL); 00309 } 00310 else { /* get from store */ 00311 pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY); 00312 data = ptraRemoveLast(pa); 00313 if (data && pms->logfile) { 00314 pms->memused[level]++; 00315 pms->meminuse[level]++; 00316 if (pms->meminuse[level] > pms->memmax[level]) 00317 pms->memmax[level]++; 00318 } 00319 if (!data) { /* none left at this level */ 00320 data = pmsGetAlloc(nbytes); 00321 if (pms->logfile) 00322 pms->memempty[level]++; 00323 } 00324 } 00325 00326 return data; 00327 } 00328 00329 00330 /*! 00331 * pmsCustomDealloc() 00332 * 00333 * Input: data (to be freed or returned to the storage) 00334 * Return: void 00335 */ 00336 void 00337 pmsCustomDealloc(void *data) 00338 { 00339 l_int32 level; 00340 L_PIX_MEM_STORE *pms; 00341 L_PTRA *pa; 00342 00343 PROCNAME("pmsCustomDealloc"); 00344 00345 if ((pms = CustomPMS) == NULL) { 00346 L_ERROR("pms not defined", procName); 00347 return; 00348 } 00349 00350 if (pmsGetLevelForDealloc(data, &level) == 1) { 00351 L_ERROR("level not found", procName); 00352 return; 00353 } 00354 00355 if (level < 0) /* no logging; just free the data */ 00356 FREE(data); 00357 else { /* return the data to the store */ 00358 pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY); 00359 ptraAdd(pa, data); 00360 if (pms->logfile) 00361 pms->meminuse[level]--; 00362 } 00363 00364 return; 00365 } 00366 00367 00368 /*! 00369 * pmsGetAlloc() 00370 * 00371 * Input: nbytes 00372 * Return: data 00373 * 00374 * Notes: 00375 * (1) This is called when a request for pix data cannot be 00376 * obtained from the preallocated memory store. After use it 00377 * is freed like normal memory. 00378 * (2) If logging is on, only write out allocs that are as large as 00379 * the minimum size handled by the memory store. 00380 */ 00381 void * 00382 pmsGetAlloc(size_t nbytes) 00383 { 00384 void *data; 00385 FILE *fp; 00386 L_PIX_MEM_STORE *pms; 00387 00388 PROCNAME("pmsGetAlloc"); 00389 00390 if ((pms = CustomPMS) == NULL) 00391 return (void *)ERROR_PTR("pms not defined", procName, NULL); 00392 00393 if ((data = (void *)CALLOC(nbytes, sizeof(char))) == NULL) 00394 return (void *)ERROR_PTR("data not made", procName, NULL); 00395 if (pms->logfile && nbytes >= pms->smallest) { 00396 fp = fopenWriteStream(pms->logfile, "a"); 00397 fprintf(fp, "Alloc %ld bytes at %p\n", nbytes, data); 00398 fclose(fp); 00399 } 00400 00401 return data; 00402 } 00403 00404 00405 /*! 00406 * pmsGetLevelForAlloc() 00407 * 00408 * Input: nbytes (min number of bytes in the chunk to be retrieved) 00409 * &level (<return>; -1 if either too small or too large) 00410 * Return: 0 if OK, 1 on error 00411 */ 00412 l_int32 00413 pmsGetLevelForAlloc(size_t nbytes, 00414 l_int32 *plevel) 00415 { 00416 l_int32 i; 00417 l_float64 ratio; 00418 L_PIX_MEM_STORE *pms; 00419 00420 PROCNAME("pmsGetLevelForAlloc"); 00421 00422 if (!plevel) 00423 return ERROR_INT("&level not defined", procName, 1); 00424 *plevel = -1; 00425 if ((pms = CustomPMS) == NULL) 00426 return ERROR_INT("pms not defined", procName, 1); 00427 00428 if (nbytes < pms->minsize || nbytes > pms->largest) 00429 return 0; /* -1 */ 00430 00431 ratio = (l_float64)nbytes / (l_float64)(pms->smallest); 00432 for (i = 0; i < pms->nlevels; i++) { 00433 if (ratio <= 1.0) 00434 break; 00435 ratio /= 2.0; 00436 } 00437 *plevel = i; 00438 00439 return 0; 00440 } 00441 00442 00443 /*! 00444 * pmsGetLevelForDealloc() 00445 * 00446 * Input: data (ptr to memory chunk) 00447 * &level (<return> level in memory store; -1 if allocated 00448 * outside the store) 00449 * Return: 0 if OK, 1 on error 00450 */ 00451 l_int32 00452 pmsGetLevelForDealloc(void *data, 00453 l_int32 *plevel) 00454 { 00455 l_int32 i; 00456 l_uint32 *first; 00457 L_PIX_MEM_STORE *pms; 00458 00459 PROCNAME("pmsGetLevelForDealloc"); 00460 00461 if (!plevel) 00462 return ERROR_INT("&level not defined", procName, 1); 00463 *plevel = -1; 00464 if (!data) 00465 return ERROR_INT("data not defined", procName, 1); 00466 if ((pms = CustomPMS) == NULL) 00467 return ERROR_INT("pms not defined", procName, 1); 00468 00469 if (data < (void *)pms->baseptr || data >= (void *)pms->maxptr) 00470 return 0; /* -1 */ 00471 00472 for (i = 1; i < pms->nlevels; i++) { 00473 first = pms->firstptr[i]; 00474 if (data < (void *)first) 00475 break; 00476 } 00477 *plevel = i - 1; 00478 00479 return 0; 00480 } 00481 00482 00483 /*! 00484 * pmsLogInfo() 00485 * 00486 * Input: (none) 00487 * Return: void 00488 */ 00489 void 00490 pmsLogInfo() 00491 { 00492 l_int32 i; 00493 L_PIX_MEM_STORE *pms; 00494 00495 if ((pms = CustomPMS) == NULL) 00496 return; 00497 00498 fprintf(stderr, "Total number of pix used at each level\n"); 00499 for (i = 0; i < pms->nlevels; i++) 00500 fprintf(stderr, " Level %d (%ld bytes): %d\n", i, pms->sizes[i], 00501 pms->memused[i]); 00502 00503 fprintf(stderr, "Max number of pix in use at any time in each level\n"); 00504 for (i = 0; i < pms->nlevels; i++) 00505 fprintf(stderr, " Level %d (%ld bytes): %d\n", i, pms->sizes[i], 00506 pms->memmax[i]); 00507 00508 fprintf(stderr, "Number of pix alloc'd because none were available\n"); 00509 for (i = 0; i < pms->nlevels; i++) 00510 fprintf(stderr, " Level %d (%ld bytes): %d\n", i, pms->sizes[i], 00511 pms->memempty[i]); 00512 00513 return; 00514 } 00515 00516