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 |
}
|