(* Copyright (C) 1989, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* Last modified on Thu Apr 23 09:36:20 PDT 1992 by kalsow *) (* modified on Tue Apr 21 15:56:06 PDT 1992 by muller *) (* This module is very similar to the WrMove module, so we will list its code with only a few comments. *) UNSAFE MODULE RdMove EXPORTS Rd, RdClass, UnsafeRd; IMPORT Thread; FROM Thread IMPORT Alerted; REVEAL Private = Thread.Mutex BRANDED "RdMove.Private" OBJECT next, stop := -1; END; (* The implementation of Rd.Seek is lazy. When a client calls Rd.Seek with an index that does not lie within the buffer, Rd.Seek simply records the destination index in rd.cur and sets both rd.next and rd.stop to NIL. When rd.next # NIL, the Rd implementation ignores the value of rd.cur in determining cur(rd), but when rd.next = NIL the Rd implementation uses the value of rd.cur. A reader rd is "clean" if the following conditions hold (see the WrRep module for more explanation): C1. If rd.next # rd.stop, then Ready(rd) AND (rd.next = ADR(rd.buff[rd.st + cur(rd) - rd.lo]) C2. If rd.next # rd.stop, then rd.stop = ADR(rd.buff[rd.st + (rd.hi - 1) - rd.lo]) + ADRSIZE(CHAR) C3. The validity conditions V1 and V3 hold for rd. C4. If rd.next # NIL then rd.next = ADR(rd.buff[rd.st + cur(rd) - rd.lo]) C5. If rd.next = NIL, then rd is valid. *) <*INLINE*> PROCEDURE MakeValid(rd: T) RAISES {} = (* rd locked and clean *) BEGIN IF rd.next # -1 THEN rd.cur := rd.lo + rd.next - rd.st; END END MakeValid; (* rd is locked, clean, and valid. Furthermore, if rd.next#NIL, then rd.cur=cur(rd); this is important for the implementation of Index. *) PROCEDURE MakeClean(rd: T) RAISES {} = BEGIN IF (rd.lo <= rd.cur) AND (rd.cur < rd.hi) AND (rd.buff # NIL) THEN rd.next := rd.st + rd.cur - rd.lo; rd.stop := rd.st + rd.hi - rd.lo; IF rd.stop < rd.next THEN Error() END; ELSE rd.stop := -1; rd.next := -1 END END MakeClean; PROCEDURE SlowGetChar(rd: T): CHAR RAISES {EndOfFile, Failure, Alerted} = (* rd is locked and clean; rd.next = rd.stop *) VAR res: CHAR; BEGIN IF rd.closed THEN Error() END; TRY MakeValid(rd); IF rd.seek(dontBlock := FALSE) = SeekResult.Eof THEN RAISE EndOfFile END FINALLY MakeClean(rd) END; IF rd.next = rd.stop THEN Error() END; res := rd.buff[rd.next]; INC(rd.next); RETURN res END SlowGetChar; <*INLINE*> PROCEDURE GetChar(rd: T): CHAR RAISES {EndOfFile, Failure, Alerted} = (* rd is unlocked *) BEGIN LOCK rd DO RETURN FastGetChar(rd) END END GetChar; <*INLINE*> PROCEDURE FastGetChar(rd: T): CHAR RAISES {EndOfFile, Failure, Alerted} = (* rd is locked *) VAR res: CHAR; BEGIN IF rd.next # rd.stop THEN res := rd.buff[rd.next]; INC(rd.next); ELSE res := SlowGetChar(rd) END; RETURN res END FastGetChar; <*INLINE*> PROCEDURE EOF(rd: T): BOOLEAN RAISES {Failure, Alerted} = (* rd is unlocked *) BEGIN LOCK rd DO RETURN FastEOF(rd) END END EOF; <*INLINE*> PROCEDURE FastEOF(rd: T): BOOLEAN RAISES {Failure, Alerted} = (* rd is locked *) BEGIN IF rd.next # rd.stop THEN RETURN FALSE ELSE RETURN SlowEOF(rd) END END FastEOF; PROCEDURE SlowEOF(rd: T): BOOLEAN RAISES {Failure, Alerted} = (* rd is locked; rd.next = rd.stop *) BEGIN IF rd.closed THEN Error() END; MakeValid(rd); TRY RETURN rd.seek(dontBlock := FALSE) = SeekResult.Eof FINALLY MakeClean(rd) END END SlowEOF; PROCEDURE UnGetChar(rd: T) RAISES {} = BEGIN LOCK rd DO FastUnGetChar (rd) END; END UnGetChar; PROCEDURE FastUnGetChar(rd: T) RAISES {} = BEGIN IF rd.closed THEN Error() END; IF (rd.next = -1) OR (rd.next = rd.st) THEN Error() END; DEC(rd.next) END FastUnGetChar; PROCEDURE CharsReady(rd: T): CARDINAL RAISES {Failure} = <*FATAL Thread.Alerted*> BEGIN LOCK rd DO IF rd.closed THEN Error() END; MakeValid(rd); IF NOT (rd.lo <= rd.cur AND rd.cur < rd.hi) THEN TRY IF rd.seek(dontBlock := TRUE) = SeekResult.Eof THEN RETURN 1 END FINALLY MakeClean(rd) END; IF rd.cur > rd.hi THEN Error() END; END; RETURN rd.hi - rd.cur; END END CharsReady; PROCEDURE Index(rd: T): CARDINAL RAISES {} = <*FATAL Failure, Alerted*> BEGIN LOCK rd DO IF rd.closed THEN Error() END; MakeValid(rd); IF rd.seekable AND (rd.next = -1) THEN rd.cur := MIN(rd.cur, rd.length()) END; RETURN rd.cur END END Index; PROCEDURE Length(rd: T): CARDINAL RAISES {Failure, Alerted} = BEGIN LOCK rd DO IF rd.closed OR rd.intermittent THEN Error() END; TRY MakeValid(rd); RETURN rd.length() FINALLY MakeClean(rd) END END END Length; PROCEDURE Seek(rd: T; n: CARDINAL) RAISES {Failure, Alerted} = BEGIN LOCK rd DO IF rd.closed OR NOT rd.seekable THEN Error() END; rd.cur := n; MakeClean(rd) END END Seek; PROCEDURE Close(rd: T) RAISES {Failure, Alerted} = BEGIN LOCK rd DO FastClose (rd); END; END Close; PROCEDURE FastClose(rd: T) RAISES {Failure, Alerted} = BEGIN IF NOT rd.closed THEN TRY MakeValid(rd); rd.close() FINALLY rd.closed := TRUE; rd.next := -1; rd.stop := -1; END END END FastClose; PROCEDURE Lock (rd: T) RAISES {} = BEGIN Thread.Acquire (rd); MakeValid (rd); END Lock; PROCEDURE Unlock (rd: T) RAISES {} = BEGIN MakeClean (rd); Thread.Release (rd); END Unlock; PROCEDURE LengthDefault (<*UNUSED*> rd: T): CARDINAL RAISES {} = BEGIN Error (); <*ASSERT FALSE*> END LengthDefault; PROCEDURE CloseDefault(rd: T) RAISES {} = BEGIN rd.buff := NIL; END CloseDefault; PROCEDURE Error () RAISES {} = <*FATAL Failure*> BEGIN RAISE Failure(NIL); (* force a checked runtime error *) END Error; BEGIN END RdMove.