diff --git a/doc/ref/api-io.texi b/doc/ref/api-io.texi index 78f7caec7..1a9c8212d 100644 --- a/doc/ref/api-io.texi +++ b/doc/ref/api-io.texi @@ -2263,72 +2263,20 @@ interface works internally. @cindex ptob @tindex scm_t_ptob_descriptor @tindex scm_t_port -@tindex scm_t_port_buffer @findex SCM_PTAB_ENTRY @findex SCM_PTOBNUM @vindex scm_ptobs -Guile's port facility consists of three main data structures. A port -type object (ptob) is of type @code{scm_t_ptob_descriptor}, and holds -pointers to the methods that implement the port type. A port instance -is of type @code{scm_t_port}, and holds all state for the port. Finally -the read and write buffers are the @code{read_buf} and @code{write_buf} -members of the port instance, and are of type @code{scm_t_port_buffer}. +Guile's port facility consists of two main data types: port type objects +and port instances. A port type object (or @dfn{ptob}) is of type +@code{scm_t_ptob_descriptor}, and holds pointers to the methods that +implement the port type. A port instance is of type @code{scm_t_port}, +and holds all state for the port. Given an @code{SCM} variable which points to a port, the corresponding C port object can be obtained using the @code{SCM_PTAB_ENTRY} macro. The ptob can be obtained by using @code{SCM_PTOBNUM} to give an index into the @code{scm_ptobs} global array. -@subsubheading Port buffers - -An input port always has a read buffer and an output port always has a -write buffer. @xref{Buffering}. These buffers are represented in C by -@code{scm_t_port_buffer} objects. - -The port buffer consists of data as a byte array, pointed to by its -@code{buf} field. The valid data in the buffer is between the -@code{cur} and @code{end} indices into @code{buf}; @code{cur} must -always be less than or equal to @code{end}, which in turn must be less -than or equal to the buffer size @code{size}. The @code{buf} pointer is -actually a pointer to the start of a bytevector, stored in the -@code{bytevector} member. Using bytevectors to back port buffers allows -Scheme to manipulate these buffers. - -``Valid data'' for a read buffer is data that has been buffered, but not -yet read by the user. A port's @code{read} procedure fills a read -buffer from the @code{end} element. For a write buffer, the ``valid -data'' is data which has been written by the user, but not yet flushed -to the mutable store. A port's @code{write} procedure will consume the -data between @code{cur} and @code{end} (not including @code{end}) and -advance @code{cur}. - -The size of the buffers is controlled by the user, via @code{setvbuf}. -A port implementation can provide an idea of what the ``natural'' size -for its buffers are, but it has no guarantee that the buffer will be -those sizes. It's also possible for big reads or writes to work on -auxiliary buffers, and it's possible for @code{unget-bytevector} to -cause a read buffer to expand temporarily; port implementations can't -assume that the buffer they have been given to fill or empty corresponds -to the port's designated read or write buffer. - -Port read buffers also have a flag indicating that the last read did not -advance @code{end}, which indicates end-of-stream. It is cleared by -Guile when Guile gives the user an EOF object. - -@subsubheading The @code{rw_random} flag - -Special treatment is required for ports which can be seeked at random. -Before various operations, such as seeking the port or changing from -input to output on a bidirectional port or vice versa. Seeking on a -port with buffered input, or switching to writing after reading, will -cause the buffered input to be discarded and Guile will seek the port -back the buffered number of bytes. Likewise seeking on a port with -buffered output, or switching to reading after writing, will flush -pending bytes with a call to the @code{write} procedure. Indicate to -Guile that your port needs this behavior by setting the @code{rw_random} -flag. This flag is set by default if the port type supplies a seek -implementation. - @subsubheading C interface A port type object is created by calling @code{scm_make_port_type}. @@ -2403,6 +2351,46 @@ before hand, as appropriate. Set using @deftypefun void scm_set_port_truncate (scm_t_bits tc, void (*truncate) (SCM port, scm_t_off length)) @end deftypefun +@item random_access_p +Determine whether this port is a random-access port. + +@cindex random access +Seeking on a random-access port with buffered input, or switching to +writing after reading, will cause the buffered input to be discarded and +Guile will seek the port back the buffered number of bytes. Likewise +seeking on a random-access port with buffered output, or switching to +reading after writing, will flush pending bytes with a call to the +@code{write} procedure. @xref{Buffering}. + +Indicate to Guile that your port needs this behavior by returning a +nonzero value from your @code{random_access_p} function. The default +implementation of this function returns nonzero if the port type +supplies a seek implementation. + +@deftypefun void scm_set_port_random_access_p (scm_t_bits tc, int (*random_access_p) (SCM port)); +@end deftypefun + +@item get_natural_buffer_sizes +Guile will internally attach buffers to ports. An input port always has +a read buffer and an output port always has a write buffer. +@xref{Buffering}. A port buffer consists of a bytevector, along with +some cursors into that bytevector denoting where to get and put data. + +Port implementations generally don't have to be concerned with +buffering: a port type's @code{read} or @code{write} function will +receive the buffer's bytevector as an argument, along with an offset and +a length into that bytevector, and should then either fill or empty that +bytevector. However in some cases, port implementations may be able to +provide an appropriate default buffer size to Guile. + +@deftypefun void scm_set_port_get_natural_buffer_sizes @ + (scm_t_bits tc, void (*get_natural_buffer_sizes) (SCM, size_t *read_buf_size, size_t *write_buf_size)) +Fill in @var{read_buf_size} and @var{write_buf_size} with an appropriate buffer size for this port, if one is known. +@end deftypefun + +File ports implement a @code{get_natural_buffer_sizes} to let the +operating system inform Guile about the appropriate buffer sizes for the +particular file opened by the port. @end table @node BOM Handling diff --git a/libguile/fports.c b/libguile/fports.c index c6071febb..aab83c9d3 100644 --- a/libguile/fports.c +++ b/libguile/fports.c @@ -418,7 +418,6 @@ scm_i_fdes_to_port (int fdes, long mode_bits, SCM name) port = scm_c_make_port (scm_tc16_fport, mode_bits, (scm_t_bits)fp); - SCM_PTAB_ENTRY (port)->rw_random = SCM_FDES_RANDOM_P (fdes); SCM_SET_FILENAME (port, name); return port; @@ -639,6 +638,12 @@ fport_close (SCM port) scm_syserror ("fport_close"); } +static int +fport_random_access_p (SCM port) +{ + return SCM_FDES_RANDOM_P (SCM_FSTREAM (port)->fdes); +} + /* Query the OS to get the natural buffering for FPORT, if available. */ static void fport_get_natural_buffer_sizes (SCM port, size_t *read_size, size_t *write_size) @@ -663,6 +668,7 @@ scm_make_fptob () scm_set_port_seek (tc, fport_seek); scm_set_port_truncate (tc, fport_truncate); scm_set_port_input_waiting (tc, fport_input_waiting); + scm_set_port_random_access_p (tc, fport_random_access_p); scm_set_port_get_natural_buffer_sizes (tc, fport_get_natural_buffer_sizes); return tc; diff --git a/libguile/ports.c b/libguile/ports.c index 1ad5db096..a2509fb7a 100644 --- a/libguile/ports.c +++ b/libguile/ports.c @@ -196,6 +196,12 @@ scm_c_port_type_add_x (scm_t_ptob_descriptor *desc) static SCM trampoline_to_c_read_subr; static SCM trampoline_to_c_write_subr; +static int +default_random_access_p (SCM port) +{ + return SCM_PORT_DESCRIPTOR (port)->seek != NULL; +} + scm_t_bits scm_make_port_type (char *name, size_t (*read) (SCM port, SCM dst, size_t start, @@ -215,6 +221,7 @@ scm_make_port_type (char *name, desc->c_write = write; desc->scm_read = read ? trampoline_to_c_read_subr : SCM_BOOL_F; desc->scm_write = write ? trampoline_to_c_write_subr : SCM_BOOL_F; + desc->random_access_p = default_random_access_p; ptobnum = scm_c_port_type_add_x (desc); @@ -333,6 +340,13 @@ scm_set_port_input_waiting (scm_t_bits tc, int (*input_waiting) (SCM)) scm_c_port_type_ref (SCM_TC2PTOBNUM (tc))->input_waiting = input_waiting; } +void +scm_set_port_random_access_p (scm_t_bits tc, int (*random_access_p) (SCM)) +{ + scm_t_ptob_descriptor *ptob = scm_c_port_type_ref (SCM_TC2PTOBNUM (tc)); + ptob->random_access_p = random_access_p; +} + void scm_set_port_get_natural_buffer_sizes (scm_t_bits tc, void (*get_natural_buffer_sizes) (SCM, size_t *, size_t *)) @@ -721,9 +735,6 @@ scm_c_make_port_with_encoding (scm_t_bits tag, unsigned long mode_bits, entry->internal = pti; entry->file_name = SCM_BOOL_F; - /* By default, any port type with a seek function has random-access - ports. */ - entry->rw_random = ptob->seek != NULL; entry->port = ret; entry->stream = stream; entry->encoding = encoding; @@ -743,6 +754,8 @@ scm_c_make_port_with_encoding (scm_t_bits tag, unsigned long mode_bits, initialize_port_buffers (ret); + entry->rw_random = ptob->random_access_p (ret); + return ret; } diff --git a/libguile/ports.h b/libguile/ports.h index dc0b30dce..f9b638961 100644 --- a/libguile/ports.h +++ b/libguile/ports.h @@ -191,6 +191,7 @@ typedef struct scm_t_ptob_descriptor void (*get_natural_buffer_sizes) (SCM port, size_t *read_size, size_t *write_size); + int (*random_access_p) (SCM port); int (*input_waiting) (SCM port); @@ -230,6 +231,8 @@ SCM_API void scm_set_port_truncate (scm_t_bits tc, SCM_API void scm_set_port_input_waiting (scm_t_bits tc, int (*input_waiting) (SCM)); SCM_API void scm_set_port_get_natural_buffer_sizes (scm_t_bits tc, void (*get_natural_buffer_sizes) (SCM, size_t *, size_t *)); +SCM_API void scm_set_port_random_access_p (scm_t_bits tc, + int (*random_access_p) (SCM port)); /* The input, output, error, and load ports. */ SCM_API SCM scm_current_input_port (void);