1 /* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gxclmem.c,v 1.5 2002/06/16 05:48:55 lpd Exp $ */
18 /* RAM-based command list implementation */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gxclmem.h"
23
24 /*
25 * Based on: memfile.c Version: 1.4 3/21/95 14:59:33 by Ray Johnston.
26 * Copyright assigned to Aladdin Enterprises.
27 */
28
29 /*****************************************************************************
30
31 This package is more or less optimal for use by the clist routines, with
32 a couple of the more likely to change "tuning" parameters given in the
33 two macros below -- NEED_TO_COMPRESS and GET_NUM_RAW_BUFFERS. Usually
34 the NEED_TO_COMPRESS decision will be deferred as long as possible based
35 on some total system free RAM space remaining.
36
37 The data structures are in "memfile.h", and the primary 'tuning' parameter
38 is MEMFILE_DATA_SIZE. This should not be too small to keep the overhead
39 ratio of the block structures to the clist data small. A value of 16384
40 is probably in the ballpark.
41
42 The concept is that a memory based "file" is created initially without
43 compression, with index blocks every MEMFILE_DATA_SIZE of the file. The
44 primary blocks (used by the memfile_fseek logic) for indexing into the
45 file are called 'logical' (LOG_MEMFILE_BLK) and the data in stored in a
46 different block called a 'physical' block (PHYS_MEMFILE_BLK). When the
47 file is not yet compressed, indicated by (f->phys_curr==NULL), then there
48 is one physical block for each logical block. The physical block also has
49 the 'data_limit' set to NULL if the data is not compressed. Thus when a
50 file is not compressed there is one physical block for each logical block.
51
52 COMPRESSION.
53
54 When compression is triggered for a file then all of the blocks except
55 the last are compressed. Compression will result in a physical block
56 that holds data for more than one logical block. Each logical block now
57 points to the start of compressed data in a physical block with the
58 'phys_pdata' pointer. The 'data_limit' pointer in the physical block is
59 where the compression logic stopped storing data (as stream data
60 compressors are allowed to do). The data for the logical block may span
61 to the next physical block. Once physical blocks are compressed, they are
62 chained together using the 'link' field.
63
64 The 'f->phys_curr' points to the block being filled by compression, with
65 the 'f->wt.ptr' pointing to the last byte filled in the block. These are
66 used during subsequent compression when the last logical block of the
67 file fills the physical block.
68
69 DECOMPRESSION.
70
71 During reading the clist, if the logical block points to an uncompressed
72 physical block, then 'memfile_get_pdata' simply sets the 'pdata' and the
73 'pdata_end' pointers. If the logical block was compressed, then it may
74 still be resident in a cache of decompression buffers. The number of these
75 decompression buffers is not critical -- even one is enough, but having
76 more may prevent decompressing blocks more than once (a cache_miss). The
77 number of decompression buffers, called "raw" buffers, that are attempted
78 to allocate can be changed with the GET_NUM_RAW_BUFFERS macro, but no
79 error occurs if less than that number can be allocated.
80
81 If the logical block still resides in a decompression cache buffer, then
82 the 'raw_block' will identify the block. If the data for a logical block
83 only exists in compressed form, then the "tail" of the list of decompression
84 buffers is re-used, marking the 'raw_block' of the logical block that was
85 previously associated with this data to NULL.
86
87 Whichever raw decompression buffer is accessed is moved to the head of the
88 decompression buffer list in order to keep the tail of the list as the
89 "least recently used".
90
91 There are some DEBUG global static variables used to count the number of
92 cache hits "tot_cache_hits" and the number of times a logical block is
93 decompressed "tot_cache_miss". Note that the actual number of cache miss
94 events is 'f->log_length/MEMFILE_DATA_SIZE - tot_cache_miss' since we
95 assume that every logical block must be decmpressed at least once.
96
97 Empirical results so far indicate that if one cache raw buffer for every
98 32 logical blocks, then the hit/miss ratio exceeds 99%. Of course, the
99 number of raw buffers should be more than 1 if possible, and in many
100 implementations (single threaded), the memory usage does not increase
101 during the page output step so almost all of memory can be used for
102 these raw buffers to prevent the likelihood of a cache miss.
103
104 Of course, this is dependent on reasonably efficient clist blocking
105 during writing which is dependent on the data and on the BufferSpace
106 value which determines the number of clist band data buffers available.
107 Empirical testing shows that the overall efficiency is best if the
108 BufferSpace value is 1,000,000 (as in the original Ghostscript source).
109 [Note: I expected to be able to use smaller buffer sizes for some cases,
110 but this resulted in a high level of thrashing...RJJ]
111
112 LIMITATIONS.
113
114 The most serious limitation is caused by the way 'memfile_fwrite' decides
115 to free up and re-initialize a file. If memfile_fwrite is called after
116 a seek to any location except the start of the file, then an error is
117 issued since logic is not present to properly free up on a partial file.
118 This is not a problem as used by the 'clist' logic since rewind is used
119 to position to the start of a file when re-using it after an 'erasepage'.
120
121 Since the 'clist' logic always traverses the clist using fseek's to ever
122 increasing locations, no optimizations of backward seeks was implemented.
123 This would be relatively easy with back chain links or bi-directional
124 "X-OR" pointer information to link the logical block chain. The rewind
125 function is optimal and moves directly to the start of the file.
126
127 ********************************************************************************/
128
129 /*
130 The need to compress should be conditional on the amount of available
131 memory, but we don't have a way to communicate this to these routines.
132 Instead, we simply start compressing when we've allocated more than
133 COMPRESSION_THRESHOLD amount of data. The threshold should be at
134 least as large as the fixed overhead of the compressor plus the
135 decompressor, plus the expected compressed size of a block that size.
136 */
137 private const long COMPRESSION_THRESHOLD = 300000;
138
139 #define NEED_TO_COMPRESS(f)\
140 ((f)->ok_to_compress && (f)->total_space > COMPRESSION_THRESHOLD)
141
142 /* FOR NOW ALLOCATE 1 raw buffer for every 32 blocks (at least 8) */
143 #define GET_NUM_RAW_BUFFERS( f ) \
144 max(f->log_length/MEMFILE_DATA_SIZE/32, 8)
145
146 #define MALLOC(f, siz, cname)\
147 (void *)gs_alloc_bytes((f)->data_memory, siz, cname)
148 #define FREE(f, obj, cname)\
149 (gs_free_object((f)->data_memory, obj, cname),\
150 (f)->total_space -= sizeof(*(obj)))
151
152 /* Structure descriptor for GC */
153 private_st_MEMFILE();
154
155 /* forward references */
156 private void memfile_free_mem(MEMFILE * f);
157 private int memfile_init_empty(MEMFILE * f);
158
159 /************************************************/
160 /* #define DEBUG /- force statistics -/ */
161 /************************************************/
162
163 #ifdef DEBUG
164 long tot_compressed;
165 long tot_raw;
166 long tot_cache_miss;
167 long tot_cache_hits;
168 long tot_swap_out;
169
170 /*
171 The following pointers are here only for helping with a dumb debugger
172 that can't inspect local variables!
173 */
174 byte *decomp_wt_ptr0, *decomp_wt_limit0;
175 const byte *decomp_rd_ptr0, *decomp_rd_limit0;
176 byte *decomp_wt_ptr1, *decomp_wt_limit1;
177 const byte *decomp_rd_ptr1, *decomp_rd_limit1;
178
179 #endif
180
181 /* ----------------------------- Memory Allocation --------------------- */
182 void * /* allocated memory's address, 0 if failure */
allocateWithReserve(MEMFILE * f,int sizeofBlock,int * return_code,const char * allocName,const char * errorMessage)183 allocateWithReserve(
184 MEMFILE *f, /* file to allocate mem to */
185 int sizeofBlock, /* size of block to allocate */
186 int *return_code, /* RET 0 ok, -ve GS-style error, or +1 if OK but low memory */
187 const char *allocName, /* name to allocate by */
188 const char *errorMessage /* error message to print */
189 )
190 {
191 int code = 0; /* assume success */
192 void *block = MALLOC(f, sizeofBlock, allocName);
193
194 if (block == NULL) {
195 /* Try to recover block from reserve */
196 if (sizeofBlock == sizeof(LOG_MEMFILE_BLK)) {
197 if (f->reserveLogBlockCount > 0) {
198 block = f->reserveLogBlockChain;
199 f->reserveLogBlockChain = f->reserveLogBlockChain->link;
200 --f->reserveLogBlockCount;
201 }
202 } else if (sizeofBlock == sizeof(PHYS_MEMFILE_BLK) ||
203 sizeofBlock == sizeof(RAW_BUFFER)
204 ) {
205 if (f->reservePhysBlockCount > 0) {
206 block = f->reservePhysBlockChain;
207 f->reservePhysBlockChain = f->reservePhysBlockChain->link;
208 --f->reservePhysBlockCount;
209 }
210 }
211 if (block != NULL)
212 code = 1; /* successful, but allocated from reserve */
213 }
214 if (block != NULL)
215 f->total_space += sizeofBlock;
216 else
217 code = gs_note_error(gs_error_VMerror);
218 *return_code = code;
219 return block;
220 }
221
222 /* ---------------- Open/close/unlink ---------------- */
223
224 int
memfile_fopen(char fname[gp_file_name_sizeof],const char * fmode,clist_file_ptr * pf,gs_memory_t * mem,gs_memory_t * data_mem,bool ok_to_compress)225 memfile_fopen(char fname[gp_file_name_sizeof], const char *fmode,
226 clist_file_ptr /*MEMFILE * */ * pf,
227 gs_memory_t *mem, gs_memory_t *data_mem, bool ok_to_compress)
228 {
229 MEMFILE *f = 0;
230 int code = 0;
231
232 /* We don't implement reopening an existing file. */
233 if (fname[0] != 0 || fmode[0] != 'w') {
234 code = gs_note_error(gs_error_invalidfileaccess);
235 goto finish;
236 }
237
238 /* There is no need to set fname in this implementation, */
239 /* but we do it anyway. */
240 fname[0] = (ok_to_compress ? 'a' : 'b');
241 fname[1] = 0;
242
243 f = gs_alloc_struct(mem, MEMFILE, &st_MEMFILE,
244 "memfile_open_scratch(MEMFILE)");
245 if (f == NULL) {
246 eprintf1("memfile_open_scratch(%s): gs_alloc_struct failed\n", fname);
247 code = gs_note_error(gs_error_VMerror);
248 goto finish;
249 }
250 f->memory = mem;
251 f->data_memory = data_mem;
252 /* init an empty file, BEFORE allocating de/compress state */
253 f->compress_state = 0; /* make clean for GC, or alloc'n failure */
254 f->decompress_state = 0;
255 f->total_space = 0;
256 f->reservePhysBlockChain = NULL;
257 f->reservePhysBlockCount = 0;
258 f->reserveLogBlockChain = NULL;
259 f->reserveLogBlockCount = 0;
260 /* init an empty file */
261 if ((code = memfile_init_empty(f)) < 0)
262 goto finish;
263 if ((code = memfile_set_memory_warning(f, 0)) < 0)
264 goto finish;
265 /*
266 * Disregard the ok_to_compress flag, since the size threshold gives us
267 * a much better criterion for deciding when compression is appropriate.
268 */
269 f->ok_to_compress = /*ok_to_compress */ true;
270 f->compress_state = 0; /* make clean for GC */
271 f->decompress_state = 0;
272 if (f->ok_to_compress) {
273 const stream_state *compress_proto = clist_compressor_state(NULL);
274 const stream_state *decompress_proto = clist_decompressor_state(NULL);
275 const stream_template *compress_template = compress_proto->template;
276 const stream_template *decompress_template = decompress_proto->template;
277
278 f->compress_state =
279 gs_alloc_struct(mem, stream_state, compress_template->stype,
280 "memfile_open_scratch(compress_state)");
281 f->decompress_state =
282 gs_alloc_struct(mem, stream_state, decompress_template->stype,
283 "memfile_open_scratch(decompress_state)");
284 if (f->compress_state == 0 || f->decompress_state == 0) {
285 eprintf1("memfile_open_scratch(%s): gs_alloc_struct failed\n", fname);
286 code = gs_note_error(gs_error_VMerror);
287 goto finish;
288 }
289 memcpy(f->compress_state, compress_proto,
290 gs_struct_type_size(compress_template->stype));
291 f->compress_state->memory = mem;
292 memcpy(f->decompress_state, decompress_proto,
293 gs_struct_type_size(decompress_template->stype));
294 f->decompress_state->memory = mem;
295 if (compress_template->set_defaults)
296 (*compress_template->set_defaults) (f->compress_state);
297 if (decompress_template->set_defaults)
298 (*decompress_template->set_defaults) (f->decompress_state);
299 }
300 f->total_space = 0;
301
302 #ifdef DEBUG
303 /* If this is the start, init some statistics. */
304 /* Hack: we know the 'a' file is opened first. */
305 if (*fname == 'a') {
306 tot_compressed = 0;
307 tot_raw = 0;
308 tot_cache_miss = 0;
309 tot_cache_hits = 0;
310 tot_swap_out = 0;
311 }
312 #endif
313 finish:
314 if (code < 0) {
315 /* return failure, clean up memory before leaving */
316 if (f != NULL)
317 memfile_fclose((clist_file_ptr)f, fname, true);
318 } else {
319 /* return success */
320 *pf = f;
321 }
322 return code;
323 }
324
325 int
memfile_fclose(clist_file_ptr cf,const char * fname,bool delete)326 memfile_fclose(clist_file_ptr cf, const char *fname, bool delete)
327 {
328 MEMFILE *const f = (MEMFILE *)cf;
329
330 /* We don't implement closing without deletion. */
331 if (!delete)
332 return_error(gs_error_invalidfileaccess);
333 memfile_free_mem(f);
334
335 /* Free reserve blocks; don't do it in memfile_free_mem because */
336 /* that routine gets called to reinit file */
337 while (f->reserveLogBlockChain != NULL) {
338 LOG_MEMFILE_BLK *block = f->reserveLogBlockChain;
339
340 f->reserveLogBlockChain = block->link;
341 FREE(f, block, "memfile_set_block_size");
342 }
343 while (f->reservePhysBlockChain != NULL) {
344 PHYS_MEMFILE_BLK *block = f->reservePhysBlockChain;
345
346 f->reservePhysBlockChain = block->link;
347 FREE(f, block, "memfile_set_block_size");
348 }
349
350 /* deallocate de/compress state */
351 gs_free_object(f->memory, f->decompress_state,
352 "memfile_close_and_unlink(decompress_state)");
353 gs_free_object(f->memory, f->compress_state,
354 "memfile_close_and_unlink(compress_state)");
355
356 /* deallocate the memfile object proper */
357 gs_free_object(f->memory, f, "memfile_close_and_unlink(MEMFILE)");
358 return 0;
359 }
360
361 int
memfile_unlink(const char * fname)362 memfile_unlink(const char *fname)
363 {
364 /*
365 * Since we have no way to represent a memfile other than by the
366 * pointer, we don't (can't) implement unlinking.
367 */
368 return_error(gs_error_invalidfileaccess);
369 }
370
371 /* ---------------- Writing ---------------- */
372
373 /* Pre-alloc enough reserve mem blox to guarantee a write of N bytes will succeed */
374 int /* returns 0 ok, gs_error_VMerror if insufficient */
memfile_set_memory_warning(clist_file_ptr cf,int bytes_left)375 memfile_set_memory_warning(clist_file_ptr cf, int bytes_left)
376 {
377 MEMFILE *const f = (MEMFILE *)cf;
378 int code = 0;
379 /*
380 * Determine req'd memory block count from bytes_left.
381 * Allocate enough phys & log blocks to hold bytes_left
382 * + 1 phys blk for compress_log_blk + 1 phys blk for decompress.
383 */
384 int logNeeded =
385 (bytes_left + MEMFILE_DATA_SIZE - 1) / MEMFILE_DATA_SIZE;
386 int physNeeded = logNeeded;
387
388 if (bytes_left > 0)
389 ++physNeeded;
390 if (f->raw_head == NULL)
391 ++physNeeded; /* have yet to allocate read buffers */
392
393 /* Allocate or free memory depending on need */
394 while (logNeeded > f->reserveLogBlockCount) {
395 LOG_MEMFILE_BLK *block =
396 MALLOC( f, sizeof(LOG_MEMFILE_BLK), "memfile_set_block_size" );
397
398 if (block == NULL) {
399 code = gs_note_error(gs_error_VMerror);
400 goto finish;
401 }
402 block->link = f->reserveLogBlockChain;
403 f->reserveLogBlockChain = block;
404 ++f->reserveLogBlockCount;
405 }
406 while (logNeeded < f->reserveLogBlockCount) {
407 LOG_MEMFILE_BLK *block = f->reserveLogBlockChain;
408
409 f->reserveLogBlockChain = block->link;
410 FREE(f, block, "memfile_set_block_size");
411 --f->reserveLogBlockCount;
412 }
413 while (physNeeded > f->reservePhysBlockCount) {
414 PHYS_MEMFILE_BLK *block =
415 MALLOC( f,
416 max( sizeof(PHYS_MEMFILE_BLK), sizeof(RAW_BUFFER) ),
417 "memfile_set_block_size");
418
419 if (block == NULL) {
420 code = gs_note_error(gs_error_VMerror);
421 goto finish;
422 }
423 block->link = f->reservePhysBlockChain;
424 f->reservePhysBlockChain = block;
425 ++f->reservePhysBlockCount;
426 }
427 while (physNeeded < f->reservePhysBlockCount) {
428 PHYS_MEMFILE_BLK *block = f->reservePhysBlockChain;
429
430 f->reservePhysBlockChain = block->link;
431 FREE(f, block, "memfile_set_block_size");
432 --f->reservePhysBlockCount;
433 }
434 f->error_code = 0; /* memfile_set_block_size is how user resets this */
435 finish:
436 return code;
437 }
438
439 private int
compress_log_blk(MEMFILE * f,LOG_MEMFILE_BLK * bp)440 compress_log_blk(MEMFILE * f, LOG_MEMFILE_BLK * bp)
441 {
442 int status;
443 int ecode = 0; /* accumulate low-memory warnings */
444 int code;
445 long compressed_size;
446 byte *start_ptr;
447 PHYS_MEMFILE_BLK *newphys;
448
449 /* compress this block */
450 f->rd.ptr = (const byte *)(bp->phys_blk->data) - 1;
451 f->rd.limit = f->rd.ptr + MEMFILE_DATA_SIZE;
452
453 bp->phys_blk = f->phys_curr;
454 bp->phys_pdata = (char *)(f->wt.ptr) + 1;
455 if (f->compress_state->template->reinit != 0)
456 (*f->compress_state->template->reinit)(f->compress_state);
457 compressed_size = 0;
458
459 start_ptr = f->wt.ptr;
460 status = (*f->compress_state->template->process)(f->compress_state,
461 &(f->rd), &(f->wt), true);
462 bp->phys_blk->data_limit = (char *)(f->wt.ptr);
463
464 if (status == 1) { /* More output space needed (see strimpl.h) */
465 /* allocate another physical block, then compress remainder */
466 compressed_size = f->wt.limit - start_ptr;
467 newphys =
468 allocateWithReserve(f, sizeof(*newphys), &code, "memfile newphys",
469 "compress_log_blk : MALLOC for 'newphys' failed\n");
470 if (code < 0)
471 return code;
472 ecode |= code; /* accumulate any low-memory warnings */
473 newphys->link = NULL;
474 bp->phys_blk->link = newphys;
475 f->phys_curr = newphys;
476 f->wt.ptr = (byte *) (newphys->data) - 1;
477 f->wt.limit = f->wt.ptr + MEMFILE_DATA_SIZE;
478
479 start_ptr = f->wt.ptr;
480 status =
481 (*f->compress_state->template->process)(f->compress_state,
482 &(f->rd), &(f->wt), true);
483 if (status != 0) {
484 /*
485 * You'd think the above line is a bug, but in real life 1 src
486 * block never ends up getting split across 3 dest blocks.
487 */
488 /* CHANGE memfile_set_memory_warning if this assumption changes. */
489 eprintf("Compression required more than one full block!\n");
490 return_error(gs_error_Fatal);
491 }
492 newphys->data_limit = (char *)(f->wt.ptr);
493 }
494 compressed_size += f->wt.ptr - start_ptr;
495 if (compressed_size > MEMFILE_DATA_SIZE) {
496 eprintf2("\nCompression didn't - raw=%d, compressed=%ld\n",
497 MEMFILE_DATA_SIZE, compressed_size);
498 }
499 #ifdef DEBUG
500 tot_compressed += compressed_size;
501 #endif
502 return (status < 0 ? gs_note_error(gs_error_ioerror) : ecode);
503 } /* end "compress_log_blk()" */
504
505 /* Internal (private) routine to handle end of logical block */
506 private int /* ret 0 ok, -ve error, or +ve low-memory warning */
memfile_next_blk(MEMFILE * f)507 memfile_next_blk(MEMFILE * f)
508 {
509 LOG_MEMFILE_BLK *bp = f->log_curr_blk;
510 LOG_MEMFILE_BLK *newbp;
511 PHYS_MEMFILE_BLK *newphys, *oldphys;
512 int ecode = 0; /* accumulate low-memory warnings */
513 int code;
514
515 if (f->phys_curr == NULL) { /* means NOT compressing */
516 /* allocate a new block */
517 newphys =
518 allocateWithReserve(f, sizeof(*newphys), &code, "memfile newphys",
519 "memfile_next_blk: MALLOC 1 for 'newphys' failed\n");
520 if (code < 0)
521 return code;
522 ecode |= code; /* accumulate low-mem warnings */
523 newphys->link = NULL;
524 newphys->data_limit = NULL; /* raw */
525
526 newbp =
527 allocateWithReserve(f, sizeof(*newbp), &code, "memfile newbp",
528 "memfile_next_blk: MALLOC 1 for 'newbp' failed\n");
529 if (code < 0) {
530 FREE(f, newphys, "memfile newphys");
531 return code;
532 }
533 ecode |= code; /* accumulate low-mem warnings */
534 bp->link = newbp;
535 newbp->link = NULL;
536 newbp->raw_block = NULL;
537 f->log_curr_blk = newbp;
538
539 /* check if need to start compressing */
540 if (NEED_TO_COMPRESS(f)) {
541 if_debug0(':', "[:]Beginning compression\n");
542 /* compress the entire file up to this point */
543 if (!f->compressor_initialized) {
544 int code = 0;
545
546 if (f->compress_state->template->init != 0)
547 code = (*f->compress_state->template->init) (f->compress_state);
548 if (code < 0)
549 return_error(gs_error_VMerror); /****** BOGUS ******/
550 if (f->decompress_state->template->init != 0)
551 code = (*f->decompress_state->template->init)
552 (f->decompress_state);
553 if (code < 0)
554 return_error(gs_error_VMerror); /****** BOGUS ******/
555 f->compressor_initialized = true;
556 }
557 /* Write into the new physical block we just allocated, */
558 /* replace it after the loop (after some blocks are freed) */
559 f->phys_curr = newphys;
560 f->wt.ptr = (byte *) (newphys->data) - 1;
561 f->wt.limit = f->wt.ptr + MEMFILE_DATA_SIZE;
562 bp = f->log_head;
563 while (bp != newbp) { /* don't compress last block */
564 int code;
565
566 oldphys = bp->phys_blk;
567 if ((code = compress_log_blk(f, bp)) < 0)
568 return code;
569 ecode |= code;
570 FREE(f, oldphys, "memfile_next_blk(oldphys)");
571 bp = bp->link;
572 } /* end while( ) compress loop */
573 /* Allocate a physical block for this (last) logical block */
574 newphys =
575 allocateWithReserve(f, sizeof(*newphys), &code,
576 "memfile newphys",
577 "memfile_next_blk: MALLOC 2 for 'newphys' failed\n");
578 if (code < 0)
579 return code;
580 ecode |= code; /* accumulate low-mem warnings */
581 newphys->link = NULL;
582 newphys->data_limit = NULL; /* raw */
583
584 } /* end convert file to compressed */
585 newbp->phys_blk = newphys;
586 f->pdata = newphys->data;
587 f->pdata_end = newphys->data + MEMFILE_DATA_SIZE;
588 } /* end if NOT compressing */
589 /* File IS being compressed */
590 else {
591 int code;
592
593 oldphys = bp->phys_blk; /* save raw phys block ID */
594 /* compresses bp on phys list */
595 if ((code = compress_log_blk(f, bp)) < 0)
596 return code;
597 ecode |= code;
598 newbp =
599 allocateWithReserve(f, sizeof(*newbp), &code, "memfile newbp",
600 "memfile_next_blk: MALLOC 2 for 'newbp' failed\n");
601 if (code < 0)
602 return code;
603 ecode |= code;
604 bp->link = newbp;
605 newbp->link = NULL;
606 newbp->raw_block = NULL;
607 /* Re-use the raw phys block for this new logical blk */
608 newbp->phys_blk = oldphys;
609 f->pdata = oldphys->data;
610 f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
611 f->log_curr_blk = newbp;
612 } /* end else (when we are compressing) */
613
614 return (ecode);
615 }
616
617 int /* returns # of chars actually written */
memfile_fwrite_chars(const void * data,uint len,clist_file_ptr cf)618 memfile_fwrite_chars(const void *data, uint len, clist_file_ptr cf)
619 {
620 const char *str = (const char *)data;
621 MEMFILE *f = (MEMFILE *) cf;
622 uint count = len;
623 int ecode;
624
625 /* check if we are writing to the start of the file. If so, then */
626 /* free the file memory and re-initialize it (frees memory) */
627 if (f->log_curr_pos == 0) {
628 int code;
629
630 memfile_free_mem(f);
631 if ((code = memfile_init_empty(f)) < 0) {
632 f->error_code = code;
633 return 0;
634 }
635 }
636 if (f->log_curr_blk->link != 0) {
637 eprintf(" Write file truncate -- need to free physical blocks.\n");
638 }
639 while (count) {
640 uint move_count = f->pdata_end - f->pdata;
641
642 if (move_count == 0) {
643 if ((ecode = memfile_next_blk(f)) != 0) {
644 f->error_code = ecode;
645 if (ecode < 0)
646 return 0;
647 }
648 } else {
649 if (move_count > count)
650 move_count = count;
651 memmove(f->pdata, str, move_count);
652 f->pdata += move_count;
653 str += move_count;
654 count -= move_count;
655 }
656 }
657 f->log_curr_pos += len;
658 f->log_length = f->log_curr_pos; /* truncate length to here */
659 #ifdef DEBUG
660 tot_raw += len;
661 #endif
662 return (len);
663 }
664
665 /* */
666 /* Internal routine to set the f->pdata and f->pdata_end pointers */
667 /* for the current logical block f->log_curr_blk */
668 /* */
669 /* If data only exists in compressed form, allocate a raw buffer */
670 /* and decompress it. */
671 /* */
672
673 private int
memfile_get_pdata(MEMFILE * f)674 memfile_get_pdata(MEMFILE * f)
675 {
676 int i, num_raw_buffers, status;
677 LOG_MEMFILE_BLK *bp = f->log_curr_blk;
678
679 if (bp->phys_blk->data_limit == NULL) {
680 /* Not compressed, return this data pointer */
681 f->pdata = (bp->phys_blk)->data;
682 i = f->log_curr_pos % MEMFILE_DATA_SIZE; /* pos within block */
683 i = f->log_curr_pos - i; /* base of block */
684 if (i + MEMFILE_DATA_SIZE > f->log_length)
685 f->pdata_end = f->pdata + f->log_length - i;
686 else
687 f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
688 } else {
689 /* data was compressed */
690 if (f->raw_head == NULL) {
691 /* need to allocate the raw buffer pool */
692 num_raw_buffers = GET_NUM_RAW_BUFFERS(f);
693 if (f->reservePhysBlockCount) {
694 /* HACK: allocate reserve block that's been reserved for
695 * decompression. This buffer's block was pre-allocated to make
696 * sure we won't come up short here. Take from chain instead of
697 * allocateWithReserve() since this buf would just be wasted if
698 * allowed to remain preallocated. */
699 f->raw_head = (RAW_BUFFER *)f->reservePhysBlockChain;
700 f->reservePhysBlockChain = f->reservePhysBlockChain->link;
701 --f->reservePhysBlockCount;
702 } else {
703 int code;
704
705 f->raw_head =
706 allocateWithReserve(f, sizeof(*f->raw_head), &code,
707 "memfile raw buffer",
708 "memfile_get_pdata: MALLOC for 'raw_head' failed\n");
709 if (code < 0)
710 return code;
711 }
712 f->raw_head->back = NULL;
713 f->raw_tail = f->raw_head;
714 f->raw_tail->log_blk = NULL;
715 for (i = 0; i < num_raw_buffers; i++) {
716 f->raw_tail->fwd = (RAW_BUFFER *) MALLOC(f, sizeof(RAW_BUFFER),
717 "memfile raw buffer");
718 /* if MALLOC fails, then just stop allocating */
719 if (!f->raw_tail->fwd)
720 break;
721 f->total_space += sizeof(RAW_BUFFER);
722 f->raw_tail->fwd->back = f->raw_tail;
723 f->raw_tail = f->raw_tail->fwd;
724 f->raw_tail->log_blk = NULL;
725 }
726 f->raw_tail->fwd = NULL;
727 num_raw_buffers = i + 1; /* if MALLOC failed, then OK */
728 if_debug1(':', "[:]Number of raw buffers allocated=%d\n",
729 num_raw_buffers);
730 } /* end allocating the raw buffer pool (first time only) */
731 if (bp->raw_block == NULL) {
732 #ifdef DEBUG
733 tot_cache_miss++; /* count every decompress */
734 #endif
735 /* find a raw buffer and decompress */
736 if (f->raw_tail->log_blk != NULL) {
737 /* This block was in use, grab it */
738 #ifdef DEBUG
739 tot_swap_out++;
740 #endif
741 f->raw_tail->log_blk->raw_block = NULL; /* data no longer here */
742 f->raw_tail->log_blk = NULL;
743 }
744 /* Use the last raw block in the chain (the oldest) */
745 f->raw_tail->back->fwd = NULL; /* disconnect from tail */
746 f->raw_tail->fwd = f->raw_head; /* new head */
747 f->raw_head->back = f->raw_tail;
748 f->raw_tail = f->raw_tail->back;
749 f->raw_head = f->raw_head->back;
750 f->raw_head->back = NULL;
751 f->raw_head->log_blk = bp;
752
753 /* Decompress the data into this raw block */
754 /* Initialize the decompressor */
755 if (f->decompress_state->template->reinit != 0)
756 (*f->decompress_state->template->reinit) (f->decompress_state);
757 /* Set pointers and call the decompress routine */
758 f->wt.ptr = (byte *) (f->raw_head->data) - 1;
759 f->wt.limit = f->wt.ptr + MEMFILE_DATA_SIZE;
760 f->rd.ptr = (const byte *)(bp->phys_pdata) - 1;
761 f->rd.limit = (const byte *)bp->phys_blk->data_limit;
762 #ifdef DEBUG
763 decomp_wt_ptr0 = f->wt.ptr;
764 decomp_wt_limit0 = f->wt.limit;
765 decomp_rd_ptr0 = f->rd.ptr;
766 decomp_rd_limit0 = f->rd.limit;
767 #endif
768 status = (*f->decompress_state->template->process)
769 (f->decompress_state, &(f->rd), &(f->wt), true);
770 if (status == 0) { /* More input data needed */
771 /* switch to next block and continue decompress */
772 int back_up = 0; /* adjust pointer backwards */
773
774 if (f->rd.ptr != f->rd.limit) {
775 /* transfer remainder bytes from the previous block */
776 back_up = f->rd.limit - f->rd.ptr;
777 for (i = 0; i < back_up; i++)
778 *(bp->phys_blk->link->data - back_up + i) = *++f->rd.ptr;
779 }
780 f->rd.ptr = (const byte *)bp->phys_blk->link->data - back_up - 1;
781 f->rd.limit = (const byte *)bp->phys_blk->link->data_limit;
782 #ifdef DEBUG
783 decomp_wt_ptr1 = f->wt.ptr;
784 decomp_wt_limit1 = f->wt.limit;
785 decomp_rd_ptr1 = f->rd.ptr;
786 decomp_rd_limit1 = f->rd.limit;
787 #endif
788 status = (*f->decompress_state->template->process)
789 (f->decompress_state, &(f->rd), &(f->wt), true);
790 if (status == 0) {
791 eprintf("Decompression required more than one full block!\n");
792 return_error(gs_error_Fatal);
793 }
794 }
795 bp->raw_block = f->raw_head; /* point to raw block */
796 }
797 /* end if( raw_block == NULL ) meaning need to decompress data */
798 else {
799 /* data exists in the raw data cache, if not raw_head, move it */
800 if (bp->raw_block != f->raw_head) {
801 /* move to raw_head */
802 /* prev.fwd = this.fwd */
803 bp->raw_block->back->fwd = bp->raw_block->fwd;
804 if (bp->raw_block->fwd != NULL)
805 /* next.back = this.back */
806 bp->raw_block->fwd->back = bp->raw_block->back;
807 else
808 f->raw_tail = bp->raw_block->back; /* tail = prev */
809 f->raw_head->back = bp->raw_block; /* head.back = this */
810 bp->raw_block->fwd = f->raw_head; /* this.fwd = orig head */
811 f->raw_head = bp->raw_block; /* head = this */
812 f->raw_head->back = NULL; /* this.back = NULL */
813 #ifdef DEBUG
814 tot_cache_hits++; /* counting here prevents repeats since */
815 /* won't count if already at head */
816 #endif
817 }
818 }
819 f->pdata = bp->raw_block->data;
820 f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
821 /* NOTE: last block is never compressed, so a compressed block */
822 /* is always full size. */
823 } /* end else (when data was compressed) */
824
825 return (0);
826 }
827
828 /* ---------------- Reading ---------------- */
829
830 int
memfile_fread_chars(void * data,uint len,clist_file_ptr cf)831 memfile_fread_chars(void *data, uint len, clist_file_ptr cf)
832 {
833 char *str = (char *)data;
834 MEMFILE *f = (MEMFILE *) cf;
835 uint count = len, num_read, move_count;
836
837 num_read = f->log_length - f->log_curr_pos;
838 if (count > num_read)
839 count = num_read;
840 num_read = count;
841
842 while (count) {
843 f->log_curr_pos++; /* move into next byte */
844 if (f->pdata == f->pdata_end) {
845 f->log_curr_blk = (f->log_curr_blk)->link;
846 memfile_get_pdata(f);
847 }
848 move_count = f->pdata_end - f->pdata;
849 if (move_count > count)
850 move_count = count;
851 f->log_curr_pos += move_count - 1; /* new position */
852 memmove(str, f->pdata, move_count);
853 str += move_count;
854 f->pdata += move_count;
855 count -= move_count;
856 }
857
858 return (num_read);
859 }
860
861 /* ---------------- Position/status ---------------- */
862
863 int
memfile_ferror_code(clist_file_ptr cf)864 memfile_ferror_code(clist_file_ptr cf)
865 {
866 return (((MEMFILE *) cf)->error_code); /* errors stored here */
867 }
868
869 long
memfile_ftell(clist_file_ptr cf)870 memfile_ftell(clist_file_ptr cf)
871 {
872 return (((MEMFILE *) cf)->log_curr_pos);
873 }
874
875 void
memfile_rewind(clist_file_ptr cf,bool discard_data,const char * ignore_fname)876 memfile_rewind(clist_file_ptr cf, bool discard_data, const char *ignore_fname)
877 {
878 MEMFILE *f = (MEMFILE *) cf;
879
880 if (discard_data) {
881 memfile_free_mem(f);
882 /* We have to call memfile_init_empty to preserve invariants. */
883 memfile_init_empty(f);
884 } else {
885 f->log_curr_blk = f->log_head;
886 f->log_curr_pos = 0;
887 memfile_get_pdata(f);
888 }
889 }
890
891 int
memfile_fseek(clist_file_ptr cf,long offset,int mode,const char * ignore_fname)892 memfile_fseek(clist_file_ptr cf, long offset, int mode, const char *ignore_fname)
893 {
894 MEMFILE *f = (MEMFILE *) cf;
895 long i, block_num, new_pos;
896
897 switch (mode) {
898 case SEEK_SET: /* offset from the beginning of the file */
899 new_pos = offset;
900 break;
901
902 case SEEK_CUR: /* offset from the current position in the file */
903 new_pos = offset + f->log_curr_pos;
904 break;
905
906 case SEEK_END: /* offset back from the end of the file */
907 new_pos = f->log_length - offset;
908 break;
909
910 default:
911 return (-1);
912 }
913 if (new_pos < 0 || new_pos > f->log_length)
914 return -1;
915 if ((f->pdata == f->pdata_end) && (f->log_curr_blk->link != NULL)) {
916 /* log_curr_blk is actually one block behind log_curr_pos */
917 f->log_curr_blk = f->log_curr_blk->link;
918 }
919 block_num = new_pos / MEMFILE_DATA_SIZE;
920 i = f->log_curr_pos / MEMFILE_DATA_SIZE;
921 if (block_num < i) { /* if moving backwards, start at beginning */
922 f->log_curr_blk = f->log_head;
923 i = 0;
924 }
925 for (; i < block_num; i++) {
926 f->log_curr_blk = f->log_curr_blk->link;
927 }
928 f->log_curr_pos = new_pos;
929 memfile_get_pdata(f); /* pointers to start of block */
930 f->pdata += new_pos - (block_num * MEMFILE_DATA_SIZE);
931
932 return 0; /* return "normal" status */
933 }
934
935 /* ---------------- Internal routines ---------------- */
936
937 private void
memfile_free_mem(MEMFILE * f)938 memfile_free_mem(MEMFILE * f)
939 {
940 LOG_MEMFILE_BLK *bp, *tmpbp;
941
942 #ifdef DEBUG
943 /* output some diagnostics about the effectiveness */
944 if (tot_raw > 100) {
945 if_debug2(':', "[:]tot_raw=%ld, tot_compressed=%ld\n",
946 tot_raw, tot_compressed);
947 }
948 if (tot_cache_hits != 0) {
949 if_debug3(':', "[:]Cache hits=%ld, cache misses=%ld, swapouts=%ld\n",
950 tot_cache_hits,
951 tot_cache_miss - (f->log_length / MEMFILE_DATA_SIZE),
952 tot_swap_out);
953 }
954 tot_raw = 0;
955 tot_compressed = 0;
956 tot_cache_hits = 0;
957 tot_cache_miss = 0;
958 tot_swap_out = 0;
959 #endif
960
961 /* Free up memory that was allocated for the memfile */
962 bp = f->log_head;
963
964 /******************************************************************
965 * The following was the original algorithm here. This algorithm has a bug:
966 * the second loop references the physical blocks again after they have been
967 * freed.
968 ******************************************************************/
969
970 #if 0 /**************** *************** */
971
972 if (bp != NULL) {
973 /* Free the physical blocks that make up the compressed data */
974 PHYS_MEMFILE_BLK *pphys = (f->log_head)->phys_blk;
975
976 if (pphys->data_limit != NULL) {
977 /* the data was compressed, free the chain of blocks */
978 while (pphys != NULL) {
979 PHYS_MEMFILE_BLK *tmpphys = pphys->link;
980
981 FREE(f, pphys, "memfile_free_mem(pphys)");
982 pphys = tmpphys;
983 }
984 }
985 }
986 /* free the logical blocks */
987 while (bp != NULL) {
988 /* if this logical block was not compressed, free the phys_blk */
989 if (bp->phys_blk->data_limit == NULL) {
990 FREE(f, bp->phys_blk, "memfile_free_mem(phys_blk)");
991 }
992 tmpbp = bp->link;
993 FREE(f, bp, "memfile_free_mem(log_blk)");
994 bp = tmpbp;
995 }
996
997 #else /**************** *************** */
998 # if 1 /**************** *************** */
999
1000 /****************************************************************
1001 * This algorithm is correct (we think).
1002 ****************************************************************/
1003
1004 if (bp != NULL) {
1005 /* Null out phys_blk pointers to compressed data. */
1006 PHYS_MEMFILE_BLK *pphys = bp->phys_blk;
1007
1008 {
1009 for (tmpbp = bp; tmpbp != NULL; tmpbp = tmpbp->link)
1010 if (tmpbp->phys_blk->data_limit != NULL)
1011 tmpbp->phys_blk = 0;
1012 }
1013 /* Free the physical blocks that make up the compressed data */
1014 if (pphys->data_limit != NULL) {
1015 /* the data was compressed, free the chain of blocks */
1016 while (pphys != NULL) {
1017 PHYS_MEMFILE_BLK *tmpphys = pphys->link;
1018
1019 FREE(f, pphys, "memfile_free_mem(pphys)");
1020 pphys = tmpphys;
1021 }
1022 }
1023 }
1024 /* Now free the logical blocks, and any uncompressed physical blocks. */
1025 while (bp != NULL) {
1026 if (bp->phys_blk != NULL) {
1027 FREE(f, bp->phys_blk, "memfile_free_mem(phys_blk)");
1028 }
1029 tmpbp = bp->link;
1030 FREE(f, bp, "memfile_free_mem(log_blk)");
1031 bp = tmpbp;
1032 }
1033
1034 /***********************************************************************
1035 * This algorithm appears to be both simpler and free of the bug that
1036 * occasionally causes the older one to reference freed blocks; but in
1037 * fact it can miss blocks, because the very last compressed logical block
1038 * can have spill into a second physical block, which is not referenced by
1039 * any logical block.
1040 ***********************************************************************/
1041
1042 # else /**************** *************** */
1043
1044 {
1045 PHYS_MEMFILE_BLK *prev_phys = 0;
1046
1047 while (bp != NULL) {
1048 PHYS_MEMFILE_BLK *phys = bp->phys_blk;
1049
1050 if (phys != prev_phys) {
1051 FREE(f, phys, "memfile_free_mem(phys_blk)");
1052 prev_phys = phys;
1053 }
1054 tmpbp = bp->link;
1055 FREE(f, bp, "memfile_free_mem(log_blk)");
1056 bp = tmpbp;
1057 }
1058 }
1059
1060 # endif /**************** *************** */
1061 #endif /**************** *************** */
1062
1063 f->log_head = NULL;
1064
1065 /* Free any internal compressor state. */
1066 if (f->compressor_initialized) {
1067 if (f->decompress_state->template->release != 0)
1068 (*f->decompress_state->template->release) (f->decompress_state);
1069 if (f->compress_state->template->release != 0)
1070 (*f->compress_state->template->release) (f->compress_state);
1071 f->compressor_initialized = false;
1072 }
1073 /* free the raw buffers */
1074 while (f->raw_head != NULL) {
1075 RAW_BUFFER *tmpraw = f->raw_head->fwd;
1076
1077 FREE(f, f->raw_head, "memfile_free_mem(raw)");
1078 f->raw_head = tmpraw;
1079 }
1080 }
1081
1082 private int
memfile_init_empty(MEMFILE * f)1083 memfile_init_empty(MEMFILE * f)
1084 {
1085 PHYS_MEMFILE_BLK *pphys;
1086 LOG_MEMFILE_BLK *plog;
1087
1088 /* Zero out key fields so that allocation failure will be unwindable */
1089 f->phys_curr = NULL; /* flag as file not compressed */
1090 f->log_head = NULL;
1091 f->log_curr_blk = NULL;
1092 f->log_curr_pos = 0;
1093 f->log_length = 0;
1094 f->raw_head = NULL;
1095 f->compressor_initialized = false;
1096 f->total_space = 0;
1097
1098 /* File empty - get a physical mem block (includes the buffer area) */
1099 pphys = MALLOC(f, sizeof(*pphys), "memfile pphys");
1100 if (!pphys) {
1101 eprintf("memfile_init_empty: MALLOC for 'pphys' failed\n");
1102 return_error(gs_error_VMerror);
1103 }
1104 f->total_space += sizeof(*pphys);
1105 pphys->data_limit = NULL; /* raw data for now */
1106
1107 /* Get logical mem block to go with physical one */
1108 plog = (LOG_MEMFILE_BLK *)MALLOC( f, sizeof(*plog), "memfile_init_empty" );
1109 if (plog == NULL) {
1110 FREE(f, pphys, "memfile_init_empty");
1111 eprintf("memfile_init_empty: MALLOC for log_curr_blk failed\n");
1112 return_error(gs_error_VMerror);
1113 }
1114 f->total_space += sizeof(*plog);
1115 f->log_head = f->log_curr_blk = plog;
1116 f->log_curr_blk->link = NULL;
1117 f->log_curr_blk->phys_blk = pphys;
1118 f->log_curr_blk->phys_pdata = NULL;
1119 f->log_curr_blk->raw_block = NULL;
1120
1121 f->pdata = pphys->data;
1122 f->pdata_end = f->pdata + MEMFILE_DATA_SIZE;
1123
1124 f->error_code = 0;
1125
1126 return 0;
1127 }
1128