diff options
Diffstat (limited to 'base/stream.h')
-rw-r--r-- | base/stream.h | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/base/stream.h b/base/stream.h new file mode 100644 index 00000000..2d2ad8d2 --- /dev/null +++ b/base/stream.h @@ -0,0 +1,443 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* Definitions for Ghostscript stream package */ +/* Requires stdio.h */ + +#ifndef stream_INCLUDED +# define stream_INCLUDED + +#include "scommon.h" +#include "gxiodev.h" +#include "srdline.h" + +/* See scommon.h for documentation on the design of streams. */ + +/* ------ Stream structure definition ------ */ + +/* + * We expose the stream structure definition to clients so that + * they can get reasonable performance out of the basic operations. + */ + +/* Define the "virtual" stream procedures. */ + +typedef struct { + + /* Store # available for reading. */ + /* Return 0 if OK, ERRC if error or not implemented. */ +#define stream_proc_available(proc)\ + int proc(stream *, gs_offset_t *) + stream_proc_available((*available)); + + /* Set position. */ + /* Return 0 if OK, ERRC if error or not implemented. */ +#define stream_proc_seek(proc)\ + int proc(stream *, gs_offset_t) + stream_proc_seek((*seek)); + + /* Clear buffer and, if relevant, unblock channel. */ + /* Cannot cause an error. */ +#define stream_proc_reset(proc)\ + void proc(stream *) + stream_proc_reset((*reset)); + + /* Flush buffered data to output, or drain input. */ + /* Return 0 if OK, ERRC if error. */ +#define stream_proc_flush(proc)\ + int proc(stream *) + stream_proc_flush((*flush)); + + /* Flush data (if writing) & close stream. */ + /* Return 0 if OK, ERRC if error. */ +#define stream_proc_close(proc)\ + int proc(stream *) + stream_proc_close((*close)); + + /* Process a buffer, updating the cursor pointers. */ + /* See strimpl.h for details. */ + stream_proc_process((*process)); + + /* Switch the stream to read or write mode. */ + /* false = read, true = write. */ + /* If the procedure is 0, switching is not allowed. */ +#define stream_proc_switch_mode(proc)\ + int proc(stream *, bool) + stream_proc_switch_mode((*switch_mode)); + +} stream_procs; + +/* ------ The actual stream structure ------ */ + +struct stream_s { + /* + * To allow the stream itself to serve as the "state" + * of a couple of heavily used types, we start its + * definition with the common stream state. + */ + stream_state_common; + /* + * The following invariants apply at all times for read streams: + * + * s->cbuf - 1 <= s->cursor.r.ptr <= s->cursor.r.limit. + * + * The amount of data in the buffer is s->cursor.r.limit + 1 - s->cbuf. + * + * s->position represents the stream position as of the beginning + * of the buffer, so the current position is s->position + + * (s->cursor.r.ptr + 1 - s->cbuf). + * + * Analogous invariants apply for write streams: + * + * s->cbuf - 1 <= s->cursor.w.ptr <= s->cursor.w.limit. + * + * The amount of data in the buffer is s->cursor.w.ptr + 1 - s->cbuf. + * + * s->position represents the stream position as of the beginning + * of the buffer, so the current position is s->position + + * (s->cursor.w.ptr + 1 - s->cbuf). + */ + stream_cursor cursor; /* cursor for reading/writing data */ + byte *cbuf; /* base of buffer */ + uint bsize; /* size of buffer, 0 if closed */ + uint cbsize; /* size of buffer */ + /* + * end_status indicates what should happen when the client + * reaches the end of the buffer: + * 0 in the normal case; + * EOFC if a read stream has reached EOD or a write + * stream has written the EOD marker; + * ERRC if an error terminated the last read or write + * operation from or to the underlying data source + * or sink; + * INTC if the last transfer was interrupted (NOT + * USED YET); + * CALLC if a callout is required. + */ + short end_status; /* status at end of buffer (when */ + /* reading) or now (when writing) */ + byte foreign; /* true if buffer is outside heap */ + byte modes; /* access modes allowed for this stream */ +#define s_mode_read 1 +#define s_mode_write 2 +#define s_mode_seek 4 +#define s_mode_append 8 /* (s_mode_write also set) */ +#define s_is_valid(s) ((s)->modes != 0) +#define s_is_reading(s) (((s)->modes & s_mode_read) != 0) +#define s_is_writing(s) (((s)->modes & s_mode_write) != 0) +#define s_can_seek(s) (((s)->modes & s_mode_seek) != 0) + gs_string cbuf_string; /* cbuf/cbsize if cbuf is a string, */ + /* 0/? if not */ + gs_offset_t position; /* file position of beginning of */ + /* buffer */ + stream_procs procs; + stream *strm; /* the underlying stream, non-zero */ + /* iff this is a filter stream */ + int is_temp; /* if >0, this is a temporary */ + /* stream and should be freed */ + /* when its source/sink is closed; */ + /* if >1, the buffer is also */ + /* temporary */ + int inline_temp; /* temporary for inline access */ + /* (see spgetc_inline below) */ + stream_state *state; /* state of process */ + /* + * The following are for the use of the interpreter. + * See files.h for more information on read_id and write_id, + * zfile.c for more information on prev and next, + * zfilter.c for more information on close_strm. + */ + ushort read_id; /* "unique" serial # for detecting */ + /* references to closed streams */ + /* and for validating read access */ + ushort write_id; /* ditto to validate write access */ + stream *prev, *next; /* keep track of all files */ + bool close_strm; /* CloseSource/CloseTarget */ + bool close_at_eod; /*(default is true, only false if "reusable")*/ + int (*save_close)(stream *); /* save original close proc */ + /* + * In order to avoid allocating a separate stream_state for + * file streams, which are the most heavily used stream type, + * we put their state here. + */ + gp_file *file; /* file handle for C library */ + gs_const_string file_name; /* file name (optional) -- clients must */ + /* access only through procedures */ + uint file_modes; /* access modes for the file, */ + /* may be a superset of modes */ + /* Clients must only set the following through sread_subfile. */ + gs_offset_t file_offset; /* starting point in file (reading) */ + gs_offset_t file_limit; /* ending point in file (reading) */ +}; + +/* The descriptor is only public for subclassing. */ +extern_st(st_stream); +#define public_st_stream() /* in stream.c */\ + gs_public_st_composite_final(st_stream, stream, "stream",\ + stream_enum_ptrs, stream_reloc_ptrs, stream_finalize) +#define STREAM_NUM_PTRS 6 + +#define S_FILE_LIMIT_MAX (sizeof(gs_offset_t) > 4 ? max_int64_t : max_long) + +/* Initialize the checking IDs of a stream. */ +#define s_init_ids(s) ((s)->read_id = (s)->write_id = 1) +#define s_init_read_id(s) ((s)->read_id = 1, (s)->write_id = 0) +#define s_init_write_id(s) ((s)->read_id = 0, (s)->write_id = 1) +#define s_init_no_id(s) ((s)->read_id = (s)->write_id = 0) + +/* ------ Stream functions ------ */ + +/* Some of these are macros -- beware. */ +/* Note that unlike the C stream library, */ +/* ALL stream procedures take the stream as the first argument. */ +#define sendrp(s) ((s)->cursor.r.ptr >= (s)->cursor.r.limit) /* NOT FOR CLIENTS */ +#define sendwp(s) ((s)->cursor.w.ptr >= (s)->cursor.w.limit) /* NOT FOR CLIENTS */ + +/* + * Following are valid for all streams. + */ +/* flush is NOT a no-op for read streams -- it discards data until EOF. */ +/* close is NOT a no-op for non-file streams -- */ +/* it actively disables them. */ +/* The close routine must do a flush if needed. */ +#define sseekable(s) s_can_seek(s) +int savailable(stream *, gs_offset_t *); + +#define sreset(s) (*(s)->procs.reset)(s) +#define sflush(s) (*(s)->procs.flush)(s) +int sclose(stream *); +int sswitch(stream *, bool); + +/* + * Following are only valid for read streams. + */ +int spgetcc(stream *, bool); /* bool indicates close at EOD */ +#define spgetc(s) spgetcc(s, true) /* a procedure equivalent of sgetc */ +/* + * Note that sgetc must call spgetc one byte early, because filter must read + * ahead to detect EOD. + * + * In the definition of sgetc, the first alternative should read + * (int)(*++((s)->cursor.r.ptr)) + * but the Borland compiler generates truly atrocious code for this. + * The SCO ODT compiler requires the first, pointless cast to int. + */ +#define sgetc(s)\ + ((int)((s)->cursor.r.limit - (s)->cursor.r.ptr > 1 ? (++((s)->cursor.r.ptr), (int)*(s)->cursor.r.ptr) : spgetc(s))) +int sgets(stream *, byte *, uint, uint *); +int sungetc(stream *, byte); /* ERRC on error, 0 if OK */ + +#define sputback(s) ((s)->cursor.r.ptr--) /* can only do this once! */ +#define seofp(s) (sendrp(s) && (s)->end_status == EOFC) +#define serrorp(s) (sendrp(s) && (s)->end_status == ERRC) +int spskip(stream *, gs_offset_t, gs_offset_t *); + +#define sskip(s,nskip,pskipped) spskip(s, (long)(nskip), pskipped) +/* + * Attempt to refill the buffer of a read stream. + * Only call this if the end_status is not EOFC, + * and if the buffer is (nearly) empty. + */ +int s_process_read_buf(stream *); + +/* + * Following are only valid for write streams. + */ +int spputc(stream *, byte); /* a procedure equivalent of sputc */ + +/* + * The first alternative should read + * ((int)(*++((s)->cursor.w.ptr)=(c))) + * but the Borland compiler generates truly atrocious code for this. + */ +#define sputc(s,c)\ + (!sendwp(s) ? (++((s)->cursor.w.ptr), *(s)->cursor.w.ptr=(c), 0) : spputc((s),(c))) +int sputs(stream *, const byte *, uint, uint *); + +/* + * Attempt to empty the buffer of a write stream. + * Only call this if the end_status is not EOFC. + */ +int s_process_write_buf(stream *, bool); + +/* Following are only valid for positionable streams. */ +gs_offset_t stell(stream *); +int spseek(stream *, gs_offset_t); + +#define sseek(s,pos) spseek(s, (gs_offset_t)(pos)) + +/* Following are for high-performance reading clients. */ +/* bufptr points to the next item. */ +#define sbufptr(s) ((s)->cursor.r.ptr + 1) +#define sbufavailable(s) ((s)->cursor.r.limit - (s)->cursor.r.ptr) +#define sbufskip(s, n) ((s)->cursor.r.ptr += (n), 0) +/* + * Define the minimum amount of data that must be left in an input buffer + * after a read operation to handle filter read-ahead, either 0 or 1 + * byte(s). + */ +#define max_min_left 1 +/* + * The stream.state min_left value is the minimum amount of data that must + * be left in an input buffer after a read operation to handle filter + * read-ahead. Once filters reach EOD, return 0 since read-ahead is + * no longer relevant. + */ +#define sbuf_min_left(s) \ + ((s->end_status == EOFC || s->end_status == ERRC ? 0 : s->state->min_left)) + +/* The following are for very high-performance clients of read streams, */ +/* who unpack the stream state into local variables. */ +/* Note that any non-inline operations must do a s_end_inline before, */ +/* and a s_begin_inline after. */ +#define s_declare_inline(s, cp, ep)\ + register const byte *cp;\ + const byte *ep +#define s_begin_inline(s, cp, ep)\ + cp = (s)->cursor.r.ptr, ep = (s)->cursor.r.limit +#define s_end_inline(s, cp, ep)\ + (s)->cursor.r.ptr = cp +#define sbufavailable_inline(s, cp, ep)\ + (ep - cp) +#define sendbufp_inline(s, cp, ep)\ + (cp >= ep) +/* The (int) is needed to pacify the SCO ODT compiler. */ +#define sgetc_inline(s, cp, ep)\ + ((int)(sendbufp_inline(s, cp, ep) ? spgetc_inline(s, cp, ep) : *++cp)) +#define spgetc_inline(s, cp, ep)\ + (s_end_inline(s, cp, ep), (s)->inline_temp = spgetc(s),\ + s_begin_inline(s, cp, ep), (s)->inline_temp) +#define sputback_inline(s, cp, ep)\ + --cp + +/* Allocate a stream or a stream state. */ +stream *s_alloc(gs_memory_t *, client_name_t); +stream_state *s_alloc_state(gs_memory_t *, gs_memory_type_ptr_t, client_name_t); +/* + * Initialize a separately allocated stream or stream state, as if allocated + * by s_alloc[_state]. + */ +void s_init(stream *, gs_memory_t *); +void s_init_state(stream_state *, const stream_template *, gs_memory_t *); + +/* create a stream for a file object */ +int file_prepare_stream(const char *, uint, const char *, + uint, stream **, char[4], gs_memory_t *); + +/* Set up a file stream on a gp_file. */ +int file_init_stream(stream *, gp_file *, const char *, byte *, uint); + +/* Open a file stream, optionally on a gp_file. */ +int file_open_stream(const char *, uint, const char *, + uint, stream **, gx_io_device *, + iodev_proc_fopen_t, gs_memory_t *); + +/* Allocate and return a file stream. */ +stream * file_alloc_stream(gs_memory_t *, client_name_t); + +/* + * Macros for checking file validity. + * NOTE: in order to work around a bug in the Borland 5.0 compiler, + * you must use file_is_invalid rather than !file_is_valid. + */ +#define file_is_valid(svar,op)\ + (svar = fptr(op), (svar->read_id | svar->write_id) == r_size(op)) +#define file_is_invalid(svar,op)\ + (svar = fptr(op), (svar->read_id | svar->write_id) != r_size(op)) +#define check_file(svar,op)\ + BEGIN\ + check_type(*(op), t_file);\ + if ( file_is_invalid(svar, op) ) return_error(gs_error_ioerror);\ + END + +/* Close a file stream. */ +int file_close_file(stream *); + +int file_close_finish(stream *); + +/* Disable further access on the stream by mangling the id's */ +int file_close_disable(stream *); + +/* Create a stream on a string or a file. */ +void sread_string(stream *, const byte *, uint), + sread_string_reusable(stream *, const byte *, uint), + swrite_string(stream *, byte *, uint); +void sread_file(stream *, gp_file *, byte *, uint), + swrite_file(stream *, gp_file *, byte *, uint); +int sappend_file(stream *, gp_file *, byte *, uint); + +/* Confine reading to a subfile. This is primarily for reusable streams. */ +int sread_subfile(stream *s, gs_offset_t start, gs_offset_t length); + +/* Set the file name of a stream, copying the name. */ +/* Return <0 if the copy could not be allocated. */ +int ssetfilename(stream *, const byte *, uint); + +/* Return the file name of a stream, if any. */ +/* There is a guaranteed 0 byte after the string. */ +int sfilename(stream *, gs_const_string *); + +/* Create a stream that tracks the position, */ +/* for calculating how much space to allocate when actually writing. */ +void swrite_position_only(stream *); + +/* Standard stream initialization */ +void s_std_init(stream *, byte *, uint, const stream_procs *, int /*mode */ ); + +/* Standard stream finalization */ +void s_disable(stream *); + +/* Generic stream procedures exported for templates */ +int s_std_null(stream *); +void s_std_read_reset(stream *), s_std_write_reset(stream *); +int s_std_read_flush(stream *), s_std_write_flush(stream *), s_std_noavailable(stream *, gs_offset_t *), + s_std_noseek(stream *, gs_offset_t), s_std_close(stream *), s_std_switch_mode(stream *, bool); + +/* Generic procedures for filters. */ +int s_filter_write_flush(stream *), s_filter_close(stream *); + +/* Generic procedure structures for filters. */ +extern const stream_procs s_filter_read_procs, s_filter_write_procs; + +/* + * Add a filter to an output pipeline. The client must have allocated the + * stream state, if any, using the given allocator. For s_init_filter, the + * client must have called s_init and s_init_state. + * + * Note that if additional buffering is needed, s_add_filter may add + * an additional filter to provide it. + */ +int s_init_filter(stream *fs, stream_state *fss, byte *buf, uint bsize, + stream *target); +stream *s_add_filter(stream **ps, const stream_template *template, + stream_state *ss, gs_memory_t *mem); + +/* + * Close the filters in a pipeline, up to a given target stream, freeing + * their buffers and state structures. + */ +int s_close_filters(stream **ps, stream *target); + +/* Define templates for the NullEncode/Decode filters. */ +/* They have no state. */ +extern const stream_template s_NullE_template; +extern const stream_template s_NullD_template; + + /* for ziodev.c */ +int file_close_finish(stream *); +int file_close_disable(stream *); + +#endif /* stream_INCLUDED */ |