Coverage Report

Created: 2026-07-24 07:44

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