stable/src/StableLog.m3


Copyright (C) 1994, Digital Equipment Corp.
 Created by Carsten Weich                                    
                                                             

UNSAFE MODULE StableLog;

IMPORT Rd, Wr, WrClass, RdClass, Text, TextF,
       Thread, Pickle,
       StableError, RdUtils, Word;

REVEAL
  WrClass.Private <: MUTEX;
  RdClass.Private <: MUTEX;

<*FATAL Thread.Alerted*>

CONST
  CallMark     = 27353;  (* this is Gregs birthday... *)
  EndCallMark  = 30965;  (* ...Carstens birthday...*)

  Mtext        = ORD('t');
  Mpickle      = ORD('P');

PROCEDURE OutCall (log: Wr.T; procId: CARDINAL) =
  BEGIN
    OutInteger(log, CallMark);
    OutInteger(log, procId)
  END OutCall;

PROCEDURE OutCallEndMark (log: Wr.T) =
  BEGIN
    OutInteger(log, EndCallMark)
  END OutCallEndMark;

PROCEDURE InCall (log: Rd.T; max: CARDINAL): CARDINAL RAISES {Error} =
  BEGIN
    IF InInteger(log) # CallMark THEN RAISE Error END;
    VAR res:= InInteger(log); BEGIN
      IF res > max OR res < 0 THEN RAISE Error END;
      RETURN res
    END
  END InCall;

PROCEDURE CheckCallEndMark (log: Rd.T): BOOLEAN =
  BEGIN
    TRY
      RETURN EndCallMark = InInteger(log)
    EXCEPT
      Error => RETURN FALSE
    END
  END CheckCallEndMark;

PROCEDURE OutRef(log: Wr.T; r: REFANY) =
  BEGIN
    TYPECASE r OF
    | TEXT(x) => OutInteger(log, Mtext); OutText(log, x);
    ELSE
      TRY
        OutInteger(log, Mpickle);
        Pickle.Write(log, r)
      EXCEPT
        Wr.Failure (err) =>
        StableError.Halt(
            "Cannot write to logfile: " & RdUtils.FailureText(err))
      | Pickle.Error (msg) =>
        StableError.Halt("Cannot write to logfile: Pickle error: " & msg)
      END
    END
  END OutRef;

PROCEDURE InRef(log: Rd.T): REFANY RAISES {Error} =
  VAR r: REFANY;
      code:= InInteger(log);
  BEGIN
    IF code = Mpickle THEN
      TRY
        r:= Pickle.Read(log)
      EXCEPT
      | Pickle.Error, Rd.EndOfFile => RAISE Error
      | Rd.Failure (err) =>
        StableError.Halt(
            "Can not read log file: " & RdUtils.FailureText(err))
      END
    ELSIF code = Mtext THEN
      r:= InText(log)
    ELSE
      RAISE Error
    END;
    RETURN r
  END InRef;
Procedures for generic logging of procedure parameters

PROCEDURE OutChar (log: Wr.T; c: CHAR) =
  BEGIN
    TRY
      Wr.PutChar(log, c)
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutChar;

PROCEDURE OutChars (log: Wr.T; READONLY chars: ARRAY OF CHAR) =
  VAR n:= NUMBER(chars) - NUMBER(chars) MOD BYTESIZE(Word.T);
  BEGIN
    TRY
      Wr.PutString(log, SUBARRAY(chars, 0, n));
      FOR i:= n TO LAST(chars) DO
        Wr.PutChar(log, chars[i])
      END
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutChars;
OutInteger(log, i) skips to the next word aligned byte in log, and then stores i by loopholing. See AlignWr.
PROCEDURE OutInteger (log: Wr.T; i: INTEGER) =
  BEGIN
    LOCK log DO
      VAR
        ip := LOOPHOLE(AlignWr(log, BYTESIZE(INTEGER)),
                       UNTRACED REF INTEGER);
      BEGIN
        ip^ := i;
        INC(log.cur, BYTESIZE(INTEGER))
      END
    END (*LOCK*)
  END OutInteger;

PROCEDURE OutCardinal (log: Wr.T; card: CARDINAL) =
  BEGIN
    OutInteger(log, card)
  END OutCardinal;

PROCEDURE OutBoolean (log: Wr.T; bool: BOOLEAN) =
  BEGIN
    TRY
      Wr.PutChar(log, VAL(ORD(bool), CHAR))
    EXCEPT
      Wr.Failure (err) =>
        StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END OutBoolean;

PROCEDURE OutReal (log: Wr.T; r: REAL) =
  BEGIN
    LOCK log DO
      VAR
        rp := LOOPHOLE(AlignWr(log, BYTESIZE(REAL)),
                       UNTRACED REF REAL);
      BEGIN
        rp^ := r;
        INC(log.cur, BYTESIZE(REAL))
      END
    END
  END OutReal;

PROCEDURE OutLongreal (log: Wr.T; r: LONGREAL) =
  BEGIN
    LOCK log DO
      VAR
        rp := LOOPHOLE(AlignWr(log, BYTESIZE(LONGREAL)),
                       UNTRACED REF LONGREAL);
      BEGIN
        rp^ := r;
        INC(log.cur, BYTESIZE(LONGREAL))
      END
    END
  END OutLongreal;

PROCEDURE OutExtended (log: Wr.T; r: EXTENDED) =
  BEGIN
    LOCK log DO
      VAR
        rp := LOOPHOLE(AlignWr(log, BYTESIZE(EXTENDED)),
                       UNTRACED REF EXTENDED);
      BEGIN
        rp^ := r;
        INC(log.cur, BYTESIZE(EXTENDED))
      END
    END
  END OutExtended;

PROCEDURE OutText(log: Wr.T; text: TEXT) =
  VAR len: INTEGER;
  BEGIN
    IF text # NIL THEN
      len := Text.Length(text)
    ELSE
      len := -1
    END;
    OutInteger(log, len);
    IF len > 0 THEN OutChars(log, SUBARRAY(text^, 0, len)) END
  END OutText;

PROCEDURE AlignWr(wr: Wr.T; align: CARDINAL) : ADDRESS =
  VAR diff := wr.cur MOD align;
      res: ADDRESS;
  BEGIN
    TRY
      (* here we rely on the alignment invariants *)
      IF diff # 0 THEN INC(wr.cur, align-diff) END;
      IF wr.cur = wr.hi THEN wr.seek(wr.cur) END;
      res := ADR(wr.buff[wr.st + wr.cur - wr.lo]);
      RETURN res
    EXCEPT
      Wr.Failure (err) =>
        <*NOWARN*> StableError.Halt(
          "Cannot write to logfile: " & RdUtils.FailureText(err))
    END
  END AlignWr;
The following procedures are provided in support of generic reading the log.

PROCEDURE InChar (log: Rd.T): CHAR RAISES {Error} =
  BEGIN
    TRY
      RETURN Rd.GetChar(log)
    EXCEPT
      Rd.EndOfFile => RAISE Error
    | Rd.Failure (err) =>
        <*NOWARN*> StableError.Halt(
          "Can not read log file: " & RdUtils.FailureText(err))
    END
  END InChar;

PROCEDURE InCharsLen (log: Rd.T): CARDINAL RAISES {Error} =
  BEGIN
    RETURN InInteger(log)
  END InCharsLen;

PROCEDURE InChars (    log  : Rd.T;
                   VAR chars: ARRAY OF CHAR)
  RAISES {Error} =
  VAR n:= NUMBER(chars) - NUMBER(chars) MOD BYTESIZE(Word.T);
  BEGIN
    TRY
      IF Rd.GetSub(log, SUBARRAY(chars, 0, n)) # n THEN
        RAISE Error
      END;
      FOR i:= n TO LAST(chars) DO
        chars[i]:= Rd.GetChar(log)
      END
    EXCEPT
      Rd.EndOfFile => RAISE Error
    | Rd.Failure (err) =>
        StableError.Halt("Can not read log file: "
                           & RdUtils.FailureText(err))
    END
  END InChars;

PROCEDURE InInteger (log: Rd.T;
                     min         := FIRST(INTEGER);
                     max         := LAST(INTEGER)   ):
  INTEGER RAISES {Error} =
  BEGIN
    LOCK log DO
      VAR i: INTEGER;
      BEGIN
        i := LOOPHOLE(AlignRd(log, BYTESIZE(INTEGER)),
                      UNTRACED REF INTEGER)^;
        INC(log.cur, BYTESIZE(INTEGER));
        IF min <= i AND i <= max THEN
          RETURN i
        ELSE
          RAISE Error
        END (*IF*)
      END
    END (*LOCK*)
  END InInteger;

PROCEDURE InCardinal (log: Rd.T;
                      lim: CARDINAL := LAST(CARDINAL)):
  CARDINAL RAISES {Error} =
  BEGIN
    RETURN InInteger(log, 0, lim)
  END InCardinal;

PROCEDURE InBoolean (log: Rd.T): BOOLEAN RAISES {Error} =
  BEGIN
    TRY
      RETURN Rd.GetChar(log) = VAL(ORD(TRUE), CHAR)
    EXCEPT
      Rd.EndOfFile => RAISE Error
    | Rd.Failure (err) =>
        <*NOWARN*> StableError.Halt(
          "Can not read log file: " & RdUtils.FailureText(err))
    END
  END InBoolean;

PROCEDURE InReal (log: Rd.T): REAL RAISES {Error} =
  BEGIN
    LOCK log DO
      VAR r: REAL;
      BEGIN
        r := LOOPHOLE(AlignRd(log, BYTESIZE(REAL)),
                      UNTRACED REF REAL)^;
        INC(log.cur, BYTESIZE(REAL));
        RETURN r
      END
    END
  END InReal;

PROCEDURE InLongreal (log: Rd.T): LONGREAL RAISES {Error} =
  BEGIN
    LOCK log DO
      VAR r: LONGREAL;
      BEGIN
        r := LOOPHOLE(AlignRd(log, BYTESIZE(LONGREAL)),
                      UNTRACED REF LONGREAL)^;
        INC(log.cur, BYTESIZE(LONGREAL));
        RETURN r
      END
    END
  END InLongreal;

PROCEDURE InExtended (log: Rd.T): EXTENDED RAISES {Error} =
  BEGIN
    LOCK log DO
      VAR r: EXTENDED;
      BEGIN
        r := LOOPHOLE(AlignRd(log, BYTESIZE(EXTENDED)),
                      UNTRACED REF EXTENDED)^;
        INC(log.cur, BYTESIZE(EXTENDED));
        RETURN r
      END
    END
  END InExtended;

PROCEDURE InText(log: Rd.T) : TEXT
   RAISES {Error} =
  VAR len: INTEGER;
      text: TEXT;
  BEGIN
    len := InInteger(log);
    IF len = -1 THEN
      RETURN NIL
    ELSIF len < 0 THEN
      RAISE Error
    ELSE
      text := NEW(TEXT, len+1);
      InChars(log, SUBARRAY(text^, 0, len));
      text[len] := '\000';
      RETURN text
    END
  END InText;

PROCEDURE AlignRd(rd: Rd.T; nb: CARDINAL) : ADDRESS
    RAISES {Error} =
  VAR diff := rd.cur MOD nb;
      res: ADDRESS;
  BEGIN
    (* here we rely on the alignment invariants *)
    IF diff # 0 THEN
      VAR n := rd.cur + nb - diff; BEGIN
        IF n > rd.hi THEN RAISE Error END;
        rd.cur := n;
      END;
    END;
    IF rd.cur = rd.hi THEN
      TRY
        VAR sres:= rd.seek(rd.cur, FALSE); BEGIN
          IF sres = RdClass.SeekResult.Eof THEN
            RAISE Error
          END
        END
      EXCEPT
      | Rd.Failure (err) =>
        StableError.Halt(
            "Can not read log file: " & RdUtils.FailureText(err))
      END (*TRY*)
    END; (*IF*)
    res := ADR(rd.buff[rd.st + rd.cur - rd.lo]);
    RETURN res
  END AlignRd;

BEGIN
END StableLog.