(* Copyright (C) 1989, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* Last modified on Wed Nov 6 10:45:09 PST 1991 by kalsow *) (* modified on Fri Sep 28 23:12:34 1990 by muller *) (* The RdClass interface is analogous to the WrClass interface. It reveals that every reader contains a buffer of characters together with methods for managing the buffer. New reader classes are created by importing RdClass (to gain access to the buffer and the methods) and then defining a subclass of Rd.T whose methods provide the new class's behavior. The opaque type Private hides irrelevant details of the class-independent code. *) INTERFACE RdClass; IMPORT Rd; FROM Thread IMPORT Alerted; FROM Rd IMPORT Failure; TYPE Private <: ROOT; SeekResult = {Ready, WouldBlock, Eof}; REVEAL Rd.T = Private BRANDED "Rd.T" OBJECT buff: REF ARRAY OF CHAR; st: CARDINAL; (* index into buff *) lo, hi, cur: CARDINAL; (* indexes into src(rd) *) closed, seekable, intermittent: BOOLEAN; METHODS seek(dontBlock: BOOLEAN): SeekResult RAISES {Failure, Alerted}; length(): CARDINAL RAISES {Failure, Alerted} := LengthDefault; close() RAISES {Failure, Alerted} := CloseDefault; END; (* Let rd be a reader, abstractly given by len(rd), src(rd), cur(rd), avail(rd), closed(rd), seekable(rd), and intermittent(rd). The data fields cur, closed, seekable, and intermittent in the object represent the corresponding abstract attributes of rd. The buff, st, lo, and hi fields represent a buffer that contains part of src(rd), the rest of which is represented in some class-specific way. More precisely, we say that the state of the representation is valid if conditions V1 through V3 hold: V1. the characters of buff starting with st accurately reflect src. That is, for all i in [rd.lo .. rd.hi-1], rd.buff[rd.st + i - rd.lo] = src(rd)[i] V2. if the cur field is in range, it is up-to-date: cur(rd) = MIN(rd.cur, len(rd)) (This equation implies that rd.cur > len(rd) has the same meaning as rd.cur = len(rd). This convention allows the implementation to use ``lazy seeking''; that is, Rd.Seek can simply update rd.cur, without calling any class methods.) V3. the reader does not claim to be both intermittent and seekable: NOT (rd.intermittent AND rd.seekable) It is possible that buff = NIL in a valid state, since the range of i's in V1 may be empty; for example, in case lo = hi. There is no requirement that cur(rd) be anywhere near rd.lo or rd.hi in a valid state. If in fact cur(rd) lies between these values, we say the reader is ready. More precisely, rd is ready if: NOT rd.closed AND rd.buff # NIL AND rd.lo <= rd.cur < rd.hi If the state is ready, then Rd.GetChar can be implemented by fetching from the buffer. The class-independent code modifies rd.cur, but no other variables revealed in this interface. The class-independent code locks the reader before calling any methods. Here are the specifications for the methods: The basic purpose of the seek method is to make the reader ready. To seek to a position n, the class-independent code sets rd.cur := n; then if it is necessary to make the reader ready, it calls rd.seek. As in the case of writers, the seek method can be called even for an unseekable reader in the special case of advancing to the next buffer. There is a wrinkle to support the implementation of CharsReady. If rd is ready, the class-independent code can handle the call to CharsReady(rd) without calling any methods (since there is at least one character ready in the buffer), but if rd.cur = rd.hi, then the class independent code needs to find out from the class implementation whether any characters are ready in the next buffer. Using the seek method to advance to the next buffer won't do, since this could block, and CharsReady isn't supposed to block. Therefore, the seek method takes a boolean argument saying whether blocking is allowed. If blocking is forbidden and the next buffer isn't ready, the method returns the special value WouldBlock; this allows the class-independent code to return zero from CharsReady. More precisely, a valid state with rd.seekable or rd.cur = rd.hi, the effect of the call res := rd.seek(dontBlock) is to leave rd valid without changing the abstract state of rd. Furthermore, if res = Ready then rd is ready and cur(rd) = rd.cur; while if res = Eof, then cur(rd) = rd.cur = len(rd); and finally if res = WouldBlock then dontBlock was TRUE and avail(rd) = cur(rd). The length method returns the length of a non-intermittent reader. That is: Given a valid state in which rd.intermittent is FALSE, the call rd.length() returns len(rd) without changing the state of rd. The close method releases all resources associated with rd. The exact meaning of this is class-specific. When the method is called the state will be valid; validity is not required when the method returns (since after it returns, the class-independent code will set the closed bit in the reader, which makes the rest of the state irrelevant). The remainder of the interface is similar to the corresponding part of the WrClass interface: *) PROCEDURE Lock(rd: Rd.T) RAISES {}; (* The reader rd must be unlocked; lock it and make its state valid. *) PROCEDURE Unlock(rd: Rd.T) RAISES {}; (* The reader rd must be locked and valid; unlock it and restore the private invariant of the reader implementation. *) PROCEDURE LengthDefault(rd: Rd.T): CARDINAL RAISES {Failure, Alerted}; (* The procedure LengthDefault causes a checked runtime error; this represents an error in the (non-intermittent) class implementation. *) PROCEDURE CloseDefault(rd: Rd.T) RAISES {Failure, Alerted}; (* The procedure CloseDefault sets rd.buff to NIL. *) END RdClass.