jmemmgr.cpp
来自「Windows 图形编程 书籍」· C++ 代码 · 共 1,152 行 · 第 1/3 页
CPP
1,152 行
* management heuristics are quite different. We assume that each
* request is large enough that it may as well be passed directly to
* jpeg_get_large; the pool management just links everything together
* so that we can free it all on demand.
* Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY
* structures. The routines that create these structures (see below)
* deliberately bunch rows together to ensure a large request size.
*/
void *my_memory_mgr::alloc_large(int pool_id, size_t sizeofobject)
/* Allocate a "large" object */
{
large_pool_ptr hdr_ptr;
size_t odd_bytes;
/* Check for unsatisfiable request (do now to ensure no overflow below) */
if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-sizeof(large_pool_hdr)))
out_of_memory(cinfo, 3); /* request exceeds malloc's ability */
/* Round up the requested size to a multiple of sizeof(ALIGN_TYPE) */
odd_bytes = sizeofobject % sizeof(ALIGN_TYPE);
if (odd_bytes > 0)
sizeofobject += sizeof(ALIGN_TYPE) - odd_bytes;
/* Always make a new pool */
if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS)
cinfo->ERREXIT1(JERR_BAD_POOL_ID, pool_id); /* safety check */
hdr_ptr = (large_pool_ptr) malloc(sizeofobject +
sizeof(large_pool_hdr));
if (hdr_ptr == NULL)
out_of_memory(cinfo, 4); /* jpeg_get_large failed */
total_space_allocated += sizeofobject + sizeof(large_pool_hdr);
/* Success, initialize the new pool header and add to list */
hdr_ptr->hdr.next = large_list[pool_id];
/* We maintain space counts in each pool header for statistical purposes,
* even though they are not needed for allocation.
*/
hdr_ptr->hdr.bytes_used = sizeofobject;
hdr_ptr->hdr.bytes_left = 0;
large_list[pool_id] = hdr_ptr;
return (void *) (hdr_ptr + 1); /* point to first data byte in pool */
}
/*
* Creation of 2-D sample arrays.
* The pointers are in near heap, the samples themselves in FAR heap.
*
* To minimize allocation overhead and to allow I/O of large contiguous
* blocks, we allocate the sample rows in groups of as many rows as possible
* without exceeding MAX_ALLOC_CHUNK total bytes per allocation request.
* NB: the virtual array control routines, later in this file, know about
* this chunking of rows. The rowsperchunk value is left in the mem manager
* object so that it can be saved away if this sarray is the workspace for
* a virtual array.
*/
JSAMPARRAY my_memory_mgr::alloc_sarray (int pool_id,
JDIMENSION samplesperrow, JDIMENSION numrows)
/* Allocate a 2-D sample array */
{
JSAMPARRAY result;
JSAMPROW workspace;
JDIMENSION rowsperchunk, currow, i;
long ltemp;
/* Calculate max # of rows allowed in one allocation chunk */
ltemp = (MAX_ALLOC_CHUNK-sizeof(large_pool_hdr)) /
((long) samplesperrow * sizeof(JSAMPLE));
if (ltemp <= 0)
cinfo->ERREXIT(JERR_WIDTH_OVERFLOW);
if (ltemp < (long) numrows)
rowsperchunk = (JDIMENSION) ltemp;
else
rowsperchunk = numrows;
last_rowsperchunk = rowsperchunk;
/* Get space for row pointers (small object) */
result = (JSAMPARRAY) alloc_small(pool_id,
(size_t) (numrows * sizeof(JSAMPROW)));
/* Get the rows themselves (large objects) */
currow = 0;
while (currow < numrows) {
rowsperchunk = MIN(rowsperchunk, numrows - currow);
workspace = (JSAMPROW) alloc_large(pool_id,
(size_t) ((size_t) rowsperchunk * (size_t) samplesperrow
* sizeof(JSAMPLE)));
for (i = rowsperchunk; i > 0; i--) {
result[currow++] = workspace;
workspace += samplesperrow;
}
}
return result;
}
/*
* Creation of 2-D coefficient-block arrays.
* This is essentially the same as the code for sample arrays, above.
*/
JBLOCKARRAY my_memory_mgr::alloc_barray (int pool_id,
JDIMENSION blocksperrow, JDIMENSION numrows)
/* Allocate a 2-D coefficient-block array */
{
JBLOCKARRAY result;
JBLOCKROW workspace;
JDIMENSION rowsperchunk, currow, i;
long ltemp;
/* Calculate max # of rows allowed in one allocation chunk */
ltemp = (MAX_ALLOC_CHUNK-sizeof(large_pool_hdr)) /
((long) blocksperrow * sizeof(JBLOCK));
if (ltemp <= 0)
cinfo->ERREXIT(JERR_WIDTH_OVERFLOW);
if (ltemp < (long) numrows)
rowsperchunk = (JDIMENSION) ltemp;
else
rowsperchunk = numrows;
last_rowsperchunk = rowsperchunk;
/* Get space for row pointers (small object) */
result = (JBLOCKARRAY) alloc_small(pool_id,
(size_t) (numrows * sizeof(JBLOCKROW)));
/* Get the rows themselves (large objects) */
currow = 0;
while (currow < numrows) {
rowsperchunk = MIN(rowsperchunk, numrows - currow);
workspace = (JBLOCKROW) alloc_large(pool_id,
(size_t) ((size_t) rowsperchunk * (size_t) blocksperrow
* sizeof(JBLOCK)));
for (i = rowsperchunk; i > 0; i--) {
result[currow++] = workspace;
workspace += blocksperrow;
}
}
return result;
}
/*
* About virtual array management:
*
* The above "normal" array routines are only used to allocate strip buffers
* (as wide as the image, but just a few rows high). Full-image-sized buffers
* are handled as "virtual" arrays. The array is still accessed a strip at a
* time, but the memory manager must save the whole array for repeated
* accesses. The intended implementation is that there is a strip buffer in
* memory (as high as is possible given the desired memory limit), plus a
* backing file that holds the rest of the array.
*
* The request_virt_array routines are told the total size of the image and
* the maximum number of rows that will be accessed at once. The in-memory
* buffer must be at least as large as the maxaccess value.
*
* The request routines create control blocks but not the in-memory buffers.
* That is postponed until realize_virt_arrays is called. At that time the
* total amount of space needed is known (approximately, anyway), so free
* memory can be divided up fairly.
*
* The access_virt_array routines are responsible for making a specific strip
* area accessible (after reading or writing the backing file, if necessary).
* Note that the access routines are told whether the caller intends to modify
* the accessed strip; during a read-only pass this saves having to rewrite
* data to disk. The access routines are also responsible for pre-zeroing
* any newly accessed rows, if pre-zeroing was requested.
*
* In current usage, the access requests are usually for nonoverlapping
* strips; that is, successive access start_row numbers differ by exactly
* num_rows = maxaccess. This means we can get good performance with simple
* buffer dump/reload logic, by making the in-memory buffer be a multiple
* of the access height; then there will never be accesses across bufferload
* boundaries. The code will still work with overlapping access requests,
* but it doesn't handle bufferload overlaps very efficiently.
*/
jvirt_sarray_ptr my_memory_mgr::request_virt_sarray (int pool_id, boolean pre_zero,
JDIMENSION samplesperrow, JDIMENSION numrows,
JDIMENSION maxaccess)
/* Request a virtual 2-D sample array */
{
jvirt_sarray_ptr result;
/* Only IMAGE-lifetime virtual arrays are currently supported */
if (pool_id != JPOOL_IMAGE)
cinfo->ERREXIT1(JERR_BAD_POOL_ID, pool_id); /* safety check */
/* get control block */
result = (jvirt_sarray_ptr) alloc_small(pool_id,
sizeof(struct jvirt_sarray_control));
result->mem_buffer = NULL; /* marks array not yet realized */
result->rows_in_array = numrows;
result->samplesperrow = samplesperrow;
result->maxaccess = maxaccess;
result->pre_zero = pre_zero;
result->b_s_open = FALSE; /* no associated backing-store object */
result->next = virt_sarray_list; /* add to list of virtual arrays */
virt_sarray_list = result;
return result;
}
jvirt_barray_ptr my_memory_mgr::request_virt_barray (int pool_id, boolean pre_zero,
JDIMENSION blocksperrow, JDIMENSION numrows,
JDIMENSION maxaccess)
/* Request a virtual 2-D coefficient-block array */
{
jvirt_barray_ptr result;
/* Only IMAGE-lifetime virtual arrays are currently supported */
if (pool_id != JPOOL_IMAGE)
cinfo->ERREXIT1(JERR_BAD_POOL_ID, pool_id); /* safety check */
/* get control block */
result = (jvirt_barray_ptr) alloc_small(pool_id,
sizeof(struct jvirt_barray_control));
result->mem_buffer = NULL; /* marks array not yet realized */
result->rows_in_array = numrows;
result->blocksperrow = blocksperrow;
result->maxaccess = maxaccess;
result->pre_zero = pre_zero;
result->b_s_open = FALSE; /* no associated backing-store object */
result->next = virt_barray_list; /* add to list of virtual arrays */
virt_barray_list = result;
return result;
}
void my_memory_mgr::realize_virt_arrays (void)
/* Allocate the in-memory buffers for any unrealized virtual arrays */
{
long space_per_minheight, maximum_space, avail_mem;
long minheights, max_minheights;
jvirt_sarray_ptr sptr;
jvirt_barray_ptr bptr;
/* Compute the minimum space needed (maxaccess rows in each buffer)
* and the maximum space needed (full image height in each buffer).
* These may be of use to the system-dependent jpeg_mem_available routine.
*/
space_per_minheight = 0;
maximum_space = 0;
for (sptr = virt_sarray_list; sptr != NULL; sptr = sptr->next) {
if (sptr->mem_buffer == NULL) { /* if not realized yet */
space_per_minheight += (long) sptr->maxaccess *
(long) sptr->samplesperrow * sizeof(JSAMPLE);
maximum_space += (long) sptr->rows_in_array *
(long) sptr->samplesperrow * sizeof(JSAMPLE);
}
}
for (bptr = virt_barray_list; bptr != NULL; bptr = bptr->next) {
if (bptr->mem_buffer == NULL) { /* if not realized yet */
space_per_minheight += (long) bptr->maxaccess *
(long) bptr->blocksperrow * sizeof(JBLOCK);
maximum_space += (long) bptr->rows_in_array *
(long) bptr->blocksperrow * sizeof(JBLOCK);
}
}
if (space_per_minheight <= 0)
return; /* no unrealized arrays, no work */
/* Determine amount of memory to actually use; this is system-dependent. */
avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space,
total_space_allocated);
/* If the maximum space needed is available, make all the buffers full
* height; otherwise parcel it out with the same number of minheights
* in each buffer.
*/
if (avail_mem >= maximum_space)
max_minheights = 1000000000L;
else {
max_minheights = avail_mem / space_per_minheight;
/* If there doesn't seem to be enough space, try to get the minimum
* anyway. This allows a "stub" implementation of jpeg_mem_available().
*/
if (max_minheights <= 0)
max_minheights = 1;
}
/* Allocate the in-memory buffers and initialize backing store as needed. */
for (sptr = virt_sarray_list; sptr != NULL; sptr = sptr->next) {
if (sptr->mem_buffer == NULL) { /* if not realized yet */
minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L;
if (minheights <= max_minheights) {
/* This buffer fits in memory */
sptr->rows_in_mem = sptr->rows_in_array;
} else {
/* It doesn't fit in memory, create backing store. */
sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess);
jpeg_open_backing_store(cinfo, & sptr->b_s_info,
(long) sptr->rows_in_array *
(long) sptr->samplesperrow *
(long) sizeof(JSAMPLE));
sptr->b_s_open = TRUE;
}
sptr->mem_buffer = alloc_sarray(JPOOL_IMAGE,
sptr->samplesperrow, sptr->rows_in_mem);
sptr->rowsperchunk = last_rowsperchunk;
sptr->cur_start_row = 0;
sptr->first_undef_row = 0;
sptr->dirty = FALSE;
}
}
for (bptr = virt_barray_list; bptr != NULL; bptr = bptr->next) {
if (bptr->mem_buffer == NULL) { /* if not realized yet */
minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L;
if (minheights <= max_minheights) {
/* This buffer fits in memory */
bptr->rows_in_mem = bptr->rows_in_array;
} else {
/* It doesn't fit in memory, create backing store. */
bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess);
jpeg_open_backing_store(cinfo, & bptr->b_s_info,
(long) bptr->rows_in_array *
(long) bptr->blocksperrow *
(long) sizeof(JBLOCK));
bptr->b_s_open = TRUE;
}
bptr->mem_buffer = alloc_barray(JPOOL_IMAGE,
bptr->blocksperrow, bptr->rows_in_mem);
bptr->rowsperchunk = last_rowsperchunk;
bptr->cur_start_row = 0;
bptr->first_undef_row = 0;
bptr->dirty = FALSE;
}
}
}
LOCAL(void)
do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing)
/* Do backing store read or write of a virtual sample array */
{
long bytesperrow, file_offset, byte_count, rows, thisrow, i;
bytesperrow = (long) ptr->samplesperrow * sizeof(JSAMPLE);
file_offset = ptr->cur_start_row * bytesperrow;
/* Loop to read or write each allocation chunk in mem_buffer */
for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) {
/* One chunk, but check for short chunk at end of buffer */
rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i);
/* Transfer no more than is currently defined */
thisrow = (long) ptr->cur_start_row + i;
rows = MIN(rows, (long) ptr->first_undef_row - thisrow);
/* Transfer no more than fits in file */
rows = MIN(rows, (long) ptr->rows_in_array - thisrow);
if (rows <= 0) /* this chunk might be past end of file! */
break;
byte_count = rows * bytesperrow;
if (writing)
(*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info,
(void *) ptr->mem_buffer[i],
file_offset, byte_count);
else
(*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info,
(void *) ptr->mem_buffer[i],
file_offset, byte_count);
file_offset += byte_count;
}
}
LOCAL(void)
do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing)
/* Do backing store read or write of a virtual coefficient-block array */
{
long bytesperrow, file_offset, byte_count, rows, thisrow, i;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?