Custom ports allow the user to provide input and handle output via user-supplied procedures. The most basic of these operates on the level of bytes, calling user-supplied functions to supply bytes for input and accept bytes for output. In Guile, textual ports are built on top of binary ports, encoding and decoding their codepoint sequences from the bytes; the higher-level textual layer for custom ports allows users to deal in characters instead of bytes.
Before using these procedures, import the appropriate module:
(use-modules (ice-9 binary-ports)) (use-modules (ice-9 textual-ports))
Return a new custom binary input port named id (a string) whose
input is drained by invoking read! and passing it a bytevector, an
index where bytes should be written, and the number of bytes to read.
The read!
procedure must return an integer indicating the number
of bytes read, or 0
to indicate the end-of-file.
Optionally, if get-position is not #f
, it must be a thunk
that will be called when port-position
is invoked on the custom
binary port and should return an integer indicating the position within
the underlying data stream; if get-position was not supplied, the
returned port does not support port-position
.
Likewise, if set-position! is not #f
, it should be a
one-argument procedure. When set-port-position!
is invoked on the
custom binary input port, set-position! is passed an integer
indicating the position of the next byte is to read.
Finally, if close is not #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 setvbuf
(see Buffering).
Using a custom binary input port, the open-bytevector-input-port
procedure (see Bytevector Ports) could be implemented as follows:
(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! #f)) (read (open-bytevector-input-port (string->utf8 "hello"))) ⇒ hello
Return a new custom binary output port named id (a string) whose
output is sunk by invoking write! and passing it a bytevector, an
index where bytes should be read from this bytevector, and the number of
bytes to be “written”. The write!
procedure must return an
integer indicating the number of bytes actually written; when it is
passed 0
as the number of bytes to write, it should behave as
though an end-of-file was sent to the byte sink.
The other arguments are as for make-custom-binary-input-port
.
Return a new custom binary input/output port named id (a string).
The various arguments are the same as for The other arguments are as for
make-custom-binary-input-port
and
make-custom-binary-output-port
. If buffering is enabled on the
port, as is the case by default, input will be buffered in both
directions; See Buffering. If the set-position! function is
provided and not #f
, then the port will also be marked as
random-access, causing the buffer to be flushed between reads and
writes.
Like their custom binary port counterparts, but for textual ports. Concretely this means that instead of being passed a bytevector, the read function is passed a mutable string to fill, and likewise for the buffer supplied to write. Port positions are still expressed in bytes, however.
If string ports were not supplied with Guile, we could implement them With custom textual ports:
(define (open-string-input-port source) (define position 0) (define length (string-length source)) (define (read! dst start count) (let ((count (min count (- length position)))) (string-copy! dst start source position (+ position count)) (set! position (+ position count)) count)) (make-custom-textual-input-port "strport" read! #f #f #f)) (read (open-string-input-port "hello"))