A Wr.T
(or ``writer'') is a character output stream. The basic
operation on a writer is PutChar
, which extends a writer's
character sequence by one character. Some writers (called
``seekable writers'') also allow overwriting in the middle of the
sequence. For example, writers to random access files are
seekable, but writers to terminals and sequential files are not.
\index{character output stream}
\index{output stream}
\index{stream!output}
\index{writer}
Writers can be (and usually are) buffered. This means that operations on the writer don't immediately affect the underlying target of the writer, but are saved up and performed later. For example, a writer to a disk file is not likely to update the disk after each character.
Abstractly, a writer wr
consists of:
len(wr) a non-negative integer c(wr) a character sequence of length len(wr) cur(wr) an integer in the range [0..len(wr)] target(wr) a character sequence closed(wr) a boolean seekable(wr) a boolean buffered(wr) a booleanThese values are generally not directly represented in the data fields of a writer object, but in principle they determine the state of the writer.
The sequence c(wr)
is zero-based: c(wr)[i]
is valid for i
from 0 through len(wr)-1
. The value of cur(wr)
is the index of
the character in c(wr)
that will be replaced or appended by the
next call to PutChar
. If wr
is not seekable, then cur(wr)
is
always equal to len(wr)
, since in this case all writing happens
at the end.
The difference between c(wr)
and target(wr)
reflects the
buffering: if wr
is not buffered, then target(wr)
is updated to
equal c(wr)
after every operation; if wr
is buffered, then
updates to target(wr)
can be delayed. For example, in a writer
to a file, target(wr)
is the actual sequence of characters on the
disk; in a writer to a terminal, target(wr)
is the sequence of
characters that have actually been transmitted. (This sequence may
not exist in any data structure, but it still exists abstractly.)
If wr
is buffered, then the assignment target(wr) := c(wr)
can
happen asynchronously at any time, although the procedures in this
interface are atomic with respect to such assignments.
Every writer is a monitor; that is, it contains an internal lock
that is acquired and held for each operation in this interface, so
that concurrent operations will appear atomic. For faster,
unmonitored access, see the UnsafeWr
interface.
If you are implementing a long-lived writer class, such as a pipe or TCP stream, the index of the writer may eventually overflow, causing the program to crash with a bounds fault. We recommend that you provide an operation to reset the writer index, which the client can call periodically.
It is useful to specify the effect of several of the procedures in
this interface in terms of the action PutC(wr, ch)
, which outputs
the character ch
to the writer wr
:
PutC(wr, ch) = IF closed(wr) THEN Cause checked runtime error END; IF cur(wr) = len(wr) THEN Extend c(wr) by one character, incrementing len(wr) END; c(wr)[cur(wr)] := ch; INC(cur(wr));
PutC
is used only in specifying the interface; it is not a real
procedure.
Like PutC
, PutWC
is used to specify how wide characters are written.
Wide characters are written in little-endian order, the low-order
8-bits first, followed by the high-order 8-bits:
PutWC(wr, wch) = PutC(wr, VAL (Word.Extract (ORD (wch), 0, 8), CHAR)); PutC(wr, VAL (Word.Extract (ORD (wch), 8, 8), CHAR));
INTERFACESince there are many classes of writers, there are many ways that a writer can break---for example, the network can go down, the disk can fill up, etc. All problems of this sort are reported by raising the exceptionWr ; IMPORT AtomList, OSConfig; FROM Thread IMPORT Alerted; TYPE T <: ROOT; EXCEPTION Failure(AtomList.T);
Failure
. The documentation of each writer
class should specify what failures the class can raise and how they
are encoded in the argument to Failure
.
Illegal operations (for example, writing to a closed writer) cause checked runtime errors.
CONST EOL = OSConfig.LineSep;
End of line.
On POSIX,
EOL
is {\tt "\n"}; on Win32,
EOL
is {\tt "\r\n"}.
PROCEDURE PutChar(wr: T; ch: CHAR) RAISES {Failure, Alerted};
Outputch
towr
. More precisely, this is equivalent to:
PutC(wr, ch); IF NOT buffered(wr) THEN Flush(wr) ENDPROCEDURE PutWideChar(wr: T; ch: WIDECHAR) RAISES {Failure, Alerted};Outputch
towr
. More precisely, this is equivalent to:
PutWC(wr, ch); IF NOT buffered(wr) THEN Flush(wr) ENDMany operations on a writer can wait indefinitely. For example,
PutChar
can wait if the user has suspended output to his terminal. These waits can be alertable, so each procedure that might wait includesThread.Alerted
in its raises clause.PROCEDURE PutText(wr: T; t: TEXT) RAISES {Failure, Alerted};Outputt
towr
. More precisely, this is equivalent to:
FOR i := 0 TO Text.Length(t) - 1 DO PutC(wr, Text.GetChar(t, i)) END; IF NOT buffered(wr) THEN Flush(wr) ENDexcept that, like all operations in this interface, it is atomic with respect to other operations in the interface. (It would be wrong to writePutChar
instead ofPutC
, sincePutChar
always flushes if the writer is unbuffered.)PROCEDURE PutWideText(wr: T; t: TEXT) RAISES {Failure, Alerted};Outputt
towr
. More precisely, this is equivalent to:
FOR i := 0 TO Text.Length(t) - 1 DO PutWC(wr, Text.GetChar(t, i)) END; IF NOT buffered(wr) THEN Flush(wr) ENDPROCEDURE PutString(wr: T; READONLY a: ARRAY OF CHAR) RAISES {Failure, Alerted};Outputa
towr
. More precisely, other than the fact that this is atomic, it is equivalent to:
FOR i := FIRST(a) TO LAST(a) DO PutC(wr, a[i]) END; IF NOT buffered(wr) THEN Flush(wr) ENDPROCEDURE PutWideString(wr: T; READONLY a: ARRAY OF WIDECHAR) RAISES {Failure, Alerted};Outputa
towr
. More precisely, other than the fact that this is atomic, it is equivalent to:
FOR i := FIRST(a) TO LAST(a) DO PutWC(wr, a[i]) END; IF NOT buffered(wr) THEN Flush(wr) ENDPROCEDURE Seek(wr: T; n: CARDINAL) RAISES {Failure, Alerted};Set the current position ofwr
ton
. This is an error ifwr
is closed. More precisely, this is equivalent to:
IF wr.closed OR NOT seekable(wr) THEN Cause checked runtime error END; cur(wr) := MIN(n, len(wr))PROCEDURE Flush(wr: T) RAISES {Failure, Alerted};Perform all buffered operations. That is, settarget(wr) := c(wr)
. It is a checked runtime error ifwr
is closed.PROCEDURE Close(wr: T) RAISES {Failure, Alerted};Flushwr
, release any resources associated withwr
, and setclosed(wr) := TRUE
. The documentation for a procedure that creates a writer should specify what resources are released when the writer is closed. This leavesclosed(wr)
equal toTRUE
even if it raises an exception, and is a no-op ifwr
is closed.PROCEDURE Length(wr: T): CARDINAL RAISES {Failure, Alerted}; PROCEDURE Index(wr: T): CARDINAL RAISES {}; PROCEDURE Seekable(wr: T): BOOLEAN RAISES {}; PROCEDURE Closed(wr: T): BOOLEAN RAISES {}; PROCEDURE Buffered(wr: T): BOOLEAN RAISES {};These procedures returnlen(wr)
,cur(wr)
,seekable(wr)
,closed(wr)
, andbuffered(wr)
, respectively.Length
andIndex
cause a checked runtime error ifwr
is closed; the other three procedures do not.END Wr.