blob: 614755f04e084a8a4cfb56bf47988a898fa29faa [file] [log] [blame]
Thomas G. Lane4a6b7301992-03-17 00:00:00 +00001/*
2 * jmemmgr.c
3 *
4 * Copyright (C) 1991, 1992, Thomas G. Lane.
5 * This file is part of the Independent JPEG Group's software.
6 * For conditions of distribution and use, see the accompanying README file.
7 *
8 * This file provides the standard system-independent memory management
9 * routines. This code is usable across a wide variety of machines; most
10 * of the system dependencies have been isolated in a separate file.
11 * The major functions provided here are:
12 * * bookkeeping to allow all allocated memory to be freed upon exit;
13 * * policy decisions about how to divide available memory among the
14 * various large arrays;
15 * * control logic for swapping virtual arrays between main memory and
16 * backing storage.
17 * The separate system-dependent file provides the actual backing-storage
18 * access code, and it contains the policy decision about how much total
19 * main memory to use.
20 * This file is system-dependent in the sense that some of its functions
21 * are unnecessary in some systems. For example, if there is enough virtual
22 * memory so that backing storage will never be used, much of the big-array
23 * control logic could be removed. (Of course, if you have that much memory
24 * then you shouldn't care about a little bit of unused code...)
25 *
26 * These routines are invoked via the methods alloc_small, free_small,
27 * alloc_medium, free_medium, alloc_small_sarray, free_small_sarray,
28 * alloc_small_barray, free_small_barray, request_big_sarray,
29 * request_big_barray, alloc_big_arrays, access_big_sarray, access_big_barray,
30 * free_big_sarray, free_big_barray, and free_all.
31 */
32
33#define AM_MEMORY_MANAGER /* we define big_Xarray_control structs */
34
35#include "jinclude.h"
36#include "jmemsys.h" /* import the system-dependent declarations */
37
38
39/*
40 * On many systems it is not necessary to distinguish alloc_small from
41 * alloc_medium; the main case where they must be distinguished is when
42 * FAR pointers are distinct from regular pointers. However, you might
43 * want to keep them separate if you have different system-dependent logic
44 * for small and large memory requests (i.e., jget_small and jget_large
45 * do different things).
46 */
47
48#ifdef NEED_FAR_POINTERS
49#define NEED_ALLOC_MEDIUM /* flags alloc_medium really exists */
50#endif
51
52
53/*
54 * Some important notes:
55 * The allocation routines provided here must never return NULL.
56 * They should exit to error_exit if unsuccessful.
57 *
58 * It's not a good idea to try to merge the sarray and barray routines,
59 * even though they are textually almost the same, because samples are
60 * usually stored as bytes while coefficients are shorts. Thus, in machines
61 * where byte pointers have a different representation from word pointers,
62 * the resulting machine code could not be the same.
63 */
64
65
66static external_methods_ptr methods; /* saved for access to error_exit */
67
68
69#ifdef MEM_STATS /* optional extra stuff for statistics */
70
71/* These macros are the assumed overhead per block for malloc().
72 * They don't have to be accurate, but the printed statistics will be
73 * off a little bit if they are not.
74 */
75#define MALLOC_OVERHEAD (SIZEOF(void *)) /* overhead for jget_small() */
76#define MALLOC_FAR_OVERHEAD (SIZEOF(void FAR *)) /* for jget_large() */
77
78static long total_num_small = 0; /* total # of small objects alloced */
79static long total_bytes_small = 0; /* total bytes requested */
80static long cur_num_small = 0; /* # currently alloced */
81static long max_num_small = 0; /* max simultaneously alloced */
82
83#ifdef NEED_ALLOC_MEDIUM
84static long total_num_medium = 0; /* total # of medium objects alloced */
85static long total_bytes_medium = 0; /* total bytes requested */
86static long cur_num_medium = 0; /* # currently alloced */
87static long max_num_medium = 0; /* max simultaneously alloced */
88#endif
89
90static long total_num_sarray = 0; /* total # of sarray objects alloced */
91static long total_bytes_sarray = 0; /* total bytes requested */
92static long cur_num_sarray = 0; /* # currently alloced */
93static long max_num_sarray = 0; /* max simultaneously alloced */
94
95static long total_num_barray = 0; /* total # of barray objects alloced */
96static long total_bytes_barray = 0; /* total bytes requested */
97static long cur_num_barray = 0; /* # currently alloced */
98static long max_num_barray = 0; /* max simultaneously alloced */
99
100
101LOCAL void
102print_mem_stats (void)
103{
104 /* since this is only a debugging stub, we can cheat a little on the
105 * trace message mechanism... helpful 'cuz trace_message can't handle longs.
106 */
107 fprintf(stderr, "total_num_small = %ld\n", total_num_small);
108 fprintf(stderr, "total_bytes_small = %ld\n", total_bytes_small);
109 if (cur_num_small)
110 fprintf(stderr, "cur_num_small = %ld\n", cur_num_small);
111 fprintf(stderr, "max_num_small = %ld\n", max_num_small);
112
113#ifdef NEED_ALLOC_MEDIUM
114 fprintf(stderr, "total_num_medium = %ld\n", total_num_medium);
115 fprintf(stderr, "total_bytes_medium = %ld\n", total_bytes_medium);
116 if (cur_num_medium)
117 fprintf(stderr, "cur_num_medium = %ld\n", cur_num_medium);
118 fprintf(stderr, "max_num_medium = %ld\n", max_num_medium);
119#endif
120
121 fprintf(stderr, "total_num_sarray = %ld\n", total_num_sarray);
122 fprintf(stderr, "total_bytes_sarray = %ld\n", total_bytes_sarray);
123 if (cur_num_sarray)
124 fprintf(stderr, "cur_num_sarray = %ld\n", cur_num_sarray);
125 fprintf(stderr, "max_num_sarray = %ld\n", max_num_sarray);
126
127 fprintf(stderr, "total_num_barray = %ld\n", total_num_barray);
128 fprintf(stderr, "total_bytes_barray = %ld\n", total_bytes_barray);
129 if (cur_num_barray)
130 fprintf(stderr, "cur_num_barray = %ld\n", cur_num_barray);
131 fprintf(stderr, "max_num_barray = %ld\n", max_num_barray);
132}
133
134#endif /* MEM_STATS */
135
136
137LOCAL void
138out_of_memory (int which)
139/* Report an out-of-memory error and stop execution */
140/* If we compiled MEM_STATS support, report alloc requests before dying */
141{
142#ifdef MEM_STATS
143 if (methods->trace_level <= 0) /* don't do it if free_all() will */
144 print_mem_stats(); /* print optional memory usage statistics */
145#endif
146 ERREXIT1(methods, "Insufficient memory (case %d)", which);
147}
148
149
150/*
151 * Management of "small" objects.
152 * These are all-in-memory, and are in near-heap space on an 80x86.
153 */
154
155typedef struct small_struct * small_ptr;
156
157typedef struct small_struct {
158 small_ptr next; /* next in list of allocated objects */
159 } small_hdr;
160
161static small_ptr small_list; /* head of list */
162
163
164METHODDEF void *
165alloc_small (size_t sizeofobject)
166/* Allocate a "small" object */
167{
168 small_ptr result;
169
170 sizeofobject += SIZEOF(small_hdr); /* add space for header */
171
172#ifdef MEM_STATS
173 total_num_small++;
174 total_bytes_small += sizeofobject + MALLOC_OVERHEAD;
175 cur_num_small++;
176 if (cur_num_small > max_num_small) max_num_small = cur_num_small;
177#endif
178
179 result = (small_ptr) jget_small(sizeofobject);
180 if (result == NULL)
181 out_of_memory(1);
182
183 result->next = small_list;
184 small_list = result;
185 result++; /* advance past header */
186
187 return (void *) result;
188}
189
190
191METHODDEF void
192free_small (void *ptr)
193/* Free a "small" object */
194{
195 small_ptr hdr;
196 small_ptr * llink;
197
198 hdr = (small_ptr) ptr;
199 hdr--; /* point back to header */
200
201 /* Remove item from list -- linear search is fast enough */
202 llink = &small_list;
203 while (*llink != hdr) {
204 if (*llink == NULL)
205 ERREXIT(methods, "Bogus free_small request");
206 llink = &( (*llink)->next );
207 }
208 *llink = hdr->next;
209
210 jfree_small((void *) hdr);
211
212#ifdef MEM_STATS
213 cur_num_small--;
214#endif
215}
216
217
218/*
219 * Management of "medium-size" objects.
220 * These are just like small objects except they are in the FAR heap.
221 */
222
223#ifdef NEED_ALLOC_MEDIUM
224
225typedef struct medium_struct FAR * medium_ptr;
226
227typedef struct medium_struct {
228 medium_ptr next; /* next in list of allocated objects */
229 } medium_hdr;
230
231static medium_ptr medium_list; /* head of list */
232
233
234METHODDEF void FAR *
235alloc_medium (size_t sizeofobject)
236/* Allocate a "medium-size" object */
237{
238 medium_ptr result;
239
240 sizeofobject += SIZEOF(medium_hdr); /* add space for header */
241
242#ifdef MEM_STATS
243 total_num_medium++;
244 total_bytes_medium += sizeofobject + MALLOC_FAR_OVERHEAD;
245 cur_num_medium++;
246 if (cur_num_medium > max_num_medium) max_num_medium = cur_num_medium;
247#endif
248
249 result = (medium_ptr) jget_large(sizeofobject);
250 if (result == NULL)
251 out_of_memory(2);
252
253 result->next = medium_list;
254 medium_list = result;
255 result++; /* advance past header */
256
257 return (void FAR *) result;
258}
259
260
261METHODDEF void
262free_medium (void FAR *ptr)
263/* Free a "medium-size" object */
264{
265 medium_ptr hdr;
266 medium_ptr FAR * llink;
267
268 hdr = (medium_ptr) ptr;
269 hdr--; /* point back to header */
270
271 /* Remove item from list -- linear search is fast enough */
272 llink = &medium_list;
273 while (*llink != hdr) {
274 if (*llink == NULL)
275 ERREXIT(methods, "Bogus free_medium request");
276 llink = &( (*llink)->next );
277 }
278 *llink = hdr->next;
279
280 jfree_large((void FAR *) hdr);
281
282#ifdef MEM_STATS
283 cur_num_medium--;
284#endif
285}
286
287#endif /* NEED_ALLOC_MEDIUM */
288
289
290/*
291 * Management of "small" (all-in-memory) 2-D sample arrays.
292 * The pointers are in near heap, the samples themselves in FAR heap.
293 * The header structure is adjacent to the row pointers.
294 * To minimize allocation overhead and to allow I/O of large contiguous
295 * blocks, we allocate the sample rows in groups of as many rows as possible
296 * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request.
297 * Note that the big-array control routines, later in this file, know about
298 * this chunking of rows ... and also how to get the rowsperchunk value!
299 */
300
301typedef struct small_sarray_struct * small_sarray_ptr;
302
303typedef struct small_sarray_struct {
304 small_sarray_ptr next; /* next in list of allocated sarrays */
305 long numrows; /* # of rows in this array */
306 long rowsperchunk; /* max # of rows per allocation chunk */
307 } small_sarray_hdr;
308
309static small_sarray_ptr small_sarray_list; /* head of list */
310
311
312METHODDEF JSAMPARRAY
313alloc_small_sarray (long samplesperrow, long numrows)
314/* Allocate a "small" (all-in-memory) 2-D sample array */
315{
316 small_sarray_ptr hdr;
317 JSAMPARRAY result;
318 JSAMPROW workspace;
319 long rowsperchunk, currow, i;
320
321#ifdef MEM_STATS
322 total_num_sarray++;
323 cur_num_sarray++;
324 if (cur_num_sarray > max_num_sarray) max_num_sarray = cur_num_sarray;
325#endif
326
327 /* Calculate max # of rows allowed in one allocation chunk */
328 rowsperchunk = MAX_ALLOC_CHUNK / (samplesperrow * SIZEOF(JSAMPLE));
329 if (rowsperchunk <= 0)
330 ERREXIT(methods, "Image too wide for this implementation");
331
332 /* Get space for header and row pointers; this is always "near" on 80x86 */
333 hdr = (small_sarray_ptr) alloc_small((size_t) (numrows * SIZEOF(JSAMPROW)
334 + SIZEOF(small_sarray_hdr)));
335
336 result = (JSAMPARRAY) (hdr+1); /* advance past header */
337
338 /* Insert into list now so free_all does right thing if I fail */
339 /* after allocating only some of the rows... */
340 hdr->next = small_sarray_list;
341 hdr->numrows = 0;
342 hdr->rowsperchunk = rowsperchunk;
343 small_sarray_list = hdr;
344
345 /* Get the rows themselves; on 80x86 these are "far" */
346 currow = 0;
347 while (currow < numrows) {
348 rowsperchunk = MIN(rowsperchunk, numrows - currow);
349#ifdef MEM_STATS
350 total_bytes_sarray += rowsperchunk * samplesperrow * SIZEOF(JSAMPLE)
351 + MALLOC_FAR_OVERHEAD;
352#endif
353 workspace = (JSAMPROW) jget_large((size_t) (rowsperchunk * samplesperrow
354 * SIZEOF(JSAMPLE)));
355 if (workspace == NULL)
356 out_of_memory(3);
357 for (i = rowsperchunk; i > 0; i--) {
358 result[currow++] = workspace;
359 workspace += samplesperrow;
360 }
361 hdr->numrows = currow;
362 }
363
364 return result;
365}
366
367
368METHODDEF void
369free_small_sarray (JSAMPARRAY ptr)
370/* Free a "small" (all-in-memory) 2-D sample array */
371{
372 small_sarray_ptr hdr;
373 small_sarray_ptr * llink;
374 long i;
375
376 hdr = (small_sarray_ptr) ptr;
377 hdr--; /* point back to header */
378
379 /* Remove item from list -- linear search is fast enough */
380 llink = &small_sarray_list;
381 while (*llink != hdr) {
382 if (*llink == NULL)
383 ERREXIT(methods, "Bogus free_small_sarray request");
384 llink = &( (*llink)->next );
385 }
386 *llink = hdr->next;
387
388 /* Free the rows themselves; on 80x86 these are "far" */
389 /* Note we only free the row-group headers! */
390 for (i = 0; i < hdr->numrows; i += hdr->rowsperchunk) {
391 jfree_large((void FAR *) ptr[i]);
392 }
393
394 /* Free header and row pointers */
395 free_small((void *) hdr);
396
397#ifdef MEM_STATS
398 cur_num_sarray--;
399#endif
400}
401
402
403/*
404 * Management of "small" (all-in-memory) 2-D coefficient-block arrays.
405 * This is essentially the same as the code for sample arrays, above.
406 */
407
408typedef struct small_barray_struct * small_barray_ptr;
409
410typedef struct small_barray_struct {
411 small_barray_ptr next; /* next in list of allocated barrays */
412 long numrows; /* # of rows in this array */
413 long rowsperchunk; /* max # of rows per allocation chunk */
414 } small_barray_hdr;
415
416static small_barray_ptr small_barray_list; /* head of list */
417
418
419METHODDEF JBLOCKARRAY
420alloc_small_barray (long blocksperrow, long numrows)
421/* Allocate a "small" (all-in-memory) 2-D coefficient-block array */
422{
423 small_barray_ptr hdr;
424 JBLOCKARRAY result;
425 JBLOCKROW workspace;
426 long rowsperchunk, currow, i;
427
428#ifdef MEM_STATS
429 total_num_barray++;
430 cur_num_barray++;
431 if (cur_num_barray > max_num_barray) max_num_barray = cur_num_barray;
432#endif
433
434 /* Calculate max # of rows allowed in one allocation chunk */
435 rowsperchunk = MAX_ALLOC_CHUNK / (blocksperrow * SIZEOF(JBLOCK));
436 if (rowsperchunk <= 0)
437 ERREXIT(methods, "Image too wide for this implementation");
438
439 /* Get space for header and row pointers; this is always "near" on 80x86 */
440 hdr = (small_barray_ptr) alloc_small((size_t) (numrows * SIZEOF(JBLOCKROW)
441 + SIZEOF(small_barray_hdr)));
442
443 result = (JBLOCKARRAY) (hdr+1); /* advance past header */
444
445 /* Insert into list now so free_all does right thing if I fail */
446 /* after allocating only some of the rows... */
447 hdr->next = small_barray_list;
448 hdr->numrows = 0;
449 hdr->rowsperchunk = rowsperchunk;
450 small_barray_list = hdr;
451
452 /* Get the rows themselves; on 80x86 these are "far" */
453 currow = 0;
454 while (currow < numrows) {
455 rowsperchunk = MIN(rowsperchunk, numrows - currow);
456#ifdef MEM_STATS
457 total_bytes_barray += rowsperchunk * blocksperrow * SIZEOF(JBLOCK)
458 + MALLOC_FAR_OVERHEAD;
459#endif
460 workspace = (JBLOCKROW) jget_large((size_t) (rowsperchunk * blocksperrow
461 * SIZEOF(JBLOCK)));
462 if (workspace == NULL)
463 out_of_memory(4);
464 for (i = rowsperchunk; i > 0; i--) {
465 result[currow++] = workspace;
466 workspace += blocksperrow;
467 }
468 hdr->numrows = currow;
469 }
470
471 return result;
472}
473
474
475METHODDEF void
476free_small_barray (JBLOCKARRAY ptr)
477/* Free a "small" (all-in-memory) 2-D coefficient-block array */
478{
479 small_barray_ptr hdr;
480 small_barray_ptr * llink;
481 long i;
482
483 hdr = (small_barray_ptr) ptr;
484 hdr--; /* point back to header */
485
486 /* Remove item from list -- linear search is fast enough */
487 llink = &small_barray_list;
488 while (*llink != hdr) {
489 if (*llink == NULL)
490 ERREXIT(methods, "Bogus free_small_barray request");
491 llink = &( (*llink)->next );
492 }
493 *llink = hdr->next;
494
495 /* Free the rows themselves; on 80x86 these are "far" */
496 /* Note we only free the row-group headers! */
497 for (i = 0; i < hdr->numrows; i += hdr->rowsperchunk) {
498 jfree_large((void FAR *) ptr[i]);
499 }
500
501 /* Free header and row pointers */
502 free_small((void *) hdr);
503
504#ifdef MEM_STATS
505 cur_num_barray--;
506#endif
507}
508
509
510
511/*
512 * About "big" array management:
513 *
514 * To allow machines with limited memory to handle large images,
515 * all processing in the JPEG system is done a few pixel or block rows
516 * at a time. The above "small" array routines are only used to allocate
517 * strip buffers (as wide as the image, but just a few rows high).
518 * In some cases multiple passes must be made over the data. In these
519 * cases the "big" array routines are used. The array is still accessed
520 * a strip at a time, but the memory manager must save the whole array
521 * for repeated accesses. The intended implementation is that there is
522 * a strip buffer in memory (as high as is possible given the desired memory
523 * limit), plus a backing file that holds the rest of the array.
524 *
525 * The request_big_array routines are told the total size of the image (in case
526 * it is useful to know the total file size that will be needed). They are
527 * also given the unit height, which is the number of rows that will be
528 * accessed at once; the in-memory buffer should be made a multiple of
529 * this height for best efficiency.
530 *
531 * The request routines create control blocks (and may open backing files),
532 * but they don't create the in-memory buffers. This is postponed until
533 * alloc_big_arrays is called. At that time the total amount of space needed
534 * is known (approximately, anyway), so free memory can be divided up fairly.
535 *
536 * The access_big_array routines are responsible for making a specific strip
537 * area accessible (after reading or writing the backing file, if necessary).
538 * Note that the access routines are told whether the caller intends to modify
539 * the accessed strip; during a read-only pass this saves having to rewrite
540 * data to disk.
541 *
542 * The typical access pattern is one top-to-bottom pass to write the data,
543 * followed by one or more read-only top-to-bottom passes. However, other
544 * access patterns may occur while reading. For example, translation of image
545 * formats that use bottom-to-top scan order will require bottom-to-top read
546 * passes. The memory manager need not support multiple write passes nor
547 * funny write orders (meaning that rearranging rows must be handled while
548 * reading data out of the big array, not while putting it in).
549 *
550 * In current usage, the access requests are always for nonoverlapping strips;
551 * that is, successive access start_row numbers always differ by exactly the
552 * unitheight. This allows fairly simple buffer dump/reload logic if the
553 * in-memory buffer is made a multiple of the unitheight. It would be
554 * possible to keep subsampled rather than fullsize data in the "big" arrays,
555 * thus reducing temp file size, if we supported overlapping strip access
556 * (access requests differing by less than the unitheight). At the moment
557 * I don't believe this is worth the extra complexity.
558 */
559
560
561
562/* The control blocks for virtual arrays.
563 * System-dependent info for the associated backing store is hidden inside
564 * the backing_store_info struct.
565 */
566
567struct big_sarray_control {
568 long rows_in_array; /* total virtual array height */
569 long samplesperrow; /* width of array (and of memory buffer) */
570 long unitheight; /* # of rows accessed by access_big_sarray() */
571 JSAMPARRAY mem_buffer; /* the in-memory buffer */
572 long rows_in_mem; /* height of memory buffer */
573 long rowsperchunk; /* allocation chunk size in mem_buffer */
574 long cur_start_row; /* first logical row # in the buffer */
575 boolean dirty; /* do current buffer contents need written? */
576 boolean b_s_open; /* is backing-store data valid? */
577 big_sarray_ptr next; /* link to next big sarray control block */
578 backing_store_info b_s_info; /* System-dependent control info */
579};
580
581static big_sarray_ptr big_sarray_list; /* head of list */
582
583struct big_barray_control {
584 long rows_in_array; /* total virtual array height */
585 long blocksperrow; /* width of array (and of memory buffer) */
586 long unitheight; /* # of rows accessed by access_big_barray() */
587 JBLOCKARRAY mem_buffer; /* the in-memory buffer */
588 long rows_in_mem; /* height of memory buffer */
589 long rowsperchunk; /* allocation chunk size in mem_buffer */
590 long cur_start_row; /* first logical row # in the buffer */
591 boolean dirty; /* do current buffer contents need written? */
592 boolean b_s_open; /* is backing-store data valid? */
593 big_barray_ptr next; /* link to next big barray control block */
594 backing_store_info b_s_info; /* System-dependent control info */
595};
596
597static big_barray_ptr big_barray_list; /* head of list */
598
599
600METHODDEF big_sarray_ptr
601request_big_sarray (long samplesperrow, long numrows, long unitheight)
602/* Request a "big" (virtual-memory) 2-D sample array */
603{
604 big_sarray_ptr result;
605
606 /* get control block */
607 result = (big_sarray_ptr) alloc_small(SIZEOF(struct big_sarray_control));
608
609 result->rows_in_array = numrows;
610 result->samplesperrow = samplesperrow;
611 result->unitheight = unitheight;
612 result->mem_buffer = NULL; /* marks array not yet realized */
613 result->b_s_open = FALSE; /* no associated backing-store object */
614 result->next = big_sarray_list; /* add to list of big arrays */
615 big_sarray_list = result;
616
617 return result;
618}
619
620
621METHODDEF big_barray_ptr
622request_big_barray (long blocksperrow, long numrows, long unitheight)
623/* Request a "big" (virtual-memory) 2-D coefficient-block array */
624{
625 big_barray_ptr result;
626
627 /* get control block */
628 result = (big_barray_ptr) alloc_small(SIZEOF(struct big_barray_control));
629
630 result->rows_in_array = numrows;
631 result->blocksperrow = blocksperrow;
632 result->unitheight = unitheight;
633 result->mem_buffer = NULL; /* marks array not yet realized */
634 result->b_s_open = FALSE; /* no associated backing-store object */
635 result->next = big_barray_list; /* add to list of big arrays */
636 big_barray_list = result;
637
638 return result;
639}
640
641
642METHODDEF void
643alloc_big_arrays (long extra_small_samples, long extra_small_blocks,
644 long extra_medium_space)
645/* Allocate the in-memory buffers for any unrealized "big" arrays */
646/* 'extra' values are upper bounds for total future small-array requests */
647/* and far-heap requests */
648{
649 long total_extra_space = extra_small_samples * SIZEOF(JSAMPLE)
650 + extra_small_blocks * SIZEOF(JBLOCK)
651 + extra_medium_space;
652 long space_per_unitheight, maximum_space, avail_mem;
653 long unitheights, max_unitheights;
654 big_sarray_ptr sptr;
655 big_barray_ptr bptr;
656
657 /* Compute the minimum space needed (unitheight rows in each buffer)
658 * and the maximum space needed (full image height in each buffer).
659 * These may be of use to the system-dependent jmem_available routine.
660 */
661 space_per_unitheight = 0;
662 maximum_space = total_extra_space;
663 for (sptr = big_sarray_list; sptr != NULL; sptr = sptr->next) {
664 if (sptr->mem_buffer == NULL) { /* if not realized yet */
665 space_per_unitheight += sptr->unitheight *
666 sptr->samplesperrow * SIZEOF(JSAMPLE);
667 maximum_space += sptr->rows_in_array *
668 sptr->samplesperrow * SIZEOF(JSAMPLE);
669 }
670 }
671 for (bptr = big_barray_list; bptr != NULL; bptr = bptr->next) {
672 if (bptr->mem_buffer == NULL) { /* if not realized yet */
673 space_per_unitheight += bptr->unitheight *
674 bptr->blocksperrow * SIZEOF(JBLOCK);
675 maximum_space += bptr->rows_in_array *
676 bptr->blocksperrow * SIZEOF(JBLOCK);
677 }
678 }
679
680 if (space_per_unitheight <= 0)
681 return; /* no unrealized arrays, no work */
682
683 /* Determine amount of memory to actually use; this is system-dependent. */
684 avail_mem = jmem_available(space_per_unitheight + total_extra_space,
685 maximum_space);
686
687 /* If the maximum space needed is available, make all the buffers full
688 * height; otherwise parcel it out with the same number of unitheights
689 * in each buffer.
690 */
691 if (avail_mem >= maximum_space)
692 max_unitheights = 1000000000L;
693 else {
694 max_unitheights = (avail_mem - total_extra_space) / space_per_unitheight;
695 /* If there doesn't seem to be enough space, try to get the minimum
696 * anyway. This allows a "stub" implementation of jmem_available().
697 */
698 if (max_unitheights <= 0)
699 max_unitheights = 1;
700 }
701
702 /* Allocate the in-memory buffers and initialize backing store as needed. */
703
704 for (sptr = big_sarray_list; sptr != NULL; sptr = sptr->next) {
705 if (sptr->mem_buffer == NULL) { /* if not realized yet */
706 unitheights = (sptr->rows_in_array + sptr->unitheight - 1L)
707 / sptr->unitheight;
708 if (unitheights <= max_unitheights) {
709 /* This buffer fits in memory */
710 sptr->rows_in_mem = sptr->rows_in_array;
711 } else {
712 /* It doesn't fit in memory, create backing store. */
713 sptr->rows_in_mem = max_unitheights * sptr->unitheight;
714 jopen_backing_store(& sptr->b_s_info,
715 sptr->rows_in_array
716 * sptr->samplesperrow * SIZEOF(JSAMPLE));
717 sptr->b_s_open = TRUE;
718 }
719 sptr->mem_buffer = alloc_small_sarray(sptr->samplesperrow,
720 sptr->rows_in_mem);
721 /* Reach into the small_sarray header and get the rowsperchunk field.
722 * Yes, I know, this is horrible coding practice.
723 */
724 sptr->rowsperchunk =
725 ((small_sarray_ptr) sptr->mem_buffer)[-1].rowsperchunk;
726 sptr->cur_start_row = 0;
727 sptr->dirty = FALSE;
728 }
729 }
730
731 for (bptr = big_barray_list; bptr != NULL; bptr = bptr->next) {
732 if (bptr->mem_buffer == NULL) { /* if not realized yet */
733 unitheights = (bptr->rows_in_array + bptr->unitheight - 1L)
734 / bptr->unitheight;
735 if (unitheights <= max_unitheights) {
736 /* This buffer fits in memory */
737 bptr->rows_in_mem = bptr->rows_in_array;
738 } else {
739 /* It doesn't fit in memory, create backing store. */
740 bptr->rows_in_mem = max_unitheights * bptr->unitheight;
741 jopen_backing_store(& bptr->b_s_info,
742 bptr->rows_in_array
743 * bptr->blocksperrow * SIZEOF(JBLOCK));
744 bptr->b_s_open = TRUE;
745 }
746 bptr->mem_buffer = alloc_small_barray(bptr->blocksperrow,
747 bptr->rows_in_mem);
748 /* Reach into the small_barray header and get the rowsperchunk field. */
749 bptr->rowsperchunk =
750 ((small_barray_ptr) bptr->mem_buffer)[-1].rowsperchunk;
751 bptr->cur_start_row = 0;
752 bptr->dirty = FALSE;
753 }
754 }
755}
756
757
758LOCAL void
759do_sarray_io (big_sarray_ptr ptr, boolean writing)
760/* Do backing store read or write of a "big" sample array */
761{
762 long bytesperrow, file_offset, byte_count, rows, i;
763
764 bytesperrow = ptr->samplesperrow * SIZEOF(JSAMPLE);
765 file_offset = ptr->cur_start_row * bytesperrow;
766 /* Loop to read or write each allocation chunk in mem_buffer */
767 for (i = 0; i < ptr->rows_in_mem; i += ptr->rowsperchunk) {
768 /* One chunk, but check for short chunk at end of buffer */
769 rows = MIN(ptr->rowsperchunk, ptr->rows_in_mem - i);
770 /* Transfer no more than fits in file */
771 rows = MIN(rows, ptr->rows_in_array - (ptr->cur_start_row + i));
772 if (rows <= 0) /* this chunk might be past end of file! */
773 break;
774 byte_count = rows * bytesperrow;
775 if (writing)
776 (*ptr->b_s_info.write_backing_store) (& ptr->b_s_info,
777 (void FAR *) ptr->mem_buffer[i],
778 file_offset, byte_count);
779 else
780 (*ptr->b_s_info.read_backing_store) (& ptr->b_s_info,
781 (void FAR *) ptr->mem_buffer[i],
782 file_offset, byte_count);
783 file_offset += byte_count;
784 }
785}
786
787
788LOCAL void
789do_barray_io (big_barray_ptr ptr, boolean writing)
790/* Do backing store read or write of a "big" coefficient-block array */
791{
792 long bytesperrow, file_offset, byte_count, rows, i;
793
794 bytesperrow = ptr->blocksperrow * SIZEOF(JBLOCK);
795 file_offset = ptr->cur_start_row * bytesperrow;
796 /* Loop to read or write each allocation chunk in mem_buffer */
797 for (i = 0; i < ptr->rows_in_mem; i += ptr->rowsperchunk) {
798 /* One chunk, but check for short chunk at end of buffer */
799 rows = MIN(ptr->rowsperchunk, ptr->rows_in_mem - i);
800 /* Transfer no more than fits in file */
801 rows = MIN(rows, ptr->rows_in_array - (ptr->cur_start_row + i));
802 if (rows <= 0) /* this chunk might be past end of file! */
803 break;
804 byte_count = rows * bytesperrow;
805 if (writing)
806 (*ptr->b_s_info.write_backing_store) (& ptr->b_s_info,
807 (void FAR *) ptr->mem_buffer[i],
808 file_offset, byte_count);
809 else
810 (*ptr->b_s_info.read_backing_store) (& ptr->b_s_info,
811 (void FAR *) ptr->mem_buffer[i],
812 file_offset, byte_count);
813 file_offset += byte_count;
814 }
815}
816
817
818METHODDEF JSAMPARRAY
819access_big_sarray (big_sarray_ptr ptr, long start_row, boolean writable)
820/* Access the part of a "big" sample array starting at start_row */
821/* and extending for ptr->unitheight rows. writable is true if */
822/* caller intends to modify the accessed area. */
823{
824 /* debugging check */
825 if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_array ||
826 ptr->mem_buffer == NULL)
827 ERREXIT(methods, "Bogus access_big_sarray request");
828
829 /* Make the desired part of the virtual array accessible */
830 if (start_row < ptr->cur_start_row ||
831 start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
832 if (! ptr->b_s_open)
833 ERREXIT(methods, "Virtual array controller messed up");
834 /* Flush old buffer contents if necessary */
835 if (ptr->dirty) {
836 do_sarray_io(ptr, TRUE);
837 ptr->dirty = FALSE;
838 }
839 /* Decide what part of virtual array to access.
840 * Algorithm: if target address > current window, assume forward scan,
841 * load starting at target address. If target address < current window,
842 * assume backward scan, load so that target address is top of window.
843 * Note that when switching from forward write to forward read, will have
844 * start_row = 0, so the limiting case applies and we load from 0 anyway.
845 */
846 if (start_row > ptr->cur_start_row) {
847 ptr->cur_start_row = start_row;
848 } else {
849 ptr->cur_start_row = start_row + ptr->unitheight - ptr->rows_in_mem;
850 if (ptr->cur_start_row < 0)
851 ptr->cur_start_row = 0; /* don't fall off front end of file */
852 }
853 /* If reading, read in the selected part of the array.
854 * If we are writing, we need not pre-read the selected portion,
855 * since the access sequence constraints ensure it would be garbage.
856 */
857 if (! writable) {
858 do_sarray_io(ptr, FALSE);
859 }
860 }
861 /* Flag the buffer dirty if caller will write in it */
862 if (writable)
863 ptr->dirty = TRUE;
864 /* Return address of proper part of the buffer */
865 return ptr->mem_buffer + (start_row - ptr->cur_start_row);
866}
867
868
869METHODDEF JBLOCKARRAY
870access_big_barray (big_barray_ptr ptr, long start_row, boolean writable)
871/* Access the part of a "big" coefficient-block array starting at start_row */
872/* and extending for ptr->unitheight rows. writable is true if */
873/* caller intends to modify the accessed area. */
874{
875 /* debugging check */
876 if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_array ||
877 ptr->mem_buffer == NULL)
878 ERREXIT(methods, "Bogus access_big_barray request");
879
880 /* Make the desired part of the virtual array accessible */
881 if (start_row < ptr->cur_start_row ||
882 start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
883 if (! ptr->b_s_open)
884 ERREXIT(methods, "Virtual array controller messed up");
885 /* Flush old buffer contents if necessary */
886 if (ptr->dirty) {
887 do_barray_io(ptr, TRUE);
888 ptr->dirty = FALSE;
889 }
890 /* Decide what part of virtual array to access.
891 * Algorithm: if target address > current window, assume forward scan,
892 * load starting at target address. If target address < current window,
893 * assume backward scan, load so that target address is top of window.
894 * Note that when switching from forward write to forward read, will have
895 * start_row = 0, so the limiting case applies and we load from 0 anyway.
896 */
897 if (start_row > ptr->cur_start_row) {
898 ptr->cur_start_row = start_row;
899 } else {
900 ptr->cur_start_row = start_row + ptr->unitheight - ptr->rows_in_mem;
901 if (ptr->cur_start_row < 0)
902 ptr->cur_start_row = 0; /* don't fall off front end of file */
903 }
904 /* If reading, read in the selected part of the array.
905 * If we are writing, we need not pre-read the selected portion,
906 * since the access sequence constraints ensure it would be garbage.
907 */
908 if (! writable) {
909 do_barray_io(ptr, FALSE);
910 }
911 }
912 /* Flag the buffer dirty if caller will write in it */
913 if (writable)
914 ptr->dirty = TRUE;
915 /* Return address of proper part of the buffer */
916 return ptr->mem_buffer + (start_row - ptr->cur_start_row);
917}
918
919
920METHODDEF void
921free_big_sarray (big_sarray_ptr ptr)
922/* Free a "big" (virtual-memory) 2-D sample array */
923{
924 big_sarray_ptr * llink;
925
926 /* Remove item from list -- linear search is fast enough */
927 llink = &big_sarray_list;
928 while (*llink != ptr) {
929 if (*llink == NULL)
930 ERREXIT(methods, "Bogus free_big_sarray request");
931 llink = &( (*llink)->next );
932 }
933 *llink = ptr->next;
934
935 if (ptr->b_s_open) /* there may be no backing store */
936 (*ptr->b_s_info.close_backing_store) (& ptr->b_s_info);
937
938 if (ptr->mem_buffer != NULL) /* just in case never realized */
939 free_small_sarray(ptr->mem_buffer);
940
941 free_small((void *) ptr); /* free the control block too */
942}
943
944
945METHODDEF void
946free_big_barray (big_barray_ptr ptr)
947/* Free a "big" (virtual-memory) 2-D coefficient-block array */
948{
949 big_barray_ptr * llink;
950
951 /* Remove item from list -- linear search is fast enough */
952 llink = &big_barray_list;
953 while (*llink != ptr) {
954 if (*llink == NULL)
955 ERREXIT(methods, "Bogus free_big_barray request");
956 llink = &( (*llink)->next );
957 }
958 *llink = ptr->next;
959
960 if (ptr->b_s_open) /* there may be no backing store */
961 (*ptr->b_s_info.close_backing_store) (& ptr->b_s_info);
962
963 if (ptr->mem_buffer != NULL) /* just in case never realized */
964 free_small_barray(ptr->mem_buffer);
965
966 free_small((void *) ptr); /* free the control block too */
967}
968
969
970/*
971 * Cleanup: free anything that's been allocated since jselmemmgr().
972 */
973
974METHODDEF void
975free_all (void)
976{
977 /* First free any open "big" arrays -- these may release small arrays */
978 while (big_sarray_list != NULL)
979 free_big_sarray(big_sarray_list);
980 while (big_barray_list != NULL)
981 free_big_barray(big_barray_list);
982 /* Free any open small arrays -- these may release small objects */
983 /* +1's are because we must pass a pointer to the data, not the header */
984 while (small_sarray_list != NULL)
985 free_small_sarray((JSAMPARRAY) (small_sarray_list + 1));
986 while (small_barray_list != NULL)
987 free_small_barray((JBLOCKARRAY) (small_barray_list + 1));
988 /* Free any remaining small objects */
989 while (small_list != NULL)
990 free_small((void *) (small_list + 1));
991#ifdef NEED_ALLOC_MEDIUM
992 while (medium_list != NULL)
993 free_medium((void FAR *) (medium_list + 1));
994#endif
995
996 jmem_term(); /* system-dependent cleanup */
997
998#ifdef MEM_STATS
999 if (methods->trace_level > 0)
1000 print_mem_stats(); /* print optional memory usage statistics */
1001#endif
1002}
1003
1004
1005/*
1006 * The method selection routine for virtual memory systems.
1007 * The system-dependent setup routine should call this routine
1008 * to install the necessary method pointers in the supplied struct.
1009 */
1010
1011GLOBAL void
1012jselmemmgr (external_methods_ptr emethods)
1013{
1014 methods = emethods; /* save struct addr for error exit access */
1015
1016 emethods->alloc_small = alloc_small;
1017 emethods->free_small = free_small;
1018#ifdef NEED_ALLOC_MEDIUM
1019 emethods->alloc_medium = alloc_medium;
1020 emethods->free_medium = free_medium;
1021#else
1022 emethods->alloc_medium = alloc_small;
1023 emethods->free_medium = free_small;
1024#endif
1025 emethods->alloc_small_sarray = alloc_small_sarray;
1026 emethods->free_small_sarray = free_small_sarray;
1027 emethods->alloc_small_barray = alloc_small_barray;
1028 emethods->free_small_barray = free_small_barray;
1029 emethods->request_big_sarray = request_big_sarray;
1030 emethods->request_big_barray = request_big_barray;
1031 emethods->alloc_big_arrays = alloc_big_arrays;
1032 emethods->access_big_sarray = access_big_sarray;
1033 emethods->access_big_barray = access_big_barray;
1034 emethods->free_big_sarray = free_big_sarray;
1035 emethods->free_big_barray = free_big_barray;
1036 emethods->free_all = free_all;
1037
1038 /* Initialize list headers to empty */
1039 small_list = NULL;
1040#ifdef NEED_ALLOC_MEDIUM
1041 medium_list = NULL;
1042#endif
1043 small_sarray_list = NULL;
1044 small_barray_list = NULL;
1045 big_sarray_list = NULL;
1046 big_barray_list = NULL;
1047
1048 jmem_init(emethods); /* system-dependent initialization */
1049}