1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

Add random_access_p port type method

* doc/ref/api-io.texi (I/O Extensions): Update documentation on
  implementing port types.  Document get_natural_buffer_sizes.  Document
  the new random_access_p.
* libguile/fports.c (scm_i_fdes_to_port, fport_random_access_p):
  (scm_make_fptob): Instead of frobbing rw_random manually, implement a
  random_access_p function.
* libguile/ports.c (default_random_access_p)
  (scm_set_port_random_access_p): New functions.
  scm_make_port_type, scm_c_make_port_with_encoding): Arrange for
  random_access_p to work.
This commit is contained in:
Andy Wingo 2016-05-13 10:05:23 +02:00
parent 704c42870d
commit 6ff542ee71
4 changed files with 71 additions and 61 deletions

View file

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

View file

@ -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;

View file

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

View file

@ -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);