IMPERATIVE_IO
signature
signature IMPERATIVE_IO
The IMPERATIVE_IO
signature defines the interface of the Imperative I/O layer in the I/O stack. This layer provides buffered I/O using mutable, redirectable streams.
structure StreamIO : STREAM_IO
type vector = StreamIO.vector
type elem = StreamIO.elem
type instream
type outstream
val input : instream -> vector
val input1 : instream -> elem option
val inputN : instream * int -> vector
val inputAll : instream -> vector
val canInput : instream * int -> int option
val lookahead : instream -> elem option
val closeIn : instream -> unit
val endOfStream : instream -> bool
val output : outstream * vector -> unit
val output1 : outstream * elem -> unit
val flushOut : outstream -> unit
val closeOut : outstream -> unit
val mkInstream : StreamIO.instream -> instream
val getInstream : instream -> StreamIO.instream
val setInstream : instream * StreamIO.instream -> unit
val mkOutstream : StreamIO.outstream -> outstream
val getOutstream : outstream -> StreamIO.outstream
val setOutstream : outstream * StreamIO.outstream -> unit
val getPosOut : outstream -> StreamIO.out_pos
val setPosOut : outstream * StreamIO.out_pos -> unit
structure StreamIO : STREAM_IO
STREAM_IO
interface, which is compatible with the instream
and outstream
types, in the sense that the conversion functions mkInstream
, getInstream
, mkOutstream
, and getOutstream
allow the programmer to convert between low-level streams and redirectable streams. Typically, the redirectable streams are implemented in terms of low-level streams. Note that StreamIO.outstream
is not a functional stream. The only difference between a StreamIO.outstream
and an outstream
is that the latter can be redirected.
type vector = StreamIO.vector
type elem = StreamIO.elem
Char.char
and String.string
, while for binary streams, they correspond to Word8.word
and Word8Vector.vector
.
type instream
type outstream
input strm
vector
of at least one element. When strm is at end-of-stream or is closed, it returns an empty vector. Otherwise, input
blocks until one of these conditions is met, and returns accordingly. It may raise the exception Io
.
input1 strm
SOME
(e)
if one element was available; it returns NONE
if at end-of-stream. It may block, and may raise the exception Io
.
After a call to input1
returning NONE
to indicate an end-of-stream, the input stream should be positioned after the end-of-stream.
inputN (strm, n)
Io
. It raises Size
if n < 0 or if n is greater than the maxLen
value for the vector
type.
inputAll strm
Io
. It raises Size
if the amount of data exceeds the maxLen
of the vector
type.
canInput (strm, n)
NONE
if any attempt at input would block. It returns SOME
(k)
, where 0 <= k <= n, if a call to input
would return immediately with at least k characters. Note that k = 0 corresponds to the stream being at end-of-stream.
Some streams may not support this operation, in which case the Io
exception will be raised. This function also raises the Io
exception if there is an error in the underlying system calls. It raises the Size
exception if n < 0.
Implementation note:
It is suggested that implementations of
canInput
should attempt to return as large a k as possible. For example, if the buffer contains 10 characters and the user callscanInput (f, 15)
,canInput
should callreadVecNB(5)
to see if an additional 5 characters are available.
lookahead strm
SOME
(e)
in this case; it returns NONE
if at end-of-stream. In the former case, e
is not removed from strm but stays available for further input operations. It may block, and may raise the exception Io
.
The underlying STREAM_IO
stream can be used to easily implement arbitrary lookahead.
closeIn strm
StreamIO.closeIn
. It may also raise Io
when another error occurs.
endOfStream strm
true
if strm is at end-of-stream, and false
if elements are still available. It may block until one of these conditions is determined, and may raise the exception Io
.
When endOfStream
returns true
on an untruncated stream, this denotes the current situation. After a read from strm to consume the end-of-stream, it is possible that the next call to endOfStream strm
may return false, and input operations will deliver new elements. For further information, consult the description of STREAM_IO.endOfStream
.
output (strm, vec)
Io
. In that case, it is unspecified how much of vec was actually written.
output1 (strm, el)
Io
if an error occurs. In that case, it is unspecified how much of el was actually written, especially if its physical representation is larger than just one byte. At this level, more than this cannot be guaranteed. Programs that need more control over this possibility need to make use of more primitive or OS-specific I/O routines.
flushOut strm
StreamIO.flushOut
. The function may block, and may raise the exception Io
when an error occurs.
closeOut strm
StreamIO.closeOut
. A write attempt on a closed outstream
will cause the exception Io
{cause=ClosedStream
,...}
to be raised. It may also raise Io
if another error occurs (e.g., buffers cannot be flushed out).
mkInstream strm
getInstream
.
getInstream strm
getInstream
, it is possible to get input directly from the underlying functional stream. After having done so, it may be necessary to reassign the newly obtained functional stream to strm using setInstream
; otherwise the previous input will be read again when reading from strm the next time.
setInstream (strm, strm')
mkOutstream strm
getOutstream strm
StreamIO
output stream. Using getOutstream
, it is possible to write output directly to the underlying stream, or to save it and restore it using setOutstream
after strm has been redirected.
setOutstream (strm, strm')
getPosOut strm
Io
if the stream does not support the operation, among other reasons. See StreamIO.getPosOut
.
setPosOut (strm, pos)
Io
if the stream does not support the operation, among other reasons. See StreamIO.setPosOut
.
BinIO
,ImperativeIO
,STREAM_IO
,TextIO
A word is in order concerning I/O nomenclature. We refer to the I/O provided by the IMPERATIVE_IO
signature as Imperative I/O, while the I/O provided by the STREAM_IO
signature is called Stream I/O. On the other hand, the type of buffered I/O handled by both of these layers is typically considered ``stream I/O,'' which explains why the I/O objects defined in both levels are called instream
and outstream
. To avoid confusion, we sometimes refer to I/O using the Stream I/O layer as functional, focusing on the functional flavor of the input streams at that level. This, however, glosses over the imperative nature of output at the same level. The principal distinction between the two layers is that I/O using IMPERATIVE_IO
can be redirected, while I/O using STREAM_IO
cannot.
The semantics of Imperative I/O operations are (almost) all defined in terms of the operations provided by the underlying STREAM_IO
substructure. Specifically, we have the reference implementations:
fun input(f) = let val (s,g) = StreamIO.input(getInstream f) in setInstream(f,g); s end fun inputAll(f)= let val (s,g) = StreamIO.inputAll(getInstream f) in setInstream(f,g); s end fun endOfStream(f)= StreamIO.endOfStream(getInstream f) fun output(f,s) = StreamIO.output(getOutStream f, s) fun flushOut(f) = StreamIO.flushOut(getOutStream f)with similar implementations for other imperative I/O operations.
Alternatively, we can consider Imperative I/O streams as ref
cells referring to STREAM_IO
streams:
type instream = StreamIO.instream ref type outstream = StreamIO.outstream ref fun input strm = let val (v, strm') = StreamIO.input(!strm) in strm := strm'; v end fun output (strm, v) = StreamIO.output(!strm, v)etc.
The one exception to the above approaches is input1
. If an implementation relies solely on StreamIO.input1
, input1
could never advance beyond an end-of-stream. To avoid this, a reference implementation for input1
would be:
fun input1 f = let val (s,g) = StreamIO.inputN(getInstream f, 1) in setInstream(f,g); if length s = 0 then NONE else SOME(sub(s,0)) end
Limited random access on input streams --- that is, returning to a previously scanned position --- can be accomplished using getInstream
and the underlying Stream I/O layer:
fun reread (f : instream, n : int) = let val g = getInstream(f) val s = inputN (f,n) in setInstream(f,g); (s, inputN (f,n)) endThe pair of vectors returned by
reread
will always be identical. Similarly limited random access on output streams can be done directly using getPosOut
and setPosOut
. More general random access is only available at the Primitive I/O level.
Implementation note:
Input on a closed stream behaves as though the stream is permanently at end-of-stream. Thus, in addition to closing the underlying functional stream, the
closeIn
function must also replace the functional stream with an empty stream.
Generated April 12, 2004
Last Modified January 29, 1997
Comments to John Reppy.
This document may be distributed freely over the internet as long as the copyright notice and license terms below are prominently displayed within every machine-readable copy.
Copyright © 2004 AT&T and Lucent Technologies. All rights reserved.
Permission is granted for internet users to make one paper copy for their
own personal use. Further hardcopy reproduction is strictly prohibited.
Permission to distribute the HTML document electronically on any medium
other than the internet must be requested from the copyright holders by
contacting the editors.
Printed versions of the SML Basis Manual are available from Cambridge
University Press.
To order, please visit
www.cup.org (North America) or
www.cup.cam.ac.uk (outside North America). |