mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-29 19:30:36 +02:00
Update port documentation, rename sports to suspendable ports
* module/ice-9/suspendable-ports.scm: Rename from ice-9/sports.scm, and adapt module names. Remove exports that are not related to the suspendable ports facility; we want people to continue using the port operations from their original locations. Add put-string to the replacement list. * module/Makefile.am: Adapt to rename. * test-suite/tests/suspendable-ports.test: Rename from sports.test. * test-suite/Makefile.am: Adapt to rename. * module/ice-9/textual-ports.scm (unget-char, unget-string): New functions. * doc/ref/api-io.texi (Textual I/O, Simple Output): Flesh out documentation. (Line/Delimited): Undocument write-line, read-string, and read-string!. This is handled by (ice-9 textual-ports). (Bytevector Ports): Fix duplicated section. (String Ports): Move the note about encodings down to the end. (Custom Ports): Add explanatory text. Remove mention of C functions; they should use the C port interface. (Venerable Port Interfaces): Add text, and make documentation refer to recommended interfaces. (Using Ports from C): Add documentation. (Non-Blocking I/O): Document more fully and adapt to suspendable-ports name change.
This commit is contained in:
parent
a21f6467ac
commit
c7c11f3af9
6 changed files with 214 additions and 245 deletions
|
@ -489,20 +489,24 @@ the port is unbuffered.
|
|||
The @code{put-string} procedure returns an unspecified value.
|
||||
@end deffn
|
||||
|
||||
Textual ports have a textual position associated with them: a line and a
|
||||
column. Reading in characters or writing them out advances the line and
|
||||
the column appropriately.
|
||||
|
||||
@deffn {Scheme Procedure} port-column port
|
||||
@deffnx {Scheme Procedure} port-line port
|
||||
@deffnx {C Function} scm_port_column (port)
|
||||
@deffnx {C Function} scm_port_line (port)
|
||||
Return the current column number or line number of @var{port}.
|
||||
If the number is
|
||||
unknown, the result is #f. Otherwise, the result is a 0-origin integer
|
||||
- i.e.@: the first character of the first line is line 0, column 0.
|
||||
(However, when you display a file position, for example in an error
|
||||
message, we recommend you add 1 to get 1-origin integers. This is
|
||||
because lines and column numbers traditionally start with 1, and that is
|
||||
what non-programmers will find most natural.)
|
||||
@end deffn
|
||||
|
||||
Port lines and positions are represented as 0-origin integers, which is
|
||||
to say that the the first character of the first line is line 0, column
|
||||
0. However, when you display a line number, for example in an error
|
||||
message, we recommend you add 1 to get 1-origin integers. This is
|
||||
because lines numbers traditionally start with 1, and that is what
|
||||
non-programmers will find most natural.
|
||||
|
||||
@deffn {Scheme Procedure} set-port-column! port column
|
||||
@deffnx {Scheme Procedure} set-port-line! port line
|
||||
@deffnx {C Function} scm_set_port_column_x (port, column)
|
||||
|
@ -513,6 +517,9 @@ Set the current column or line number of @var{port}.
|
|||
@node Simple Output
|
||||
@subsection Simple Textual Output
|
||||
|
||||
Guile exports a simple formatted output function, @code{simple-format}.
|
||||
For a more capable formatted output facility, @xref{Formatted Output}.
|
||||
|
||||
@deffn {Scheme Procedure} simple-format destination message . args
|
||||
@deffnx {C Function} scm_simple_format (destination, message, args)
|
||||
Write @var{message} to @var{destination}, defaulting to the current
|
||||
|
@ -524,7 +531,11 @@ current output port, if @var{destination} is @code{#f}, then return a
|
|||
string containing the formatted text. Does not add a trailing newline.
|
||||
@end deffn
|
||||
|
||||
pk / peek
|
||||
Somewhat confusingly, Guile binds the @code{format} identifier to
|
||||
@code{simple-format} at startup. Once @code{(ice-9 format)} loads, it
|
||||
actually replaces the core @code{format} binding, so depending on
|
||||
whether you or a module you use has loaded @code{(ice-9 format)}, you
|
||||
may be using the simple or the more capable version.
|
||||
|
||||
@node Buffering
|
||||
@subsection Buffering
|
||||
|
@ -728,11 +739,8 @@ The delimited-I/O module can be accessed with:
|
|||
@end lisp
|
||||
|
||||
It can be used to read or write lines of text, or read text delimited by
|
||||
a specified set of characters. It's similar to the @code{(scsh rdelim)}
|
||||
module from guile-scsh, but does not use multiple values or character
|
||||
sets and has an extra procedure @code{write-line}.
|
||||
a specified set of characters.
|
||||
|
||||
@c begin (scm-doc-string "rdelim.scm" "read-line")
|
||||
@deffn {Scheme Procedure} read-line [port] [handle-delim]
|
||||
Return a line of text from @var{port} if specified, otherwise from the
|
||||
value returned by @code{(current-input-port)}. Under Unix, a line of text
|
||||
|
@ -755,21 +763,19 @@ terminating delimiter or end-of-file object.
|
|||
@end table
|
||||
@end deffn
|
||||
|
||||
@c begin (scm-doc-string "rdelim.scm" "read-line!")
|
||||
@deffn {Scheme Procedure} read-line! buf [port]
|
||||
Read a line of text into the supplied string @var{buf} and return the
|
||||
number of characters added to @var{buf}. If @var{buf} is filled, then
|
||||
@code{#f} is returned.
|
||||
Read from @var{port} if
|
||||
specified, otherwise from the value returned by @code{(current-input-port)}.
|
||||
@code{#f} is returned. Read from @var{port} if specified, otherwise
|
||||
from the value returned by @code{(current-input-port)}.
|
||||
@end deffn
|
||||
|
||||
@c begin (scm-doc-string "rdelim.scm" "read-delimited")
|
||||
@deffn {Scheme Procedure} read-delimited delims [port] [handle-delim]
|
||||
Read text until one of the characters in the string @var{delims} is found
|
||||
or end-of-file is reached. Read from @var{port} if supplied, otherwise
|
||||
from the value returned by @code{(current-input-port)}.
|
||||
@var{handle-delim} takes the same values as described for @code{read-line}.
|
||||
Read text until one of the characters in the string @var{delims} is
|
||||
found or end-of-file is reached. Read from @var{port} if supplied,
|
||||
otherwise from the value returned by @code{(current-input-port)}.
|
||||
@var{handle-delim} takes the same values as described for
|
||||
@code{read-line}.
|
||||
@end deffn
|
||||
|
||||
@c begin (scm-doc-string "rdelim.scm" "read-delimited!")
|
||||
|
@ -787,48 +793,6 @@ buffer was full, @code{#f} is returned.
|
|||
It's something of a wacky interface, to be honest.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} write-line obj [port]
|
||||
@deffnx {C Function} scm_write_line (obj, port)
|
||||
Display @var{obj} and a newline character to @var{port}. If
|
||||
@var{port} is not specified, @code{(current-output-port)} is
|
||||
used. This function is equivalent to:
|
||||
@lisp
|
||||
(display obj [port])
|
||||
(newline [port])
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
In the past, Guile did not have a procedure that would just read out all
|
||||
of the characters from a port. As a workaround, many people just called
|
||||
@code{read-delimited} with no delimiters, knowing that would produce the
|
||||
behavior they wanted. This prompted Guile developers to add some
|
||||
routines that would read all characters from a port. So it is that
|
||||
@code{(ice-9 rdelim)} is also the home for procedures that can reading
|
||||
undelimited text:
|
||||
|
||||
@deffn {Scheme Procedure} read-string [port] [count]
|
||||
Read all of the characters out of @var{port} and return them as a
|
||||
string. If the @var{count} is present, treat it as a limit to the
|
||||
number of characters to read.
|
||||
|
||||
By default, read from the current input port, with no size limit on the
|
||||
result. This procedure always returns a string, even if no characters
|
||||
were read.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} read-string! buf [port] [start] [end]
|
||||
Fill @var{buf} with characters read from @var{port}, defaulting to the
|
||||
current input port. Return the number of characters read.
|
||||
|
||||
If @var{start} or @var{end} are specified, store data only into the
|
||||
substring of @var{str} bounded by @var{start} and @var{end} (which
|
||||
default to the beginning and end of the string, respectively).
|
||||
@end deffn
|
||||
|
||||
Some of the aforementioned I/O functions rely on the following C
|
||||
primitives. These will mainly be of interest to people hacking Guile
|
||||
internals.
|
||||
|
||||
@deffn {Scheme Procedure} %read-delimited! delims str gobble [port [start [end]]]
|
||||
@deffnx {C Function} scm_read_delimited_x (delims, str, gobble, port, start, end)
|
||||
Read characters from @var{port} into @var{str} until one of the
|
||||
|
@ -1131,7 +1095,7 @@ used only during port creation are not retained.
|
|||
Return the filename associated with @var{port}, or @code{#f} if no
|
||||
filename is associated with the port.
|
||||
|
||||
@var{port} must be open, @code{port-filename} cannot be used once the
|
||||
@var{port} must be open; @code{port-filename} cannot be used once the
|
||||
port is closed.
|
||||
@end deffn
|
||||
|
||||
|
@ -1161,64 +1125,6 @@ Return an input port whose contents are drawn from bytevector @var{bv}
|
|||
The @var{transcoder} argument is currently not supported.
|
||||
@end deffn
|
||||
|
||||
@cindex custom binary input ports
|
||||
|
||||
@deffn {Scheme Procedure} make-custom-binary-input-port id read! get-position set-position! close
|
||||
@deffnx {C Function} scm_make_custom_binary_input_port (id, read!, get-position, set-position!, close)
|
||||
Return a new custom binary input port@footnote{This is similar in spirit
|
||||
to Guile's @dfn{soft ports} (@pxref{Soft Ports}).} named @var{id} (a
|
||||
string) whose input is drained by invoking @var{read!} and passing it a
|
||||
bytevector, an index where bytes should be written, and the number of
|
||||
bytes to read. The @code{read!} procedure must return an integer
|
||||
indicating the number of bytes read, or @code{0} to indicate the
|
||||
end-of-file.
|
||||
|
||||
Optionally, if @var{get-position} is not @code{#f}, it must be a thunk
|
||||
that will be called when @code{port-position} is invoked on the custom
|
||||
binary port and should return an integer indicating the position within
|
||||
the underlying data stream; if @var{get-position} was not supplied, the
|
||||
returned port does not support @code{port-position}.
|
||||
|
||||
Likewise, if @var{set-position!} is not @code{#f}, it should be a
|
||||
one-argument procedure. When @code{set-port-position!} is invoked on the
|
||||
custom binary input port, @var{set-position!} is passed an integer
|
||||
indicating the position of the next byte is to read.
|
||||
|
||||
Finally, if @var{close} is not @code{#f}, it must be a thunk. It is
|
||||
invoked when the custom binary input port is closed.
|
||||
|
||||
The returned port is fully buffered by default, but its buffering mode
|
||||
can be changed using @code{setvbuf} (@pxref{Buffering}).
|
||||
|
||||
Using a custom binary input port, the @code{open-bytevector-input-port}
|
||||
procedure could be implemented as follows:
|
||||
|
||||
@lisp
|
||||
(define (open-bytevector-input-port source)
|
||||
(define position 0)
|
||||
(define length (bytevector-length source))
|
||||
|
||||
(define (read! bv start count)
|
||||
(let ((count (min count (- length position))))
|
||||
(bytevector-copy! source position
|
||||
bv start count)
|
||||
(set! position (+ position count))
|
||||
count))
|
||||
|
||||
(define (get-position) position)
|
||||
|
||||
(define (set-position! new-position)
|
||||
(set! position new-position))
|
||||
|
||||
(make-custom-binary-input-port "the port" read!
|
||||
get-position
|
||||
set-position!))
|
||||
|
||||
(read (open-bytevector-input-port (string->utf8 "hello")))
|
||||
@result{} hello
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} open-bytevector-output-port [transcoder]
|
||||
@deffnx {C Function} scm_open_bytevector_output_port (transcoder)
|
||||
Return two values: a binary output port and a procedure. The latter
|
||||
|
@ -1246,16 +1152,6 @@ The @var{transcoder} argument is currently not supported.
|
|||
@cindex String port
|
||||
@cindex Port, string
|
||||
|
||||
The following allow string ports to be opened by analogy to R4RS
|
||||
file port facilities:
|
||||
|
||||
With string ports, the port-encoding is treated differently than other
|
||||
types of ports. When string ports are created, they do not inherit a
|
||||
character encoding from the current locale. They are given a
|
||||
default locale that allows them to handle all valid string characters.
|
||||
Typically one should not modify a string port's character encoding
|
||||
away from its default.
|
||||
|
||||
@deffn {Scheme Procedure} call-with-output-string proc
|
||||
@deffnx {C Function} scm_call_with_output_string (proc)
|
||||
Calls the one-argument procedure @var{proc} with a newly created output
|
||||
|
@ -1309,20 +1205,25 @@ output to the port so far.
|
|||
closed the string cannot be obtained.
|
||||
@end deffn
|
||||
|
||||
A string port can be used in many procedures which accept a port
|
||||
but which are not dependent on implementation details of fports.
|
||||
E.g., seeking and truncating will work on a string port,
|
||||
but trying to extract the file descriptor number will fail.
|
||||
With string ports, the port-encoding is treated differently than other
|
||||
types of ports. When string ports are created, they do not inherit a
|
||||
character encoding from the current locale. They are given a
|
||||
default locale that allows them to handle all valid string characters.
|
||||
Typically one should not modify a string port's character encoding
|
||||
away from its default. @xref{Encoding}.
|
||||
|
||||
|
||||
@node Custom Ports
|
||||
@subsubsection Custom Ports
|
||||
|
||||
(ice-9 binary-ports), binary and text...
|
||||
Custom ports allow the user to provide input and handle output via
|
||||
user-supplied procedures. Guile currently only provides custom binary
|
||||
ports, not textual ports; for custom textual ports, @xref{Soft Ports}.
|
||||
We should add the R6RS custom textual port interfaces though.
|
||||
Contributions are appreciated.
|
||||
|
||||
@cindex custom binary input ports
|
||||
@deffn {Scheme Procedure} make-custom-binary-input-port id read! get-position set-position! close
|
||||
@deffnx {C Function} scm_make_custom_binary_input_port (id, read!, get-position, set-position!, close)
|
||||
Return a new custom binary input port@footnote{This is similar in spirit
|
||||
to Guile's @dfn{soft ports} (@pxref{Soft Ports}).} named @var{id} (a
|
||||
string) whose input is drained by invoking @var{read!} and passing it a
|
||||
|
@ -1379,7 +1280,6 @@ procedure (@pxref{Bytevector Ports}) could be implemented as follows:
|
|||
|
||||
@cindex custom binary output ports
|
||||
@deffn {Scheme Procedure} make-custom-binary-output-port id write! get-position set-position! close
|
||||
@deffnx {C Function} scm_make_custom_binary_output_port (id, write!, get-position, set-position!, close)
|
||||
Return a new custom binary output port named @var{id} (a string) whose
|
||||
output is sunk by invoking @var{write!} and passing it a bytevector, an
|
||||
index where bytes should be read from this bytevector, and the number of
|
||||
|
@ -1397,11 +1297,10 @@ The other arguments are as for @code{make-custom-binary-input-port}.
|
|||
@cindex Soft port
|
||||
@cindex Port, soft
|
||||
|
||||
A @dfn{soft-port} is a port based on a vector of procedures capable of
|
||||
A @dfn{soft port} is a port based on a vector of procedures capable of
|
||||
accepting or delivering characters. It allows emulation of I/O ports.
|
||||
|
||||
@deffn {Scheme Procedure} make-soft-port pv modes
|
||||
@deffnx {C Function} scm_make_soft_port (pv, modes)
|
||||
Return a port capable of receiving or delivering characters as
|
||||
specified by the @var{modes} string (@pxref{File Ports,
|
||||
open-file}). @var{pv} must be a vector of length 5 or 6. Its
|
||||
|
@ -1469,9 +1368,34 @@ documentation for @code{open-file} in @ref{File Ports}.
|
|||
@node Venerable Port Interfaces
|
||||
@subsection Venerable Port Interfaces
|
||||
|
||||
Over the 25 years or so that Guile has been around, its port system has
|
||||
evolved, adding many useful features. At the same time there have been
|
||||
four major Scheme standards released in those 25 years, which also
|
||||
evolve the common Scheme understanding of what a port interface should
|
||||
be. Alas, it would be too much to ask for all of these evolutionary
|
||||
branches to be consistent. Some of Guile's original interfaces don't
|
||||
mesh with the later Scheme standards, and yet Guile can't just drop old
|
||||
interfaces. Sadly as well, the R6RS and R7RS standards both part from a
|
||||
base of R5RS, but end up in different and somewhat incompatible designs.
|
||||
|
||||
Guile's approach is to pick a set of port primitives that make sense
|
||||
together. We document that set of primitives, design our internal
|
||||
interfaces around them, and recommend them to users. As the R6RS I/O
|
||||
system is the most capable standard that Scheme has yet produced in this
|
||||
domain, we mostly recommend that; @code{(ice-9 binary-ports)} and
|
||||
@code{(ice-9 textual-ports)} are wholly modelled on @code{(rnrs io
|
||||
ports)}. Guile does not wholly copy R6RS, however; @xref{R6RS
|
||||
Incompatibilities}.
|
||||
|
||||
At the same time, we have many venerable port interfaces, lore handed
|
||||
down to us from our hacker ancestors. Most of these interfaces even
|
||||
predate the expectation that Scheme should have modules, so they are
|
||||
present in the default environment. In Guile we support them as well
|
||||
and we have no plans to remove them, but again we don't recommend them
|
||||
for new users.
|
||||
|
||||
@rnindex char-ready?
|
||||
@deffn {Scheme Procedure} char-ready? [port]
|
||||
@deffnx {C Function} scm_char_ready_p (port)
|
||||
Return @code{#t} if a character is ready on input @var{port}
|
||||
and return @code{#f} otherwise. If @code{char-ready?} returns
|
||||
@code{#t} then the next @code{read-char} operation on
|
||||
|
@ -1490,90 +1414,82 @@ interactive port that has no ready characters.
|
|||
|
||||
@rnindex read-char
|
||||
@deffn {Scheme Procedure} read-char [port]
|
||||
@deffnx {C Function} scm_read_char (port)
|
||||
Return the next character available from @var{port}, updating @var{port}
|
||||
to point to the following character. If no more characters are
|
||||
available, the end-of-file object is returned. A decoding error, if
|
||||
any, is handled in accordance with the port's conversion strategy.
|
||||
The same as @code{get-char}, except that @var{port} defaults to the
|
||||
current input port. @xref{Textual I/O}.
|
||||
@end deffn
|
||||
|
||||
@rnindex peek-char
|
||||
@deffn {Scheme Procedure} peek-char [port]
|
||||
@deffnx {C Function} scm_peek_char (port)
|
||||
Return the next character available from @var{port},
|
||||
@emph{without} updating @var{port} to point to the following
|
||||
character. If no more characters are available, the
|
||||
end-of-file object is returned.
|
||||
|
||||
The value returned by
|
||||
a call to @code{peek-char} is the same as the value that would
|
||||
have been returned by a call to @code{read-char} on the same
|
||||
port. The only difference is that the very next call to
|
||||
@code{read-char} or @code{peek-char} on that @var{port} will
|
||||
return the value returned by the preceding call to
|
||||
@code{peek-char}. In particular, a call to @code{peek-char} on
|
||||
an interactive port will hang waiting for input whenever a call
|
||||
to @code{read-char} would have hung.
|
||||
|
||||
As for @code{read-char}, decoding errors are handled in accordance with
|
||||
the port's conversion strategy.
|
||||
The same as @code{lookahead-char}, except that @var{port} defaults to
|
||||
the current input port. @xref{Textual I/O}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} unread-char cobj [port]
|
||||
@deffnx {C Function} scm_unread_char (cobj, port)
|
||||
Place character @var{cobj} in @var{port} so that it will be read by the
|
||||
next read operation. If called multiple times, the unread characters
|
||||
will be read again in last-in first-out order. If @var{port} is
|
||||
not supplied, the current input port is used.
|
||||
The same as @code{unget-char}, except that @var{port} defaults to the
|
||||
current input port, and the arguments are swapped. @xref{Textual I/O}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} unread-string str port
|
||||
@deffnx {C Function} scm_unread_string (str, port)
|
||||
Place the string @var{str} in @var{port} so that its characters will
|
||||
be read from left-to-right as the next characters from @var{port}
|
||||
during subsequent read operations. If called multiple times, the
|
||||
unread characters will be read again in last-in first-out order. If
|
||||
@var{port} is not supplied, the @code{current-input-port} is used.
|
||||
The same as @code{unget-string}, except that @var{port} defaults to the
|
||||
current input port, and the arguments are swapped. @xref{Textual I/O}.
|
||||
@end deffn
|
||||
|
||||
@rnindex newline
|
||||
@deffn {Scheme Procedure} newline [port]
|
||||
@deffnx {C Function} scm_newline (port)
|
||||
Send a newline to @var{port}.
|
||||
If @var{port} is omitted, send to the current output port.
|
||||
Send a newline to @var{port}. If @var{port} is omitted, send to the
|
||||
current output port. Equivalent to @code{(put-char port #\newline)}.
|
||||
@end deffn
|
||||
|
||||
@rnindex write-char
|
||||
@deffn {Scheme Procedure} write-char chr [port]
|
||||
@deffnx {C Function} scm_write_char (chr, port)
|
||||
Send character @var{chr} to @var{port}.
|
||||
The same as @code{put-char}, except that @var{port} defaults to the
|
||||
current input port, and the arguments are swapped. @xref{Textual I/O}.
|
||||
@end deffn
|
||||
|
||||
@node Using Ports from C
|
||||
@subsection Using Ports from C
|
||||
|
||||
Guile's C interfaces provides some niceties for sending and receiving
|
||||
bytes and characters in a way that works better with C.
|
||||
|
||||
@deftypefn {C Function} size_t scm_c_read (SCM port, void *buffer, size_t size)
|
||||
Read up to @var{size} bytes from @var{port} and store them in
|
||||
@var{buffer}. The return value is the number of bytes actually read,
|
||||
which can be less than @var{size} if end-of-file has been reached.
|
||||
|
||||
Note that this function does not update @code{port-line} and
|
||||
@code{port-column} below.
|
||||
Note that as this is a binary input procedure, this function does not
|
||||
update @code{port-line} and @code{port-column} (@pxref{Textual I/O}).
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_c_write (SCM port, const void *buffer, size_t size)
|
||||
Write @var{size} bytes at @var{buffer} to @var{port}.
|
||||
|
||||
Note that this function does not update @code{port-line} and
|
||||
@code{port-column} (@pxref{Textual I/O}).
|
||||
Note that as this is a binary output procedure, this function does not
|
||||
update @code{port-line} and @code{port-column} (@pxref{Textual I/O}).
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_lfwrite (const char *buffer, size_t size, SCM port)
|
||||
Write @var{size} bytes at @var{buffer} to @var{port}. The @code{lf}
|
||||
indicates that unlike @code{scm_c_write}, this function updates the
|
||||
port's @code{port-line} and @code{port-column}, and also flushes the
|
||||
port if the data contains a newline (@code{\n}) and the port is
|
||||
line-buffered.
|
||||
@deftypefn {C Function} size_t scm_c_read_bytes (SCM port, SCM bv, size_t start, size_t count)
|
||||
@deftypefnx {C Function} void scm_c_write_bytes (SCM port, SCM bv, size_t start, size_t count)
|
||||
Like @code{scm_c_read} and @code{scm_c_write}, but reading into or
|
||||
writing from the bytevector @var{bv}. @var{count} indicates the byte
|
||||
index at which to start in the bytevector, and the read or write will
|
||||
continue for @var{count} bytes.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_unget_bytes (const unsigned char *buf, size_t len, SCM port)
|
||||
@deftypefnx {C Function} void scm_unget_byte (int c, SCM port)
|
||||
@deftypefnx {C Function} void scm_ungetc (scm_t_wchar c, SCM port)
|
||||
Like @code{unget-bytevector}, @code{unget-byte}, and @code{unget-char},
|
||||
respectively. @xref{Textual I/O}.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_c_put_latin1_chars (SCM port, const scm_t_uint8 *buf, size_t len)
|
||||
@deftypefnx {C Function} void scm_c_put_utf32_chars (SCM port, const scm_t_uint32 *buf, size_t len);
|
||||
Write a string to @var{port}. In the first case, the
|
||||
@code{scm_t_uint8*} buffer is a string in the latin-1 encoding. In the
|
||||
second, the @code{scm_t_uint32*} buffer is a string in the UTF-32
|
||||
encoding. These routines will update the port's line and column.
|
||||
@end deftypefn
|
||||
|
||||
@node I/O Extensions
|
||||
|
@ -1582,15 +1498,13 @@ line-buffered.
|
|||
This section describes how to implement a new port type in C. Although
|
||||
ports support many operations, as a data structure they present an
|
||||
opaque interface to the user. To the port implementor, you have two
|
||||
additional pieces of information: the port type, which is an opaque
|
||||
pointer allocated when defining your port type; and a port's ``stream'',
|
||||
which you allocate when you create a port.
|
||||
|
||||
The type code helps you identify which ports are actually yours. The
|
||||
``stream'' is the private data associated with that port which you and
|
||||
only you control. Get a stream from a port using the @code{SCM_STREAM}
|
||||
macro. Note that your port methods are only ever called with ports of
|
||||
your type.
|
||||
pieces of information to work with: the port type, and the port's
|
||||
``stream''. The port type is an opaque pointer allocated when defining
|
||||
your port type. It is your key into the port API, and it helps you
|
||||
identify which ports are actually yours. The ``stream'' is a pointer
|
||||
you control, and which you set when you create a port. Get a stream
|
||||
from a port using the @code{SCM_STREAM} macro. Note that your port
|
||||
methods are only ever called with ports of your type.
|
||||
|
||||
A port type is created by calling @code{scm_make_port_type}. Once you
|
||||
have your port type, you can create ports with @code{scm_c_make_port},
|
||||
|
@ -1789,27 +1703,81 @@ incantation:
|
|||
@end example
|
||||
|
||||
Now the file descriptor is open in non-blocking mode. If Guile tries to
|
||||
read or write from this file descriptor in C, it will block by polling
|
||||
on the socket's @code{read_wait_fd}, to preserve the illusion of a
|
||||
blocking read or write. @xref{I/O Extensions} for more on that internal
|
||||
interface.
|
||||
read or write from this file and the read or write returns a result
|
||||
indicating that more data can only be had by doing a blocking read or
|
||||
write, Guile will block by polling on the socket's @code{read-wait-fd}
|
||||
or @code{write-wait-fd}, to preserve the illusion of a blocking read or
|
||||
write. @xref{I/O Extensions} for more on those internal interfaces.
|
||||
|
||||
However if a user uses the new and experimental Scheme implementation of
|
||||
ports in @code{(ice-9 sports)}, Guile instead calls the value of the
|
||||
@code{current-read-waiter} or @code{current-write-waiter} parameters on
|
||||
the port before re-trying the read or write. The default value of these
|
||||
parameters does the same thing as the C port runtime: it blocks.
|
||||
However it's possible to dynamically bind these parameters to handlers
|
||||
that can suspend the current coroutine to a scheduler, to be later
|
||||
re-animated once the port becomes readable or writable in the future.
|
||||
In the mean-time the scheduler can run other code, for example servicing
|
||||
other web requests.
|
||||
So far we have just reproduced the status quo: the file descriptor is
|
||||
non-blocking, but the operations on the port do block. To go farther,
|
||||
it would be nice if we could suspend the ``thread'' using delimited
|
||||
continuations, and only resume the thread once the file descriptor is
|
||||
readable or writable. (@xref{Prompts}).
|
||||
|
||||
Guile does not currently include such a scheduler. Currently we want to
|
||||
make sure that we're providing the right primitives that can be used to
|
||||
build schedulers and other user-space concurrency patterns. In the
|
||||
meantime, have a look at 8sync (@url{https://gnu.org/software/8sync})
|
||||
for a prototype of an asynchronous I/O and concurrency facility.
|
||||
But here we run into a difficulty. The ports code is implemented in C,
|
||||
which means that although we can suspend the computation to some outer
|
||||
prompt, we can't resume it because Guile can't resume delimited
|
||||
continuations that capture the C stack.
|
||||
|
||||
To overcome this difficulty we have created a compatible but entirely
|
||||
parallel implementation of port operations. To use this implementation,
|
||||
do the following:
|
||||
|
||||
@example
|
||||
(use-module (ice-9 suspendable-ports))
|
||||
(install-suspendable-ports!)
|
||||
@end example
|
||||
|
||||
This will replace the core I/O primitives like @code{get-char} and
|
||||
@code{put-bytevector} with new versions that are exactly the same as the
|
||||
ones in the standard library, but with two differences. One is that
|
||||
when a read or a write would block, the suspendable port operations call
|
||||
out the value of the @code{current-read-waiter} or
|
||||
@code{current-write-waiter} parameter, as appropriate.
|
||||
@xref{Parameters}. The default read and write waiters do the same thing
|
||||
that the C read and write waiters do, which is to poll. User code can
|
||||
parameterize the waiters, though, enabling the computation to suspend
|
||||
and allow the program to process other I/O operations. Because the new
|
||||
suspendable ports implementation is written in Scheme, that suspended
|
||||
computation can resume again later when it is able to make progress.
|
||||
Success!
|
||||
|
||||
The other main difference is that because the new ports implementation
|
||||
is written in Scheme, it is slower than C, currently by a factor of 3 or
|
||||
4, though it depends on many factors. For this reason we have to keep
|
||||
the C implementations as the default ones. One day when Guile's
|
||||
compiler is better, we can close this gap and have only one port
|
||||
operation implementation again.
|
||||
|
||||
Note that Guile does not currently include an implementation of the
|
||||
facility to suspend the current thread and schedule other threads in the
|
||||
meantime. Before adding such a thing, we want to make sure that we're
|
||||
providing the right primitives that can be used to build schedulers and
|
||||
other user-space concurrency patterns, and that the patterns that we
|
||||
settle on are the right patterns. In the meantime, have a look at 8sync
|
||||
(@url{https://gnu.org/software/8sync}) for a prototype of an
|
||||
asynchronous I/O and concurrency facility.
|
||||
|
||||
@deffn {Scheme Procedure} install-suspendable-ports!
|
||||
Replace the core ports implementation with suspendable ports, as
|
||||
described above. This will mutate the values of the bindings like
|
||||
@code{get-char}, @code{put-u8}, and so on in place.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} uninstall-suspendable-ports!
|
||||
Restore the original core ports implementation, un-doing the effect of
|
||||
@code{install-suspendable-ports!}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Parameter} current-read-waiter
|
||||
@deffnx {Scheme Parameter} current-write-waiter
|
||||
Parameters whose values are procedures of one argument, called when a
|
||||
suspendable port operation would block on a port while reading or
|
||||
writing, respectively. The default values of these parameters do a
|
||||
blocking @code{poll} on the port's file descriptor. The procedures are
|
||||
passed the port in question as their one argument.
|
||||
@end deffn
|
||||
|
||||
|
||||
@node BOM Handling
|
||||
|
|
|
@ -106,10 +106,10 @@ SOURCES = \
|
|||
ice-9/serialize.scm \
|
||||
ice-9/session.scm \
|
||||
ice-9/slib.scm \
|
||||
ice-9/sports.scm \
|
||||
ice-9/stack-catch.scm \
|
||||
ice-9/streams.scm \
|
||||
ice-9/string-fun.scm \
|
||||
ice-9/suspendable-ports.scm \
|
||||
ice-9/syncase.scm \
|
||||
ice-9/textual-ports.scm \
|
||||
ice-9/threads.scm \
|
||||
|
|
|
@ -48,30 +48,15 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
(define-module (ice-9 sports)
|
||||
(define-module (ice-9 suspendable-ports)
|
||||
#:use-module (rnrs bytevectors)
|
||||
#:use-module (ice-9 ports internal)
|
||||
#:use-module (ice-9 match)
|
||||
#:replace (peek-char
|
||||
read-char
|
||||
force-output
|
||||
close-port)
|
||||
#:export (current-read-waiter
|
||||
current-write-waiter
|
||||
|
||||
lookahead-u8
|
||||
get-u8
|
||||
get-bytevector-n
|
||||
put-u8
|
||||
put-bytevector
|
||||
put-string
|
||||
|
||||
%read-line
|
||||
read-line
|
||||
read-delimited
|
||||
|
||||
install-sports!
|
||||
uninstall-sports!))
|
||||
install-suspendable-ports!
|
||||
uninstall-suspendable-ports!))
|
||||
|
||||
(define (default-read-waiter port) (port-poll port "r"))
|
||||
(define (default-write-waiter port) (port-poll port "w"))
|
||||
|
@ -681,11 +666,14 @@
|
|||
((ice-9 binary-ports)
|
||||
get-u8 lookahead-u8 get-bytevector-n
|
||||
put-u8 put-bytevector)
|
||||
((ice-9 textual-ports)
|
||||
;; FIXME: put-char
|
||||
put-string)
|
||||
((ice-9 rdelim) %read-line read-line read-delimited)))
|
||||
(define (install-sports!)
|
||||
(define (install-suspendable-ports!)
|
||||
(unless saved-port-bindings
|
||||
(set! saved-port-bindings (make-hash-table))
|
||||
(let ((sports (resolve-module '(ice-9 sports))))
|
||||
(let ((suspendable-ports (resolve-module '(ice-9 suspendable-ports))))
|
||||
(for-each
|
||||
(match-lambda
|
||||
((mod . syms)
|
||||
|
@ -694,11 +682,11 @@
|
|||
(hashq-set! saved-port-bindings sym
|
||||
(module-ref mod sym))
|
||||
(module-set! mod sym
|
||||
(module-ref sports sym)))
|
||||
(module-ref suspendable-ports sym)))
|
||||
syms))))
|
||||
port-bindings))))
|
||||
|
||||
(define (uninstall-sports!)
|
||||
(define (uninstall-suspendable-ports!)
|
||||
(when saved-port-bindings
|
||||
(for-each
|
||||
(match-lambda
|
|
@ -28,6 +28,8 @@
|
|||
put-char
|
||||
put-string)
|
||||
#:export (get-char
|
||||
unget-char
|
||||
unget-string
|
||||
lookahead-char
|
||||
get-string-n
|
||||
get-string-all
|
||||
|
@ -39,6 +41,17 @@
|
|||
(define (lookahead-char port)
|
||||
(peek-char port))
|
||||
|
||||
(define (unget-char port char)
|
||||
(unread-char char port))
|
||||
|
||||
(define* (unget-string port string #:optional (start 0)
|
||||
(count (- (string-length string) start)))
|
||||
(unread-string (if (and (zero? start)
|
||||
(= count (string-length string)))
|
||||
string
|
||||
(substring/shared string start (+ start count)))
|
||||
port))
|
||||
|
||||
(define (get-line port)
|
||||
(read-line port 'trim))
|
||||
|
||||
|
|
|
@ -127,7 +127,6 @@ SCM_TESTS = tests/00-initial-env.test \
|
|||
tests/session.test \
|
||||
tests/signals.test \
|
||||
tests/sort.test \
|
||||
tests/sports.test \
|
||||
tests/srcprop.test \
|
||||
tests/srfi-1.test \
|
||||
tests/srfi-6.test \
|
||||
|
@ -164,6 +163,7 @@ SCM_TESTS = tests/00-initial-env.test \
|
|||
tests/streams.test \
|
||||
tests/strings.test \
|
||||
tests/structs.test \
|
||||
tests/suspendable-ports.test \
|
||||
tests/sxml.fold.test \
|
||||
tests/sxml.match.test \
|
||||
tests/sxml.simple.test \
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
;;;; <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (test-suite test-ports)
|
||||
#:use-module ((ice-9 sports) #:select (install-sports! uninstall-sports!)))
|
||||
#:use-module (ice-9 suspendable-ports))
|
||||
|
||||
;; Include tests from ports.test.
|
||||
|
||||
|
@ -49,10 +49,10 @@
|
|||
#`((include-one #,exp) . #,(lp))))))))
|
||||
#:guess-encoding #t)))))
|
||||
|
||||
(install-sports!)
|
||||
(install-suspendable-ports!)
|
||||
|
||||
(include-tests "tests/ports.test")
|
||||
(include-tests "tests/rdelim.test")
|
||||
(include-tests "tests/r6rs-ports.test")
|
||||
|
||||
(uninstall-sports!)
|
||||
(uninstall-suspendable-ports!)
|
Loading…
Add table
Add a link
Reference in a new issue