Object <- MacServer |
|
|
NewStreamNear |
Class MacServer defines no new attributes.
Fields
No fields are defined by MacServer or any of its superclasses.
Instance template
instance MacServer tag; end instance;
/* A 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.
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 boolean`
These 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.
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. */
// intrinsic Length(wr: T): CARDINAL RAISES {Failure, Alerted};
// intrinsic Index(wr: T): CARDINAL RAISES {};
// intrinsic Seekable(wr: T): BOOLEAN RAISES {};
// intrinsic Closed(wr: T): BOOLEAN RAISES {};
// intrinsic Buffered(wr: T): BOOLEAN RAISES {};
/* These procedures return "len(wr)", "cur(wr)", "seekable(wr)",
"closed(wr)", and "buffered(wr)", respectively. "Length" and
"Index" cause a checked runtime error if "wr" is closed; the other
three procedures do not. */
/* Let wr be a writer, which abstractly is given by c(wr), target(wr),
cur(wr), closed(wr), seekable(wr), buffered(wr). The actual
representation of wr is an object of type Wr.T. The wr.cur,
wr.closed, wr.seekable, and wr.buffered fields in the object represent
the corresponding abstract attributes of wr. The wr.buff, wr.st,
wr.lo, and wr.hi fields in the object represent a buffer containing
the unflushed part of c(wr). The target of the writer is represented
in some class-specific way, which is not specified by this interface.
More precisely, we say that the state of the writer object wr
is valid if the following conditions V1 through V5 hold:
V1. the cur field and the booleans are correct:
wr.cur = cur(wr) AND wr.closed = closed(wr) AND
wr.buffered = buffered(wr) AND wr.seekable = seekable(wr)
V2. the indexes of any unflushed characters are in the range
[lo..cur-1]. That is, for all i not in [wr.lo..wr.cur-1],
c(wr)[i] = target(wr)[i]
V3. the (possibly) unflushed characters are stored in buff starting
with buff[st]. That is, for all i in [wr.lo..wr.cur-1],
c(wr)[i] = wr.buff[wr.st + i - wr.lo]
(Usually st is zero. Non-zero values may be useful to satisfy buffer
alignment constraints.)
V4. the current position is either contained in the buffer, or just
past the buffer:
wr.lo <= wr.cur <= wr.hi
It is possible that buff = NIL in a valid state, since the range of
i's in V3 can be empty; for example, in the case lo = hi = cur.
V5. if closed(wr) then wr.buff = NIL AND wr.lo = wr.hi
We say that the state is ready if the buffer contains the current
position; that is, if
NOT wr.closed AND wr.buff # NIL AND wr.lo <= cur(wr) < wr.hi
If the state is ready, then Wr.PutChar can be implemented by storing
into the buffer. The class-independent code does exactly this, until
the buffer is full, at which point it calls a class method to consume
the buffer and provide a new one. Together V4 and V5 imply
that if wr.cur # wr.hi then wr.buff # NIL and NOT wr.closed. Therefore
a valid writer is ready if "wr.cur # wr.hi".
In general, the class-independent code modifies cur and buff[i] for i
in the range [st..st+(hi-1)-lo], but not the buff reference itself,
st, lo, or hi (except that "Wr.Close" modifies "wr.lo" and "wr.cur" and
sets "wr.buff" to NIL in order to maintain invariant V5). */
Define Class MacServer;
inherits from Object;
operation NewStreamNear(nearThis: Object): Stream;
// create a new stream object
end class;