Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gxclread.c
Line
Count
Source (jump to first uncovered line)
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
/* Command list reading for Ghostscript. */
18
#include "memory_.h"
19
#include "gx.h"
20
#include "gp.h"     /* for gp_fmode_rb */
21
#include "gpcheck.h"
22
#include "gserrors.h"
23
#include "gxdevice.h"
24
#include "gscoord.h"    /* requires gsmatrix.h */
25
#include "gsdevice.h"   /* for gs_deviceinitialmatrix */
26
#include "gxdevmem.h"   /* must precede gxcldev.h */
27
#include "gxcldev.h"
28
#include "gxgetbit.h"
29
#include "gxhttile.h"
30
#include "gdevplnx.h"
31
#include "gdevp14.h"
32
#include "gsmemory.h"
33
#include "gsicc_cache.h"
34
/*
35
 * We really don't like the fact that gdevprn.h is included here, since
36
 * command lists are supposed to be usable for purposes other than printer
37
 * devices; but gdev_prn_color_usage and gdev_create_buf_device are
38
 * currently only applicable to printer devices.
39
 */
40
#include "gdevprn.h"
41
#include "stream.h"
42
#include "strimpl.h"
43
44
/* #define EXTRA_OFFSET_MAP_DEBUGGING */
45
46
/* forward decl */
47
private_st_clist_icctable_entry();
48
private_st_clist_icctable();
49
50
/* ------ Band file reading stream ------ */
51
52
#ifdef DEBUG
53
/* An auxiliary table for mapping clist buffer offsets to cfile offsets. */
54
typedef struct {
55
    uint buffered;
56
    int64_t file_offset;
57
} cbuf_offset_map_elem;
58
#endif
59
60
/*
61
 * To separate banding per se from command list interpretation,
62
 * we make the command list interpreter simply read from a stream.
63
 * When we are actually doing banding, the stream filters the band file
64
 * and only passes through the commands for the current bands (or band
65
 * ranges that include a current band).
66
 */
67
typedef struct stream_band_read_state_s {
68
    stream_state_common;
69
    gx_band_page_info_t page_info;
70
    int band_first, band_last;
71
    uint left;      /* amount of data left in this run */
72
    cmd_block b_this;
73
    gs_memory_t *local_memory;
74
#ifdef DEBUG
75
    bool skip_first;
76
    cbuf_offset_map_elem *offset_map;
77
    int bytes_skipped;
78
    int offset_map_length;
79
    int offset_map_max_length;
80
    int skip_next;
81
#endif
82
} stream_band_read_state;
83
84
static int
85
s_band_read_init(stream_state * st)
86
3.00M
{
87
3.00M
    stream_band_read_state *const ss = (stream_band_read_state *) st;
88
3.00M
    const clist_io_procs_t *io_procs = ss->page_info.io_procs;
89
90
3.00M
    ss->left = 0;
91
3.00M
    ss->b_this.band_min = 0;
92
3.00M
    ss->b_this.band_max = 0;
93
3.00M
    ss->b_this.pos = 0;
94
3.00M
    return io_procs->rewind(ss->page_info.bfile, false, ss->page_info.bfname);
95
3.00M
}
96
97
#ifdef DEBUG
98
static int
99
s_band_read_init_offset_map(gx_device_clist_reader *crdev, stream_state * st)
100
{
101
    stream_band_read_state *const ss = (stream_band_read_state *) st;
102
103
    if (gs_debug_c('L')) {
104
        ss->offset_map_length = 0;
105
        ss->offset_map_max_length = cbuf_size + 1; /* fixme: Wanted a more accurate implementation. */
106
        ss->offset_map = (cbuf_offset_map_elem *)gs_alloc_byte_array(crdev->memory,
107
                    ss->offset_map_max_length, sizeof(*ss->offset_map), "s_band_read_init_offset_map");
108
        if (ss->offset_map == NULL)
109
            return_error(gs_error_VMerror);
110
        ss->offset_map[0].buffered = 0;
111
        ss->bytes_skipped = 0;
112
        crdev->offset_map = ss->offset_map; /* Prevent collecting it as garbage.
113
                                            Debugged with ppmraw -r300 014-09.ps . */
114
    } else {
115
        ss->offset_map_length = 0;
116
        ss->offset_map_max_length = 0;
117
        ss->offset_map = NULL;
118
        ss->bytes_skipped = 0;
119
        crdev->offset_map = NULL;
120
    }
121
    ss->skip_first = true;
122
    ss->skip_next = 0;
123
    return 0;
124
}
125
126
static void
127
s_band_read_dnit_offset_map(gx_device_clist_reader *crdev, stream_state * st)
128
{
129
    if (gs_debug_c('L')) {
130
        stream_band_read_state *const ss = (stream_band_read_state *) st;
131
132
        gs_free_object(crdev->memory, ss->offset_map, "s_band_read_dnit_offset_map");
133
        crdev->offset_map = 0;
134
    }
135
}
136
#endif
137
138
static int
139
s_band_read_process(stream_state * st, stream_cursor_read * ignore_pr,
140
                    stream_cursor_write * pw, bool last)
141
41.8M
{
142
41.8M
    stream_band_read_state *const ss = (stream_band_read_state *) st;
143
41.8M
    register byte *q = pw->ptr;
144
41.8M
    byte *wlimit = pw->limit;
145
41.8M
    clist_file_ptr cfile = ss->page_info.cfile;
146
41.8M
    clist_file_ptr bfile = ss->page_info.bfile;
147
41.8M
    uint left = ss->left;
148
41.8M
    int status = 1;
149
41.8M
    uint count;
150
41.8M
    const clist_io_procs_t *io_procs = ss->page_info.io_procs;
151
41.8M
    int64_t pos;
152
153
    /* left = number of bytes unread in the current command. */
154
    /* count = number of bytes we have room in our buffer for. */
155
375M
    while ((count = wlimit - q) != 0) {
156
336M
        int bmin, bmax;
157
        /* If there is more data to be read in the current command, then pull that in. */
158
336M
        if (left) {
159
185M
            if (count > left)
160
147M
                count = left;
161
#ifdef DEBUG
162
            if (gs_debug_c('L')) {
163
                if (ss->skip_next) {
164
                    /* This buffer fill is NOT going into the normal buffer. */
165
                    ss->skip_next = 0;
166
                    ss->bytes_skipped += count;
167
#ifdef EXTRA_OFFSET_MAP_DEBUGGING
168
                    if (ss->offset_map_length != 1) {
169
                        dmlprintf(ss->local_memory, "offset_map: confused!\n");
170
                        exit(1);
171
                    }
172
#endif
173
                } else {
174
#ifdef EXTRA_OFFSET_MAP_DEBUGGING
175
                    if (ss->offset_map[ss->offset_map_length - 1].buffered + count > cbuf_size*2) {
176
                        dmlprintf2(ss->local_memory, "Invalid update to buffered. %d %d\n",
177
                                   ss->offset_map[ss->offset_map_length - 1].buffered, count);
178
                        exit(1);
179
                    }
180
#endif
181
                    ss->offset_map[ss->offset_map_length - 1].buffered += count;
182
                }
183
            }
184
#endif
185
185M
            io_procs->fread_chars(q + 1, count, cfile);
186
185M
            if (io_procs->ferror_code(cfile) < 0) {
187
0
                status = ERRC;
188
0
                break;
189
0
            }
190
185M
            q += count;
191
185M
            left -= count;
192
185M
            process_interrupts(ss->local_memory);
193
185M
            continue;
194
185M
        }
195
        /* The current command is over. So find the next command in the bfile
196
         * that applies to the current band(s) and read that in. */
197
2.79G
        do {
198
2.79G
            int nread;
199
            /* If we hit eof, end! */
200
            /* Could this test be moved into the nread < sizeof() test below? */
201
2.79G
            if (ss->b_this.band_min == cmd_band_end &&
202
2.79G
                io_procs->ftell(bfile) == ss->page_info.bfile_end_pos) {
203
2.99M
                pw->ptr = q;
204
2.99M
                ss->left = left;
205
2.99M
                return EOFC;
206
2.99M
            }
207
208
            /* Read the next cmd_block from the bfile. Each cmd_block contains
209
             * the bands to use, and the file position of the END of the data.
210
             * We therefore want to read the data from the file position given
211
             * in the PREVIOUS record onwards, and compare to the band min/max
212
             * given there too. */
213
2.79G
            bmin = ss->b_this.band_min;
214
2.79G
            bmax = ss->b_this.band_max;
215
2.79G
            pos = ss->b_this.pos; /* Record where our data starts! */
216
2.79G
            nread = io_procs->fread_chars(&ss->b_this, sizeof(ss->b_this), bfile);
217
2.79G
            if (nread < sizeof(ss->b_this)) {
218
0
                DISCARD(gs_note_error(gs_error_unregistered)); /* Must not happen. */
219
0
                return ERRC;
220
0
            }
221
2.79G
        } while (ss->band_last < bmin || ss->band_first > bmax);
222
        /* So let's set up to read the actual command data from cfile. Seek... */
223
147M
        io_procs->fseek(cfile, pos, SEEK_SET, ss->page_info.cfname);
224
147M
        left = (uint) (ss->b_this.pos - pos);
225
#ifdef DEBUG
226
        if (left > 0  && gs_debug_c('L')) {
227
            if (ss->offset_map_length >= ss->offset_map_max_length) {
228
                DISCARD(gs_note_error(gs_error_unregistered)); /* Must not happen. */
229
                return ERRC;
230
            }
231
            ss->offset_map[ss->offset_map_length].file_offset = pos;
232
            ss->offset_map[ss->offset_map_length].buffered = 0;
233
            ss->offset_map_length++;
234
        }
235
#endif
236
147M
        if_debug5m('l', ss->local_memory,
237
147M
                   "[l]reading for bands (%d,%d) at bfile %"PRId64", cfile %"PRId64", length %u\n",
238
147M
                   bmin, bmax,
239
147M
                   (io_procs->ftell(bfile) - sizeof(ss->b_this)), (int64_t)pos, left);
240
147M
    }
241
38.8M
    pw->ptr = q;
242
38.8M
    ss->left = left;
243
38.8M
    return status;
244
41.8M
}
245
246
/* Stream template */
247
static const stream_template s_band_read_template = {
248
    &st_stream_state, s_band_read_init, s_band_read_process, 1, cbuf_size
249
};
250
251
#ifdef DEBUG
252
/* In DEBUG builds, we maintain an "offset_map" within stream_band_read_state,
253
 * that allows us to relate offsets within the buffer, to offsets within the
254
 * cfile.
255
 *
256
 * At any given point, for stream_band_read_state *ss:
257
 *    There are n = ss->offset_map_length records in the table.
258
 *    offset = 0;
259
 *    for (i = 0; i < n; i++)
260
 *       // Offset 'offset' in the buffer corresponds to ss->offset_map[i].file_offset in the file.
261
 *       offset += ss->offset_map[i].buffered
262
 *
263
 * As we pull data from the stream, we keep file_offset and buffered up to date. Note that
264
 * there are 2 cbuf_size sized buffers in play here. The cmd_buffer has one cbuf_size sized
265
 * buffer in it. Data is pulled into that from the stream, which has another cbuf_sized
266
 * buffer into it. Accordingly, 'buffered' should never be > 2*cbuf_size = 8192.
267
 *
268
 * Sometimes we will pull data out of the stream, bypassing the cmd_buffer's buffer. In this
269
 * case, we 'skip' data, and record the number of bytes skipped in ss->bytes_skipped. This
270
 * should only ever happen when we have already advanced as much as possible (i.e. when the
271
 * current offset is in the first record).
272
 */
273
274
/* Given buffer_offset (an offset within the buffer), return the number of the offset_map
275
 * record that contains it. Also fill poffset0 in with the offset of the start of that
276
 * record within the buffer. (NOTE, depending on how much of the record has already been
277
 * read, some bytes may already have been lost). */
278
static int
279
buffer_segment_index(const stream_band_read_state *ss, uint buffer_offset, uint *poffset0)
280
{
281
    uint i, offset0, offset = 0;
282
283
#ifdef EXTRA_OFFSET_MAP_DEBUGGING
284
    dmlprintf1(ss->local_memory, "buffer_segment_index: buffer_offset=%d\n", buffer_offset);
285
    for (i = 0; i < ss->offset_map_length; i++) {
286
        dmlprintf3(ss->local_memory, " offset_map[%d].file_offset=%"PRId64" buffered=%d\n", i, ss->offset_map[i].file_offset, ss->offset_map[i].buffered);
287
    }
288
#endif
289
    for (i = 0; i < ss->offset_map_length; i++) {
290
        offset0 = offset;
291
        offset += ss->offset_map[i].buffered;
292
        if (buffer_offset < offset) {
293
            *poffset0 = offset0;
294
            return i;
295
        }
296
    }
297
    /* Now cope with the case where we've read exactly to the end of the buffer.
298
    * There might be more data still to come. */
299
    if (buffer_offset == offset) {
300
      *poffset0 = offset0;
301
      return i-1;
302
    }
303
#ifdef EXTRA_OFFSET_MAP_DEBUGGING
304
    dmlprintf1(ss->local_memory, "buffer_segment_index fail: buffer_offset=%d not found\n", buffer_offset);
305
    exit(1);
306
#else
307
    (void)gs_note_error(gs_error_unregistered); /* Must not happen. */
308
#endif
309
    return -1;
310
}
311
312
/* Map from a buffer offset, to the offset of the corresponding byte in the
313
 * cfile. */
314
int64_t
315
clist_file_offset(const stream_state * st, uint buffer_offset)
316
{
317
    const stream_band_read_state *ss = (const stream_band_read_state *) st;
318
    uint offset0;
319
    int i = buffer_segment_index(ss, buffer_offset, &offset0);
320
321
    if (i < 0)
322
        return 0;
323
324
    return ss->offset_map[i].file_offset + (uint)(buffer_offset - offset0);
325
}
326
327
void
328
top_up_offset_map(stream_state * st, const byte *buf, const byte *ptr, const byte *end)
329
{
330
    /* NOTE: The clist data are buffered in the clist reader buffer and in the
331
       internal buffer of the clist stream. Since the 1st buffer is not accessible
332
       from s_band_read_process, offset_map corresponds the union of the 2 buffers.
333
     */
334
    stream_band_read_state *const ss = (stream_band_read_state *) st;
335
    uint buffer_offset, offset0, consumed;
336
    int i;
337
338
#ifdef EXTRA_OFFSET_MAP_DEBUGGING
339
    if (ptr < buf || end < ptr || end < buf || end > buf + cbuf_size)
340
    {
341
        dmlprintf3(ss->local_memory, "Invalid pointers for top_up_offset_map: buf=%p ptr=%p end=%p\n", buf, ptr, end);
342
    }
343
#endif
344
345
    if (!gs_debug_c('L'))
346
        return;
347
    if (ss->skip_first) {
348
        /* Work around the trick with initializing the buffer pointer with the buffer end. */
349
        ss->skip_first = false;
350
        return;
351
    }
352
    if (ptr == buf)
353
        return;
354
355
    /* We know that buf <= ptr <= end <= buf+4096, so uint is quite enough! */
356
    buffer_offset = ptr - buf;
357
    i = buffer_segment_index(ss, buffer_offset, &offset0);
358
359
    consumed = buffer_offset - offset0;
360
#ifdef EXTRA_OFFSET_MAP_DEBUGGING
361
    dmlprintf3(ss->local_memory, "offset_map: dump %d entries + %d bytes + %d skipped bytes\n", i, consumed, ss->bytes_skipped);
362
    if (ss->offset_map[i].buffered < consumed) {
363
        dmlprintf2(ss->local_memory, "Invalid update to buffered. B %d %d\n", ss->offset_map[i].buffered, consumed);
364
        exit(1);
365
    }
366
#endif
367
    ss->offset_map[i].buffered -= consumed;
368
    ss->offset_map[i].file_offset += consumed;
369
    ss->bytes_skipped = 0;
370
    if (i) {
371
        memmove(ss->offset_map, ss->offset_map + i,
372
                (ss->offset_map_length - i) * sizeof(*ss->offset_map));
373
        ss->offset_map_length -= i;
374
    }
375
}
376
377
/* This function is called when data is copied from the stream out into a separate
378
 * buffer without going through the usual clist buffers. Essentially data for the
379
 * id we are reading at buffer_offset within the buffer is skipped. */
380
void adjust_offset_map_for_skipped_data(stream_state *st, uint buffer_offset, uint skipped)
381
{
382
    uint offset0;
383
    stream_band_read_state *const ss = (stream_band_read_state *) st;
384
    int i;
385
386
    if (!gs_debug_c('L'))
387
        return;
388
389
    i = buffer_segment_index(ss, buffer_offset, &offset0);
390
391
    ss->offset_map[i].buffered -= skipped;
392
    ss->offset_map[i].file_offset += skipped;
393
}
394
395
void
396
offset_map_next_data_out_of_band(stream_state *st)
397
{
398
    stream_band_read_state *const ss = (stream_band_read_state *) st;
399
400
    if (!gs_debug_c('L'))
401
        return;
402
403
    ss->skip_next = 1;
404
}
405
#endif /* DEBUG */
406
407
/* ------ Reading/rendering ------ */
408
409
/* Calculate the raster for a chunky or planar device. */
410
static int
411
clist_plane_raster(const gx_device *dev, const gx_render_plane_t *render_plane)
412
160M
{
413
160M
    return gx_device_raster_plane(dev, render_plane);
414
160M
}
415
416
/* Select full-pixel rendering if required for RasterOp. */
417
void
418
clist_select_render_plane(gx_device *dev, int y, int height,
419
                          gx_render_plane_t *render_plane, int index)
420
160M
{
421
160M
    if (index >= 0) {
422
0
        gx_color_usage_t color_usage;
423
0
        int ignore_start;
424
425
0
        gdev_prn_color_usage(dev, y, height, &color_usage,  &ignore_start);
426
0
        if (color_usage.slow_rop)
427
0
            index = -1;
428
0
    }
429
160M
    if (index < 0)
430
160M
        render_plane->index = index;
431
0
    else
432
0
        gx_render_plane_init(render_plane, dev, index);
433
160M
}
434
435
/*
436
 * Do device setup from params stored in command list. This is only for
437
 * async rendering & assumes that the first command in every command list
438
 * is a put_params command which sets all space-related parameters to the
439
 * value they will have for the duration of that command list.
440
 */
441
int
442
clist_setup_params(gx_device *dev)
443
0
{
444
0
    gx_device_clist *cldev = (gx_device_clist *)dev;
445
0
    gx_device_clist_reader * const crdev = &cldev->reader;
446
0
    int code = clist_render_init(cldev);
447
448
0
    if (code < 0)
449
0
        return code;
450
451
0
    code = clist_playback_file_bands(playback_action_setup,
452
0
                                     crdev, &crdev->page_info, 0, 0, 0, 0, 0);
453
454
    /* put_params may have reinitialized device into a writer */
455
0
    clist_render_init(cldev);
456
457
0
    return code;
458
0
}
459
460
int
461
clist_close_writer_and_init_reader(gx_device_clist *cldev)
462
160M
{
463
160M
    gx_device_clist_reader * const crdev = &cldev->reader;
464
160M
    gs_memory_t *base_mem = crdev->memory->thread_safe_memory;
465
160M
    gs_memory_status_t mem_status;
466
160M
    int code = 0;
467
468
    /* Initialize for rendering if we haven't done so yet. */
469
160M
    if (crdev->ymin < 0) {
470
777k
        code = clist_end_page(&cldev->writer);
471
777k
        if (code < 0)
472
0
            return code;
473
777k
        code = clist_render_init(cldev);
474
777k
        if (code < 0)
475
0
            return code;
476
        /* allocate and load the color_usage_array */
477
777k
        code = clist_read_color_usage_array(crdev);
478
777k
        if (code < 0)
479
0
            return code;
480
        /* Check for and get ICC profile table */
481
777k
        code = clist_read_icctable(crdev);
482
777k
        if (code < 0)
483
0
            return code;
484
        /* Allocate the icc cache for the clist reader */
485
        /* Since we may be rendering in multiple threads, make sure the memory */
486
        /* is thread safe by using a known thread_safe memory allocator */
487
777k
        gs_memory_status(base_mem, &mem_status);
488
777k
        if (mem_status.is_thread_safe == false) {
489
0
            return_error(gs_error_VMerror);
490
0
        }
491
492
777k
        if (crdev->icc_cache_cl == NULL) {
493
237k
            code = (crdev->icc_cache_cl = gsicc_cache_new(base_mem)) == NULL ? gs_error_VMerror : code;
494
237k
        }
495
777k
    }
496
497
160M
    check_device_compatible_encoding((gx_device *)cldev);
498
499
160M
    return code;
500
160M
}
501
502
/* Used to find the command block information in the bfile
503
   that is related to extra information stored in a psuedo band.
504
   Currently application of this is storage of the ICC profile
505
   table, the per-band color_usage array, and the spot equivalent
506
   colors when doing overprint simulation.  We may eventually
507
   use this for storing other information like compressed images.   */
508
509
static int
510
clist_find_pseudoband(gx_device_clist_reader *crdev, int band, cmd_block *cb)
511
1.59M
{
512
513
1.59M
    gx_band_page_info_t *page_info = &(crdev->page_info);
514
1.59M
    clist_file_ptr bfile = page_info->bfile;
515
1.59M
    int64_t save_pos = page_info->bfile_end_pos;
516
1.59M
    int64_t start_pos;
517
1.59M
    int code;
518
519
1.59M
    if (bfile == NULL) {
520
        /* files haven't been opened yet. Do it now */
521
0
        char fmode[4];
522
523
0
        strcpy(fmode, "r");
524
0
        strncat(fmode, gp_fmode_binary_suffix, 1);
525
0
        if ((code=page_info->io_procs->fopen(page_info->cfname, fmode,
526
0
                      &page_info->cfile,
527
0
                      crdev->memory, crdev->memory, true)) < 0 ||
528
0
                      (code=page_info->io_procs->fopen(page_info->bfname, fmode,
529
0
                      &page_info->bfile,
530
0
                      crdev->memory, crdev->memory, false)) < 0) {
531
0
            return code;
532
0
        }
533
0
        bfile = page_info->bfile;
534
0
    }
535
    /* Go to the start of the last command block */
536
1.59M
    start_pos = page_info->bfile_end_pos - sizeof(cmd_block);
537
1.59M
    page_info->io_procs->fseek(bfile, start_pos, SEEK_SET, page_info->bfname);
538
34.5M
    while( 1 ) {
539
34.5M
        int read = page_info->io_procs->fread_chars(cb, sizeof(cmd_block), bfile);
540
541
34.5M
        if (read < sizeof(cmd_block))
542
0
      return -1;
543
34.5M
        if (cb->band_max == band && cb->band_min == band) {
544
793k
            page_info->io_procs->fseek(bfile, save_pos, SEEK_SET, page_info->bfname);
545
793k
            return(0);  /* Found it */
546
793k
        }
547
33.7M
        start_pos -= sizeof(cmd_block);
548
33.7M
        if (start_pos < 0) {
549
797k
           page_info->io_procs->fseek(bfile, save_pos, SEEK_SET, page_info->bfname);
550
797k
           return(-1);  /* Did not find it before getting into other stuff in normal bands */
551
797k
        }
552
32.9M
        page_info->io_procs->fseek(bfile, start_pos, SEEK_SET, page_info->bfname);
553
32.9M
    }
554
1.59M
}
555
556
/* A procedure to read a chunk of data from the cfile at a particular location into buff */
557
int
558
clist_read_chunk(gx_device_clist_reader *crdev, int64_t position, int size, unsigned char *buf)
559
2.31M
{
560
2.31M
    clist_file_ptr cfile = crdev->page_info.cfile;
561
2.31M
    int64_t save_pos;
562
563
    /* Save our current location */
564
2.31M
    save_pos = crdev->page_info.io_procs->ftell(cfile);
565
    /* Go to our new position */
566
2.31M
    crdev->page_info.io_procs->fseek(cfile, position, SEEK_SET, crdev->page_info.cfname);
567
    /* Get the data */
568
2.31M
    crdev->page_info.io_procs->fread_chars(buf, size, cfile);
569
    /* Restore our position */
570
2.31M
    crdev->page_info.io_procs->fseek(cfile, save_pos, SEEK_SET, crdev->page_info.cfname);
571
2.31M
    return 0;
572
2.31M
}
573
574
/* read the color_usage_array back from the pseudo band */
575
int
576
clist_read_color_usage_array(gx_device_clist_reader *crdev)
577
777k
{
578
777k
    int code, size_data = crdev->nbands * sizeof(gx_color_usage_t );
579
777k
    cmd_block cb;
580
581
777k
    if (crdev->color_usage_array != NULL)
582
0
        gs_free_object(crdev->memory, crdev->color_usage_array,
583
777k
                       "clist reader color_usage_array");
584
777k
    crdev->color_usage_array = (gx_color_usage_t *)gs_alloc_bytes(crdev->memory, size_data,
585
777k
                       "clist reader color_usage_array");
586
777k
    if (crdev->color_usage_array == NULL)
587
0
        return_error(gs_error_VMerror);
588
589
777k
    code = clist_find_pseudoband(crdev, crdev->nbands + COLOR_USAGE_OFFSET - 1, &cb);
590
777k
    if (code < 0)
591
0
        return code;
592
593
777k
    code = clist_read_chunk(crdev, cb.pos, size_data, (unsigned char *)crdev->color_usage_array);
594
777k
    return code;
595
777k
}
596
597
/* read the cmyk equivalent spot colors */
598
int
599
clist_read_op_equiv_cmyk_colors(gx_device_clist_reader *crdev,
600
    equivalent_cmyk_color_params *op_equiv_cmyk_colors)
601
0
{
602
0
    int code;
603
0
    cmd_block cb;
604
605
0
    code = clist_find_pseudoband(crdev, crdev->nbands + SPOT_EQUIV_COLORS - 1, &cb);
606
0
    if (code < 0)
607
0
        return code;
608
609
0
    code = clist_read_chunk(crdev, cb.pos, sizeof(equivalent_cmyk_color_params),
610
0
        (unsigned char *)op_equiv_cmyk_colors);
611
0
    return code;
612
0
}
613
614
/* Unserialize the icc table information stored in the cfile and
615
   place it in the reader device */
616
static int
617
clist_unserialize_icctable(gx_device_clist_reader *crdev, cmd_block *cb)
618
15.5k
{
619
15.5k
    clist_file_ptr cfile = crdev->page_info.cfile;
620
15.5k
    clist_icctable_t *icc_table = crdev->icc_table;
621
15.5k
    int64_t save_pos;
622
15.5k
    int number_entries, size_data;
623
15.5k
    unsigned char *buf, *buf_start;
624
15.5k
    clist_icctable_entry_t *curr_entry;
625
15.5k
    int k;
626
15.5k
    gs_memory_t *stable_mem = crdev->memory->stable_memory;
627
628
15.5k
    if ( icc_table != NULL )
629
0
        return(0);
630
15.5k
    save_pos = crdev->page_info.io_procs->ftell(cfile);
631
15.5k
    crdev->page_info.io_procs->fseek(cfile, cb->pos, SEEK_SET, crdev->page_info.cfname);
632
    /* First four bytes tell us the number of entries. */
633
15.5k
    crdev->page_info.io_procs->fread_chars(&number_entries, sizeof(number_entries), cfile);
634
    /* Allocate the space */
635
15.5k
    size_data = number_entries*sizeof(clist_icc_serial_entry_t);
636
15.5k
    buf = gs_alloc_bytes(crdev->memory, size_data, "clist_read_icctable");
637
15.5k
    buf_start = buf;
638
15.5k
    if (buf == NULL)
639
0
        return gs_rethrow(-1, "insufficient memory for icc table buffer reader");
640
    /* Get the data */
641
15.5k
    clist_read_chunk(crdev, cb->pos + 4, size_data, buf);
642
15.5k
    icc_table = gs_alloc_struct(stable_mem, clist_icctable_t,
643
15.5k
                                &st_clist_icctable, "clist_read_icctable");
644
15.5k
    if (icc_table == NULL) {
645
0
        gs_free_object(stable_mem, buf_start, "clist_read_icctable");
646
0
        return gs_rethrow(-1, "insufficient memory for icc table buffer reader");
647
0
    }
648
15.5k
    icc_table->memory = stable_mem;
649
15.5k
    icc_table->head = NULL;
650
15.5k
    icc_table->final = NULL;
651
   /* Allocate and fill each entry */
652
15.5k
    icc_table->tablesize = number_entries;
653
15.5k
    crdev->icc_table = icc_table;
654
35.5k
    for (k = 0; k < number_entries; k++) {
655
20.0k
        curr_entry = gs_alloc_struct(stable_mem, clist_icctable_entry_t,
656
20.0k
                &st_clist_icctable_entry, "clist_read_icctable");
657
20.0k
        if (curr_entry == NULL) {
658
0
            gs_free_object(stable_mem, buf_start, "clist_read_icctable");
659
0
            return gs_rethrow(-1, "insufficient memory for icc table entry");
660
0
        }
661
20.0k
        memcpy(&(curr_entry->serial_data), buf, sizeof(clist_icc_serial_entry_t));
662
20.0k
        buf += sizeof(clist_icc_serial_entry_t);
663
20.0k
        curr_entry->icc_profile = NULL;
664
20.0k
        if ( icc_table->head == NULL ) {
665
15.5k
            icc_table->head = curr_entry;
666
15.5k
            icc_table->final = curr_entry;
667
15.5k
        } else {
668
4.47k
            icc_table->final->next = curr_entry;
669
4.47k
            icc_table->final = curr_entry;
670
4.47k
        }
671
20.0k
        curr_entry->next = NULL;
672
20.0k
    }
673
15.5k
    gs_free_object(crdev->memory, buf_start, "clist_read_icctable");
674
15.5k
    crdev->page_info.io_procs->fseek(cfile, save_pos, SEEK_SET, crdev->page_info.cfname);
675
15.5k
    return 0;
676
15.5k
}
677
678
/* Get the ICC profile table information from the clist */
679
int
680
clist_read_icctable(gx_device_clist_reader *crdev)
681
812k
{
682
    /* Look for the command block of the ICC Profile. */
683
812k
    cmd_block cb;
684
812k
    int code;
685
686
    /* First get the command block which will tell us where the
687
       information is stored in the cfile */
688
812k
    code = clist_find_pseudoband(crdev, crdev->nbands + ICC_TABLE_OFFSET - 1, &cb);
689
812k
    if (code < 0)
690
797k
        return(0);   /* No ICC information */
691
    /* Unserialize the icc_table from the cfile */
692
15.5k
    code = clist_unserialize_icctable(crdev, &cb);
693
15.5k
    return(code);
694
812k
}
695
696
/* Initialize for reading. */
697
int
698
clist_render_init(gx_device_clist *dev)
699
812k
{
700
812k
    gx_device_clist_reader * const crdev = &dev->reader;
701
702
812k
    crdev->ymin = crdev->ymax = 0;
703
812k
    crdev->yplane.index = -1;
704
    /* For normal rasterizing, pages and num_pages is 1. */
705
812k
    crdev->pages = 0;
706
812k
    crdev->num_pages = 1;   /* always at least one page */
707
812k
    crdev->offset_map = NULL;
708
812k
    crdev->icc_table = NULL;
709
812k
    crdev->color_usage_array = NULL;
710
812k
    crdev->render_threads = NULL;
711
712
812k
    return 0;
713
812k
}
714
715
/* Copy a rasterized rectangle to the client, rasterizing if needed. */
716
int
717
clist_get_bits_rectangle(gx_device *dev, const gs_int_rect * prect,
718
                         gs_get_bits_params_t *params)
719
160M
{
720
160M
    gx_device_clist *cldev = (gx_device_clist *)dev;
721
160M
    gx_device_clist_reader *crdev = &cldev->reader;
722
160M
    gx_device_clist_common *cdev = (gx_device_clist_common *)dev;
723
160M
    gs_get_bits_options_t options = params->options;
724
160M
    int y = prect->p.y;
725
160M
    int end_y = prect->q.y;
726
160M
    int line_count = end_y - y;
727
160M
    gs_int_rect band_rect;
728
160M
    int lines_rasterized;
729
160M
    gx_device *bdev;
730
160M
    uint num_planes =
731
160M
        (options & GB_PACKING_CHUNKY ? 1 :
732
160M
         options & GB_PACKING_PLANAR ? dev->color_info.num_components :
733
7.39M
         options & GB_PACKING_BIT_PLANAR ? dev->color_info.depth :
734
0
         0 /****** NOT POSSIBLE ******/);
735
160M
    gx_render_plane_t render_plane;
736
160M
    int plane_index;
737
160M
    int my;
738
160M
    int code;
739
740
160M
    if (prect->p.x < 0 || prect->q.x > dev->width ||
741
160M
        y < 0 || end_y > dev->height
742
160M
        )
743
0
        return_error(gs_error_rangecheck);
744
160M
    if (line_count <= 0 || prect->p.x >= prect->q.x)
745
8.06k
        return 0;
746
747
    /*
748
     * Calculate the render_plane from the params.  There are two cases:
749
     * full pixels, or a single plane.
750
     */
751
160M
    plane_index = -1;
752
160M
    if (options & GB_SELECT_PLANES) {
753
        /* Look for the one selected plane. */
754
0
        int i;
755
756
0
        for (i = 0; i < num_planes; ++i)
757
0
            if (params->data[i]) {
758
0
                if (plane_index >= 0)  /* >1 plane requested */
759
0
                    return gx_default_get_bits_rectangle(dev, prect, params);
760
0
                plane_index = i;
761
0
            }
762
0
    }
763
764
160M
    if (0 > (code = clist_close_writer_and_init_reader(cldev)))
765
0
        return code;
766
767
160M
    clist_select_render_plane(dev, y, line_count, &render_plane, plane_index);
768
160M
    code = gdev_create_buf_device(cdev->buf_procs.create_buf_device,
769
160M
                                  &bdev, cdev->target, y, &render_plane,
770
160M
                                  dev->memory,
771
160M
                                  &(crdev->color_usage_array[y/crdev->page_info.band_params.BandHeight]));
772
160M
    if (code < 0)
773
0
        return code;
774
160M
    code = clist_rasterize_lines(dev, y, line_count, bdev, &render_plane, &my);
775
160M
    if (code >= 0) {
776
160M
        lines_rasterized = min(code, line_count);
777
        /* Return as much of the rectangle as falls within the rasterized lines. */
778
160M
        band_rect = *prect;
779
160M
        band_rect.p.y = my;
780
160M
        band_rect.q.y = my + lines_rasterized;
781
160M
        code = dev_proc(bdev, get_bits_rectangle)
782
160M
            (bdev, &band_rect, params);
783
160M
    }
784
160M
    cdev->buf_procs.destroy_buf_device(bdev);
785
160M
    if (code < 0 || lines_rasterized == line_count)
786
160M
        return code;
787
    /*
788
     * We'll have to return the rectangle in pieces.  Force GB_RETURN_COPY
789
     * rather than GB_RETURN_POINTER, and require all subsequent pieces to
790
     * use the same values as the first piece for all of the other format
791
     * options.  If copying isn't allowed, or if there are any unread
792
     * rectangles, punt.
793
     */
794
0
    if (!(options & GB_RETURN_COPY) || code > 0)
795
0
        return gx_default_get_bits_rectangle(dev, prect, params);
796
0
    options = params->options;
797
0
    if (!(options & GB_RETURN_COPY)) {
798
        /* Redo the first piece with copying. */
799
0
        params->options = options =
800
0
            (params->options & ~GB_RETURN_ALL) | GB_RETURN_COPY;
801
0
        lines_rasterized = 0;
802
0
    }
803
0
    {
804
0
        gs_get_bits_params_t band_params;
805
0
        uint raster = gx_device_raster(bdev, true);
806
807
0
        code = gdev_create_buf_device(cdev->buf_procs.create_buf_device,
808
0
                                      &bdev, cdev->target, y, &render_plane,
809
0
                                      dev->memory,
810
0
                                      &(crdev->color_usage_array[y/crdev->page_info.band_params.BandHeight]));
811
0
        if (code < 0)
812
0
            return code;
813
0
        band_params = *params;
814
0
        while ((y += lines_rasterized) < end_y) {
815
0
            int i;
816
817
            /* Increment data pointers by lines_rasterized. */
818
0
            for (i = 0; i < num_planes; ++i)
819
0
                if (band_params.data[i])
820
0
                    band_params.data[i] += raster * lines_rasterized;
821
0
            line_count = end_y - y;
822
0
            code = clist_rasterize_lines(dev, y, line_count, bdev,
823
0
                                         &render_plane, &my);
824
0
            if (code < 0)
825
0
                break;
826
0
            lines_rasterized = min(code, line_count);
827
0
            band_rect.p.y = my;
828
0
            band_rect.q.y = my + lines_rasterized;
829
0
            code = dev_proc(bdev, get_bits_rectangle)
830
0
                (bdev, &band_rect, &band_params);
831
0
            if (code < 0)
832
0
                break;
833
0
            params->options = options = band_params.options;
834
0
            if (lines_rasterized == line_count)
835
0
                break;
836
0
        }
837
0
        cdev->buf_procs.destroy_buf_device(bdev);
838
0
    }
839
0
    return code;
840
0
}
841
842
/* Copy scan lines to the client.  This is where rendering gets done. */
843
/* Processes min(requested # lines, # lines available thru end of band) */
844
int /* returns -ve error code, or # scan lines copied */
845
clist_rasterize_lines(gx_device *dev, int y, int line_count,
846
                      gx_device *bdev, const gx_render_plane_t *render_plane,
847
                      int *pmy)
848
160M
{
849
160M
    gx_device_clist * const cldev = (gx_device_clist *)dev;
850
160M
    gx_device_clist_reader * const crdev = &cldev->reader;
851
160M
    gx_device *target = crdev->target;
852
160M
    uint raster = clist_plane_raster(target, render_plane);
853
160M
    byte *mdata = crdev->data + crdev->page_info.tile_cache_size;
854
160M
    byte *mlines = (crdev->page_info.line_ptrs_offset == 0 ? NULL : mdata + crdev->page_info.line_ptrs_offset);
855
160M
    int plane_index = (render_plane ? render_plane->index : -1);
856
160M
    int code;
857
858
    /* Render a band if necessary, and copy it incrementally. */
859
160M
    if (crdev->ymin < 0 || crdev->yplane.index != plane_index ||
860
160M
        !(y >= crdev->ymin && y < crdev->ymax)
861
160M
        ) {
862
2.96M
        int band_height = crdev->page_info.band_params.BandHeight;
863
2.96M
        int band = y / band_height;
864
2.96M
        int band_begin_line = band * band_height;
865
2.96M
        int band_end_line = band_begin_line + band_height;
866
2.96M
        int band_num_lines;
867
2.96M
        gs_int_rect band_rect;
868
869
2.96M
        if (band_end_line > dev->height)
870
68.0k
            band_end_line = dev->height;
871
        /* Clip line_count to current band */
872
2.96M
        if (line_count > band_end_line - y)
873
0
            line_count = band_end_line - y;
874
2.96M
        band_num_lines = band_end_line - band_begin_line;
875
876
2.96M
        if (y < 0 || y > dev->height)
877
0
            return_error(gs_error_rangecheck);
878
2.96M
        code = crdev->buf_procs.setup_buf_device
879
2.96M
            (bdev, mdata, raster, (byte **)mlines, 0, band_num_lines, band_num_lines);
880
2.96M
        band_rect.p.x = 0;
881
2.96M
        band_rect.p.y = band_begin_line;
882
2.96M
        band_rect.q.x = dev->width;
883
2.96M
        band_rect.q.y = band_end_line;
884
2.96M
        if (code >= 0)
885
2.96M
            code = clist_render_rectangle(cldev, &band_rect, bdev, render_plane,
886
2.96M
                                          true);
887
        /* Reset the band boundaries now, so that we don't get */
888
        /* an infinite loop. */
889
2.96M
        crdev->ymin = band_begin_line;
890
2.96M
        crdev->ymax = band_end_line;
891
2.96M
        crdev->offset_map = NULL;
892
2.96M
        if (code < 0)
893
11
            return code;
894
2.96M
    }
895
896
160M
    if (line_count > crdev->ymax - y)
897
0
        line_count = crdev->ymax - y;
898
160M
    code = crdev->buf_procs.setup_buf_device
899
160M
        (bdev, mdata, raster, (byte **)mlines, y - crdev->ymin, line_count,
900
160M
         crdev->ymax - crdev->ymin);
901
160M
    if (code < 0)
902
0
        return code;
903
904
160M
    *pmy = 0;
905
160M
    return line_count;
906
160M
}
907
908
/*
909
 * Render a rectangle to a client-supplied device.  There is no necessary
910
 * relationship between band boundaries and the region being rendered.
911
 */
912
int
913
clist_render_rectangle(gx_device_clist *cldev, const gs_int_rect *prect,
914
                       gx_device *bdev,
915
                       const gx_render_plane_t *render_plane, bool clear)
916
2.96M
{
917
2.96M
    gx_device_clist_reader * const crdev = &cldev->reader;
918
2.96M
    const gx_placed_page *ppages;
919
2.96M
    int num_pages = crdev->num_pages;
920
2.96M
    int band_height = crdev->page_info.band_params.BandHeight;
921
2.96M
    int band_first = prect->p.y / band_height;
922
2.96M
    int band_last = (prect->q.y - 1) / band_height;
923
2.96M
    gx_band_page_info_t *pinfo;
924
2.96M
    gx_band_page_info_t page_info;
925
2.96M
    int code = 0;
926
2.96M
    int i;
927
2.96M
    bool save_pageneutralcolor;
928
929
2.96M
    if (render_plane)
930
2.96M
        crdev->yplane = *render_plane;
931
0
    else
932
0
        crdev->yplane.index = -1;
933
2.96M
    if_debug2m('l', bdev->memory, "[l]rendering bands (%d,%d)\n", band_first, band_last);
934
935
2.96M
    ppages = crdev->pages;
936
937
    /* Before playing back the clist, make sure that the gray detection is disabled */
938
    /* so we don't slow down the rendering (primarily high level images).           */
939
2.96M
    save_pageneutralcolor = crdev->icc_struct->pageneutralcolor;
940
2.96M
    crdev->icc_struct->pageneutralcolor = false;
941
942
5.93M
    for (i = 0; i < num_pages && code >= 0; ++i) {
943
2.96M
        bool pdf14_needed = false;
944
2.96M
        int band;
945
946
2.96M
        if (ppages == NULL) {
947
                /*
948
                 * If we aren't rendering saved pages, do the current one.
949
                 * Note that this is the only case in which we may encounter
950
                 * a gx_saved_page with non-zero cfile or bfile.
951
                 */
952
2.96M
                bdev->band_offset_x = 0;
953
2.96M
                bdev->band_offset_y = band_first * (long)band_height;
954
2.96M
                pinfo = &(crdev->page_info);
955
2.96M
        } else {
956
0
            const gx_placed_page *ppage = &ppages[i];
957
958
            /* Store the page information. */
959
0
            page_info.cfile = page_info.bfile = NULL;
960
0
            memcpy(page_info.cfname, ppage->page->cfname, sizeof(page_info.cfname));
961
0
            memcpy(page_info.bfname, ppage->page->bfname, sizeof(page_info.bfname));
962
0
            page_info.io_procs = ppage->page->io_procs;
963
0
            page_info.tile_cache_size = ppage->page->tile_cache_size;
964
0
            page_info.line_ptrs_offset = ppage->page->line_ptrs_offset;
965
0
            page_info.bfile_end_pos = ppage->page->bfile_end_pos;
966
0
            page_info.band_params = ppage->page->band_params;
967
0
            pinfo = &page_info;
968
969
            /*
970
             * Set the band_offset_? values in case the buffer device
971
             * needs this. Example, a device may need to adjust the
972
             * phase of the dithering based on the page position, NOT
973
             * the position within the band buffer to avoid band stitch
974
             * lines in the dither pattern. The old wtsimdi device did this
975
             *
976
             * The band_offset_x is not important for placed pages that
977
             * are nested on a 'master' page (imposition) since each
978
             * page expects to be dithered independently, but setting
979
             * this allows pages to be contiguous without a dithering
980
             * shift.
981
             *
982
             * The following sets the band_offset_? relative to the
983
             * master page.
984
             */
985
0
            bdev->band_offset_x = ppage->offset.x;
986
0
            bdev->band_offset_y = ppage->offset.y + (band_first * band_height);
987
0
        }
988
        /* if any of the requested bands need transparency, use it for all of them   */
989
        /* The pdf14_ok_to_optimize checks if the target device (bdev) is compatible */
990
        /* with the pdf14 compositor info that was written to the clist: colorspace, */
991
        /* colorspace, etc.                                                          */
992
2.96M
        pdf14_needed = !pdf14_ok_to_optimize(bdev);
993
4.64M
        for (band=band_first; !pdf14_needed && band <= band_last; band++)
994
1.67M
            pdf14_needed |= (crdev->color_usage_array[band].trans_bbox.p.y <=
995
1.67M
            crdev->color_usage_array[band].trans_bbox.q.y) ? true : false;
996
997
2.96M
        code = clist_playback_file_bands(pdf14_needed ?
998
2.14M
                                         playback_action_render : playback_action_render_no_pdf14,
999
2.96M
                                         crdev, pinfo,
1000
2.96M
                                         bdev, band_first, band_last,
1001
2.96M
                                         prect->p.x - bdev->band_offset_x,
1002
2.96M
                                         prect->p.y);
1003
2.96M
    }
1004
2.96M
    crdev->icc_struct->pageneutralcolor = save_pageneutralcolor;  /* restore it */
1005
2.96M
    return code;
1006
2.96M
}
1007
1008
/* Playback the band file, taking the indicated action w/ its contents. */
1009
int
1010
clist_playback_file_bands(clist_playback_action action,
1011
                          gx_device_clist_reader *crdev,
1012
                          gx_band_page_info_t *page_info, gx_device *target,
1013
                          int band_first, int band_last, int x0, int y0)
1014
3.00M
{
1015
3.00M
    int code = 0;
1016
3.00M
    bool opened_bfile = false;
1017
3.00M
    bool opened_cfile = false;
1018
1019
    /* We have to pick some allocator for rendering.... */
1020
3.00M
    gs_memory_t *mem =crdev->memory;
1021
1022
3.00M
    stream_band_read_state rs;
1023
1024
    /* setup stream */
1025
3.00M
    s_init_state((stream_state *)&rs, &s_band_read_template,
1026
3.00M
                 (gs_memory_t *)0); /* Not mem, as we don't want to free rs */
1027
3.00M
    rs.band_first = band_first;
1028
3.00M
    rs.band_last = band_last;
1029
3.00M
    rs.page_info = *page_info;
1030
3.00M
    rs.local_memory = mem;
1031
1032
    /* If this is a saved page, open the files. */
1033
3.00M
    if (rs.page_info.cfile == 0) {
1034
0
        code = crdev->page_info.io_procs->fopen(rs.page_info.cfname,
1035
0
                           gp_fmode_rb, &rs.page_info.cfile, crdev->bandlist_memory,
1036
0
                           crdev->bandlist_memory, true);
1037
0
        opened_cfile = (code >= 0);
1038
0
    }
1039
3.00M
    if (rs.page_info.bfile == 0 && code >= 0) {
1040
0
        code = crdev->page_info.io_procs->fopen(rs.page_info.bfname,
1041
0
                           gp_fmode_rb, &rs.page_info.bfile, crdev->bandlist_memory,
1042
0
                           crdev->bandlist_memory, false);
1043
0
        opened_bfile = (code >= 0);
1044
0
    }
1045
3.00M
    if (rs.page_info.cfile != 0 && rs.page_info.bfile != 0) {
1046
3.00M
        stream s;
1047
3.00M
        byte sbuf[cbuf_size];
1048
3.00M
        static const stream_procs no_procs = {
1049
3.00M
            s_std_noavailable, s_std_noseek, s_std_read_reset,
1050
3.00M
            s_std_read_flush, s_std_close, s_band_read_process
1051
3.00M
        };
1052
1053
3.00M
        s_band_read_init((stream_state *)&rs);
1054
# ifdef DEBUG
1055
        s_band_read_init_offset_map(crdev, (stream_state *)&rs);
1056
# endif
1057
          /* The stream doesn't need a memory, but we'll need to access s.memory->gs_lib_ctx. */
1058
3.00M
        s_init(&s, mem);
1059
3.00M
        s_std_init(&s, sbuf, cbuf_size, &no_procs, s_mode_read);
1060
3.00M
        s.foreign = 1;
1061
3.00M
        s.state = (stream_state *)&rs;
1062
1063
3.00M
        code = clist_playback_band(action, crdev, &s, target, x0, y0, mem);
1064
# ifdef DEBUG
1065
        s_band_read_dnit_offset_map(crdev, (stream_state *)&rs);
1066
# endif
1067
3.00M
    }
1068
1069
    /* Close the files if we just opened them. */
1070
3.00M
    if (opened_bfile && rs.page_info.bfile != 0)
1071
0
        crdev->page_info.io_procs->fclose(rs.page_info.bfile, rs.page_info.bfname, false);
1072
3.00M
    if (opened_cfile && rs.page_info.cfile != 0)
1073
0
        crdev->page_info.io_procs->fclose(rs.page_info.cfile, rs.page_info.cfname, false);
1074
1075
3.00M
    return code;
1076
3.00M
}