| /* |
| * jvirtmem.c |
| * |
| * Copyright (C) 1991, Thomas G. Lane. |
| * This file is part of the Independent JPEG Group's software. |
| * For conditions of distribution and use, see the accompanying README file. |
| * |
| * This file provides the system-dependent memory allocation routines |
| * for the case where we can rely on virtual memory to handle large arrays. |
| * |
| * This includes some MS-DOS code just for trial purposes; "big" arrays will |
| * have to be handled with temp files on MS-DOS, so a real implementation of |
| * a DOS memory manager will probably be a separate file. (See additional |
| * comments about big arrays, below.) |
| * |
| * NB: allocation routines never return NULL. |
| * They should exit to error_exit if unsuccessful. |
| */ |
| |
| #include "jinclude.h" |
| |
| #ifdef __STDC__ |
| #include <stdlib.h> /* to declare malloc(), free() */ |
| #else |
| extern void * malloc PP((size_t size)); |
| extern void free PP((void *ptr)); |
| #endif |
| |
| |
| /* Insert system-specific definitions of far_malloc, far_free here. */ |
| |
| #ifndef NEED_FAR_POINTERS /* Generic for non-braindamaged CPUs */ |
| |
| #define far_malloc(x) malloc(x) |
| #define far_free(x) free(x) |
| |
| #else /* NEED_FAR_POINTERS */ |
| |
| #ifdef __TURBOC__ |
| /* These definitions work for Turbo C */ |
| #include <alloc.h> /* need farmalloc(), farfree() */ |
| #define far_malloc(x) farmalloc(x) |
| #define far_free(x) farfree(x) |
| #else |
| #ifdef MSDOS |
| /* These definitions work for Microsoft C and compatible compilers */ |
| #include <malloc.h> /* need _fmalloc(), _ffree() */ |
| #define far_malloc(x) _fmalloc(x) |
| #define far_free(x) _ffree(x) |
| #endif |
| #endif |
| |
| #endif /* NEED_FAR_POINTERS */ |
| |
| |
| /* |
| * Some important notes: |
| * The array alloc/dealloc routines are not merely a convenience; |
| * on 80x86 machines the bottom-level pointers in an array are FAR |
| * and thus may not be allocatable by alloc_small. |
| * |
| * Also, it's not a good idea to try to merge the sarray and barray |
| * routines, even though they are textually almost the same, because |
| * samples are usually stored as bytes while coefficients are shorts. |
| * Thus, in machines where byte pointers have a different representation |
| * from word pointers, the resulting machine code could not be the same. |
| */ |
| |
| |
| static external_methods_ptr methods; /* saved for access to error_exit */ |
| |
| |
| #ifdef MEM_STATS /* optional extra stuff for statistics */ |
| |
| #define MALLOC_OVERHEAD (SIZEOF(char *)) /* assumed overhead per request */ |
| #define MALLOC_FAR_OVERHEAD (SIZEOF(char FAR *)) /* for "far" storage */ |
| |
| static long total_num_small = 0; /* total # of small objects alloced */ |
| static long total_bytes_small = 0; /* total bytes requested */ |
| static long cur_num_small = 0; /* # currently alloced */ |
| static long max_num_small = 0; /* max simultaneously alloced */ |
| |
| #ifdef NEED_FAR_POINTERS |
| static long total_num_medium = 0; /* total # of medium objects alloced */ |
| static long total_bytes_medium = 0; /* total bytes requested */ |
| static long cur_num_medium = 0; /* # currently alloced */ |
| static long max_num_medium = 0; /* max simultaneously alloced */ |
| #endif |
| |
| static long total_num_sarray = 0; /* total # of sarray objects alloced */ |
| static long total_bytes_sarray = 0; /* total bytes requested */ |
| static long cur_num_sarray = 0; /* # currently alloced */ |
| static long max_num_sarray = 0; /* max simultaneously alloced */ |
| |
| static long total_num_barray = 0; /* total # of barray objects alloced */ |
| static long total_bytes_barray = 0; /* total bytes requested */ |
| static long cur_num_barray = 0; /* # currently alloced */ |
| static long max_num_barray = 0; /* max simultaneously alloced */ |
| |
| |
| GLOBAL void |
| j_mem_stats (void) |
| { |
| /* since this is only a debugging stub, we can cheat a little on the |
| * trace message mechanism... helps 'cuz trace can't handle longs. |
| */ |
| fprintf(stderr, "total_num_small = %ld\n", total_num_small); |
| fprintf(stderr, "total_bytes_small = %ld\n", total_bytes_small); |
| if (cur_num_small) |
| fprintf(stderr, "CUR_NUM_SMALL = %ld\n", cur_num_small); |
| fprintf(stderr, "max_num_small = %ld\n", max_num_small); |
| |
| #ifdef NEED_FAR_POINTERS |
| fprintf(stderr, "total_num_medium = %ld\n", total_num_medium); |
| fprintf(stderr, "total_bytes_medium = %ld\n", total_bytes_medium); |
| if (cur_num_medium) |
| fprintf(stderr, "CUR_NUM_MEDIUM = %ld\n", cur_num_medium); |
| fprintf(stderr, "max_num_medium = %ld\n", max_num_medium); |
| #endif |
| |
| fprintf(stderr, "total_num_sarray = %ld\n", total_num_sarray); |
| fprintf(stderr, "total_bytes_sarray = %ld\n", total_bytes_sarray); |
| if (cur_num_sarray) |
| fprintf(stderr, "CUR_NUM_SARRAY = %ld\n", cur_num_sarray); |
| fprintf(stderr, "max_num_sarray = %ld\n", max_num_sarray); |
| |
| fprintf(stderr, "total_num_barray = %ld\n", total_num_barray); |
| fprintf(stderr, "total_bytes_barray = %ld\n", total_bytes_barray); |
| if (cur_num_barray) |
| fprintf(stderr, "CUR_NUM_BARRAY = %ld\n", cur_num_barray); |
| fprintf(stderr, "max_num_barray = %ld\n", max_num_barray); |
| } |
| |
| #endif /* MEM_STATS */ |
| |
| |
| LOCAL void |
| out_of_memory (int which) |
| /* Report an out-of-memory error and stop execution */ |
| /* If we compiled MEM_STATS support, report alloc requests before dying */ |
| { |
| #ifdef MEM_STATS |
| j_mem_stats(); |
| #endif |
| ERREXIT1(methods, "Insufficient memory (case %d)", which); |
| } |
| |
| |
| |
| METHODDEF void * |
| alloc_small (size_t sizeofobject) |
| /* Allocate a "small" (all-in-memory) object */ |
| { |
| void * result; |
| |
| #ifdef MEM_STATS |
| total_num_small++; |
| total_bytes_small += sizeofobject + MALLOC_OVERHEAD; |
| cur_num_small++; |
| if (cur_num_small > max_num_small) max_num_small = cur_num_small; |
| #endif |
| |
| result = malloc(sizeofobject); |
| if (result == NULL) |
| out_of_memory(1); |
| return result; |
| } |
| |
| |
| METHODDEF void |
| free_small (void *ptr) |
| /* Free a "small" (all-in-memory) object */ |
| { |
| free(ptr); |
| |
| #ifdef MEM_STATS |
| cur_num_small--; |
| #endif |
| } |
| |
| |
| #ifdef NEED_FAR_POINTERS |
| |
| METHODDEF void FAR * |
| alloc_medium (size_t sizeofobject) |
| /* Allocate a "medium" (all in memory, but in far heap) object */ |
| { |
| void FAR * result; |
| |
| #ifdef MEM_STATS |
| total_num_medium++; |
| total_bytes_medium += sizeofobject + MALLOC_FAR_OVERHEAD; |
| cur_num_medium++; |
| if (cur_num_medium > max_num_medium) max_num_medium = cur_num_medium; |
| #endif |
| |
| result = far_malloc(sizeofobject); |
| if (result == NULL) |
| out_of_memory(2); |
| return result; |
| } |
| |
| |
| METHODDEF void |
| free_medium (void FAR *ptr) |
| /* Free a "medium" (all in memory, but in far heap) object */ |
| { |
| far_free(ptr); |
| |
| #ifdef MEM_STATS |
| cur_num_medium--; |
| #endif |
| } |
| |
| #endif /* NEED_FAR_POINTERS */ |
| |
| |
| METHODDEF JSAMPARRAY |
| alloc_small_sarray (long samplesperrow, long numrows) |
| /* Allocate a "small" (all-in-memory) 2-D sample array */ |
| { |
| JSAMPARRAY result; |
| long i; |
| |
| #ifdef MEM_STATS |
| total_num_sarray++; |
| total_bytes_sarray += (samplesperrow * SIZEOF(JSAMPLE) + MALLOC_FAR_OVERHEAD) |
| * numrows; |
| cur_num_sarray++; |
| if (cur_num_sarray > max_num_sarray) max_num_sarray = cur_num_sarray; |
| #endif |
| |
| /* Get space for row pointers; this is always "near" on 80x86 */ |
| result = (JSAMPARRAY) alloc_small((size_t) (numrows * SIZEOF(JSAMPROW))); |
| |
| /* Get the rows themselves; on 80x86 these are "far" */ |
| for (i = 0; i < numrows; i++) { |
| result[i] = (JSAMPROW) far_malloc((size_t) (samplesperrow * SIZEOF(JSAMPLE))); |
| if (result[i] == NULL) |
| out_of_memory(3); |
| } |
| |
| return result; |
| } |
| |
| |
| METHODDEF void |
| free_small_sarray (JSAMPARRAY ptr, long numrows) |
| /* Free a "small" (all-in-memory) 2-D sample array */ |
| { |
| long i; |
| |
| /* Free the rows themselves; on 80x86 these are "far" */ |
| for (i = 0; i < numrows; i++) { |
| far_free((void FAR *) ptr[i]); |
| } |
| |
| /* Free space for row pointers; this is always "near" on 80x86 */ |
| free_small((void *) ptr); |
| |
| #ifdef MEM_STATS |
| cur_num_sarray--; |
| #endif |
| } |
| |
| |
| METHODDEF JBLOCKARRAY |
| alloc_small_barray (long blocksperrow, long numrows) |
| /* Allocate a "small" (all-in-memory) 2-D coefficient-block array */ |
| { |
| JBLOCKARRAY result; |
| long i; |
| |
| #ifdef MEM_STATS |
| total_num_barray++; |
| total_bytes_barray += (blocksperrow * SIZEOF(JBLOCK) + MALLOC_FAR_OVERHEAD) |
| * numrows; |
| cur_num_barray++; |
| if (cur_num_barray > max_num_barray) max_num_barray = cur_num_barray; |
| #endif |
| |
| /* Get space for row pointers; this is always "near" on 80x86 */ |
| result = (JBLOCKARRAY) alloc_small((size_t) (numrows * SIZEOF(JBLOCKROW))); |
| |
| /* Get the rows themselves; on 80x86 these are "far" */ |
| for (i = 0; i < numrows; i++) { |
| result[i] = (JBLOCKROW) far_malloc((size_t) (blocksperrow * SIZEOF(JBLOCK))); |
| if (result[i] == NULL) |
| out_of_memory(4); |
| } |
| |
| return result; |
| } |
| |
| |
| METHODDEF void |
| free_small_barray (JBLOCKARRAY ptr, long numrows) |
| /* Free a "small" (all-in-memory) 2-D coefficient-block array */ |
| { |
| long i; |
| |
| /* Free the rows themselves; on 80x86 these are "far" */ |
| for (i = 0; i < numrows; i++) { |
| far_free((void FAR *) ptr[i]); |
| } |
| |
| /* Free space for row pointers; this is always "near" on 80x86 */ |
| free_small((void *) ptr); |
| |
| #ifdef MEM_STATS |
| cur_num_barray--; |
| #endif |
| } |
| |
| |
| |
| /* |
| * About "big" array management: |
| * |
| * To allow machines with limited memory to handle large images, |
| * all processing in the JPEG system is done a few pixel or block rows |
| * at a time. The above "small" array routines are only used to allocate |
| * strip buffers (as wide as the image, but just a few rows high). |
| * In some cases multiple passes must be made over the data. In these |
| * cases the "big" array routines are used. 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_big_array routines are told the total size of the image (in case |
| * it is useful to know the total file size that will be needed). They are |
| * also given the unit height, which is the number of rows that will be |
| * accessed at once; the in-memory buffer should usually be made a multiple of |
| * this height for best efficiency. |
| * |
| * The request routines create control blocks (and may open backing files), |
| * but they don't create the in-memory buffers. This is postponed until |
| * alloc_big_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_big_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 typical access pattern is one top-to-bottom pass to write the data, |
| * followed by one or more read-only top-to-bottom passes. However, other |
| * access patterns may occur while reading. For example, translation of image |
| * formats that use bottom-to-top scan order will require bottom-to-top read |
| * passes. The memory manager need not support multiple write passes nor |
| * funny write orders (meaning that rearranging rows must be handled while |
| * reading data out of the big array, not while putting it in). |
| * |
| * In current usage, the access requests are always for nonoverlapping strips; |
| * that is, successive access start_row numbers always differ by exactly the |
| * unitheight. This allows fairly simple buffer dump/reload logic if the |
| * in-memory buffer is made a multiple of the unitheight. It would be |
| * possible to keep subsampled rather than fullsize data in the "big" arrays, |
| * thus reducing temp file size, if we supported overlapping strip access |
| * (access requests differing by less than the unitheight). At the moment |
| * I don't believe this is worth the extra complexity. |
| * |
| * This particular implementation doesn't use temp files; the whole of a big |
| * array is allocated in (virtual) memory, and any swapping is done behind the |
| * scenes by the operating system. |
| */ |
| |
| |
| |
| /* The control blocks for virtual arrays. |
| * These are pretty minimal in this implementation. |
| * Note: in this implementation we could realize big arrays |
| * at request time and make alloc_big_arrays a no-op; |
| * however, doing it separately keeps callers honest. |
| */ |
| |
| struct big_sarray_control { |
| JSAMPARRAY mem_buffer; /* memory buffer (the whole thing, here) */ |
| long rows_in_mem; /* Height of memory buffer */ |
| long samplesperrow; /* Width of memory buffer */ |
| long unitheight; /* # of rows accessed by access_big_sarray() */ |
| big_sarray_ptr next; /* list link for unrealized arrays */ |
| }; |
| |
| struct big_barray_control { |
| JBLOCKARRAY mem_buffer; /* memory buffer (the whole thing, here) */ |
| long rows_in_mem; /* Height of memory buffer */ |
| long blocksperrow; /* Width of memory buffer */ |
| long unitheight; /* # of rows accessed by access_big_barray() */ |
| big_barray_ptr next; /* list link for unrealized arrays */ |
| }; |
| |
| |
| /* Headers of lists of control blocks for unrealized big arrays */ |
| static big_sarray_ptr unalloced_sarrays; |
| static big_barray_ptr unalloced_barrays; |
| |
| |
| METHODDEF big_sarray_ptr |
| request_big_sarray (long samplesperrow, long numrows, long unitheight) |
| /* Request a "big" (virtual-memory) 2-D sample array */ |
| { |
| big_sarray_ptr result; |
| |
| /* get control block */ |
| result = (big_sarray_ptr) alloc_small(SIZEOF(struct big_sarray_control)); |
| |
| result->mem_buffer = NULL; /* lets access routine spot premature access */ |
| result->rows_in_mem = numrows; |
| result->samplesperrow = samplesperrow; |
| result->unitheight = unitheight; |
| result->next = unalloced_sarrays; /* add to list of unallocated arrays */ |
| unalloced_sarrays = result; |
| |
| return result; |
| } |
| |
| |
| METHODDEF big_barray_ptr |
| request_big_barray (long blocksperrow, long numrows, long unitheight) |
| /* Request a "big" (virtual-memory) 2-D coefficient-block array */ |
| { |
| big_barray_ptr result; |
| |
| /* get control block */ |
| result = (big_barray_ptr) alloc_small(SIZEOF(struct big_barray_control)); |
| |
| result->mem_buffer = NULL; /* lets access routine spot premature access */ |
| result->rows_in_mem = numrows; |
| result->blocksperrow = blocksperrow; |
| result->unitheight = unitheight; |
| result->next = unalloced_barrays; /* add to list of unallocated arrays */ |
| unalloced_barrays = result; |
| |
| return result; |
| } |
| |
| |
| METHODDEF void |
| alloc_big_arrays (long extra_small_samples, long extra_small_blocks, |
| long extra_medium_space) |
| /* Allocate the in-memory buffers for any unrealized "big" arrays */ |
| /* 'extra' values are upper bounds for total future small-array requests */ |
| /* and far-heap requests */ |
| { |
| /* In this implementation we just malloc the whole arrays */ |
| /* and expect the system's virtual memory to worry about swapping them */ |
| big_sarray_ptr sptr; |
| big_barray_ptr bptr; |
| |
| for (sptr = unalloced_sarrays; sptr != NULL; sptr = sptr->next) { |
| sptr->mem_buffer = alloc_small_sarray(sptr->samplesperrow, |
| sptr->rows_in_mem); |
| } |
| |
| for (bptr = unalloced_barrays; bptr != NULL; bptr = bptr->next) { |
| bptr->mem_buffer = alloc_small_barray(bptr->blocksperrow, |
| bptr->rows_in_mem); |
| } |
| |
| unalloced_sarrays = NULL; /* reset for possible future cycles */ |
| unalloced_barrays = NULL; |
| } |
| |
| |
| METHODDEF JSAMPARRAY |
| access_big_sarray (big_sarray_ptr ptr, long start_row, boolean writable) |
| /* Access the part of a "big" sample array starting at start_row */ |
| /* and extending for ptr->unitheight rows. writable is true if */ |
| /* caller intends to modify the accessed area. */ |
| { |
| /* debugging check */ |
| if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_mem || |
| ptr->mem_buffer == NULL) |
| ERREXIT(methods, "Bogus access_big_sarray request"); |
| |
| return ptr->mem_buffer + start_row; |
| } |
| |
| |
| METHODDEF JBLOCKARRAY |
| access_big_barray (big_barray_ptr ptr, long start_row, boolean writable) |
| /* Access the part of a "big" coefficient-block array starting at start_row */ |
| /* and extending for ptr->unitheight rows. writable is true if */ |
| /* caller intends to modify the accessed area. */ |
| { |
| /* debugging check */ |
| if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_mem || |
| ptr->mem_buffer == NULL) |
| ERREXIT(methods, "Bogus access_big_barray request"); |
| |
| return ptr->mem_buffer + start_row; |
| } |
| |
| |
| METHODDEF void |
| free_big_sarray (big_sarray_ptr ptr) |
| /* Free a "big" (virtual-memory) 2-D sample array */ |
| { |
| free_small_sarray(ptr->mem_buffer, ptr->rows_in_mem); |
| free_small((void *) ptr); /* free the control block too */ |
| } |
| |
| |
| METHODDEF void |
| free_big_barray (big_barray_ptr ptr) |
| /* Free a "big" (virtual-memory) 2-D coefficient-block array */ |
| { |
| free_small_barray(ptr->mem_buffer, ptr->rows_in_mem); |
| free_small((void *) ptr); /* free the control block too */ |
| } |
| |
| |
| |
| /* |
| * The method selection routine for virtual memory systems. |
| * The system-dependent setup routine should call this routine |
| * to install the necessary method pointers in the supplied struct. |
| */ |
| |
| GLOBAL void |
| jselvirtmem (external_methods_ptr emethods) |
| { |
| methods = emethods; /* save struct addr for error exit access */ |
| |
| emethods->alloc_small = alloc_small; |
| emethods->free_small = free_small; |
| #ifdef NEED_FAR_POINTERS |
| emethods->alloc_medium = alloc_medium; |
| emethods->free_medium = free_medium; |
| #endif |
| emethods->alloc_small_sarray = alloc_small_sarray; |
| emethods->free_small_sarray = free_small_sarray; |
| emethods->alloc_small_barray = alloc_small_barray; |
| emethods->free_small_barray = free_small_barray; |
| emethods->request_big_sarray = request_big_sarray; |
| emethods->request_big_barray = request_big_barray; |
| emethods->alloc_big_arrays = alloc_big_arrays; |
| emethods->access_big_sarray = access_big_sarray; |
| emethods->access_big_barray = access_big_barray; |
| emethods->free_big_sarray = free_big_sarray; |
| emethods->free_big_barray = free_big_barray; |
| |
| unalloced_sarrays = NULL; /* make sure list headers are empty */ |
| unalloced_barrays = NULL; |
| } |