Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/planix-v0/sys/src/cmd/gs/src/gxclmem.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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 */
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
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
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
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 */
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
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 */
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 */
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
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
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
864
memfile_ferror_code(clist_file_ptr cf)
865
{
866
    return (((MEMFILE *) cf)->error_code);	/* errors stored here */
867
}
868
 
869
long
870
memfile_ftell(clist_file_ptr cf)
871
{
872
    return (((MEMFILE *) cf)->log_curr_pos);
873
}
874
 
875
void
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
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
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
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
}