-- File: DBFileAlpineImpl.mesa
-- Last edited by:
--   MBrown on April 22, 1983 3:03 pm
--   Kolling on April 27, 1983 12:37 pm
  DIRECTORY
    AlpineEnvironment
       USING[bytesPerPage, Conversation, FileID, nullFileID, OpenFileID, Outcome, PageCount,
          PageNumber, TransID, VolumeID, wordsPerPage],
    AlpFile
       USING[GetSize, Handle, Open, ReadPages, SetSize, WritePages, WriteProperties],
    AlpineInterimDirectory
       USING[Error, Open],
    AlpInstance
       USING[Create, Failed, Handle, Unknown],
    AlpTransaction
       USING[Create, Handle, Finish],
    DBEnvironment
       USING[Aborted, Error],
    DBCommon
       USING[VersionOptions],
    DBFileAlpine,
    DBStats
       USING[Starting, Stopping],
    FileIO
       USING[CreateOptions],
    RPC
       USING[CallFailed],
    Rope
       USING[ROPE, Text];
DBFileAlpineImpl: PROGRAM
  IMPORTS AlpF: AlpFile, AE: AlpineEnvironment, AlpineInterimDirectory, AlpI: AlpInstance,
     AlpT: AlpTransaction, DBEnvironment, DBStats, RPC
  EXPORTS DBFileAlpine
    
  = BEGIN
  
  ROPE: TYPE = Rope.ROPE;
  VersionOptions: TYPE = DBCommon.VersionOptions;
  VolumeID: TYPE = AE.VolumeID;
  FileID: TYPE = AE.FileID;
  nullFileID: FileID = AE.nullFileID;
  OpenFileID: TYPE = AE.OpenFileID;
  TransID: TYPE = AE.TransID;
  Conversation: TYPE = AE.Conversation;
  PageNumber: TYPE = AE.PageNumber;
  PageCount: TYPE = AE.PageCount;
  bytesPerPage: INT = AE.bytesPerPage;
  AlpineTrans: TYPE = REF ANY;
    -- must narrow to AlpTransaction.Handle
  AlpineOpenFileHandle: TYPE = REF ANY;
    -- must narrow to AlpFile.Handle
  CreateTransaction: PUBLIC PROC [server: ROPE] RETURNS [t: AlpineTrans] = {
    DBStats.Starting[AlpineFileCreateTransaction];
    DO
      instance: AlpI.Handle ← AlpI.Create[fileStore: server
         ! AlpI.Failed =>  IF why # authenticateFailed THEN LOOP];
      t ← AlpT.Create[instance
        ! RPC.CallFailed =>  IF why = unbound THEN LOOP];
     EXIT;
      ENDLOOP;
    DBStats.Stopping[AlpineFileCreateTransaction];
    RETURN[t]
    };
  FinishTransaction: PUBLIC PROC [t: AlpineTrans, abort: BOOL, continue: BOOL] = {
    outcome: AE.Outcome;
    transHandle: AlpT.Handle = NARROW[t];
    DBStats.Starting[AlpineFileFinishTransaction];
    outcome ← transHandle.Finish[
      requestedOutcome: IF abort THEN abort ELSE commit,
      continue: continue];
    IF NOT abort AND outcome = abort THEN ERROR DBEnvironment.Aborted[t];
    DBStats.Stopping[AlpineFileFinishTransaction];
    };
  CreateOptionsFromVersionOptions:
    ARRAY VersionOptions OF FileIO.CreateOptions =
      [NewFileOnly: newOnly, OldFileOnly: oldOnly, None: none];
  OpenFile: PUBLIC PROC [t: AlpineTrans, file: Rope.Text,
    version: VersionOptions, discardFileContents: BOOL, nPagesInitial: INT, noLog: BOOL]
    RETURNS [f: AlpineOpenFileHandle, createdFile: BOOL] = {
    ENABLE AlpI.Unknown => SELECT what FROM
      transID, openFileID => ERROR DBEnvironment.Aborted[t];
      ENDCASE => REJECT;
    volumeID: VolumeID; fileID: FileID;
    transHandle: AlpT.Handle = NARROW[t];
    fileHandle: AlpF.Handle;
    DBStats.Starting[AlpineFileOpen];
    [volumeID, fileID, createdFile] ← AlpineInterimDirectory.Open[
      file, CreateOptionsFromVersionOptions[version], nPagesInitial*bytesPerPage !
      AlpineInterimDirectory.Error =>
        SELECT why FROM
          illegalFileName => ERROR DBEnvironment.Error[IllegalFileName];
          ownerNotFound, fileNotFound => ERROR DBEnvironment.Error[FileNotFound];
          serverNotFound => ERROR DBEnvironment.Error[ServerNotFound];
          fileAlreadyExists => ERROR DBEnvironment.Error[AlreadyExists];
          lockFailed, transAborted => RETRY;
          serverBusy, remoteCallFailed, regServersUnavailable => REJECT;
          ENDCASE => REJECT]; -- DirectoryInconsistent {ownerRootFileNotFound}, Error[authenticateFailed, damaged, insufficientPermission, ownerRecordFull, quota}.
    { ENABLE AlpI.Unknown => SELECT what FROM
        transID, openFileID => ERROR DBEnvironment.Aborted[t];
        ENDCASE => REJECT;
      fileHandle ← AlpF.Open[transHandle, volumeID, fileID, $readWrite, [$write, $wait],
        IF noLog THEN $noLog ELSE $log, $random];
      IF NOT createdFile AND discardFileContents THEN
        fileHandle.WriteProperties[properties: LIST[[highWaterMark[highWaterMark: 0]]]];
      };
    DBStats.Stopping[AlpineFileOpen];
    RETURN [fileHandle, createdFile];
    };
  ReadFilePage: PUBLIC PROC [
    f: AlpineOpenFileHandle, p: CARDINAL, corePage: LONG POINTER] = {
    fileHandle: AlpF.Handle = NARROW[f];
    DBStats.Starting[AlpineFileReadPage];
    fileHandle.ReadPages[
      pageRun: [firstPage: p],
      pageBuffer: DESCRIPTOR [corePage, AE.wordsPerPage] !
        AlpI.Unknown => SELECT what FROM
          transID, openFileID => ERROR DBEnvironment.Aborted[fileHandle.trans];
          ENDCASE => REJECT];
    DBStats.Stopping[AlpineFileReadPage];
    };
  WriteFilePage: PUBLIC PROC [
    f: AlpineOpenFileHandle, p: CARDINAL, corePage: LONG POINTER] = {
    fileHandle: AlpF.Handle = NARROW[f];
    DBStats.Starting[AlpineFileWritePage];
    fileHandle.WritePages[
      pageRun: [firstPage: p],
      pageBuffer: DESCRIPTOR [corePage, AE.wordsPerPage] !
        AlpI.Unknown => SELECT what FROM
          transID, openFileID => ERROR DBEnvironment.Aborted[fileHandle.trans];
          ENDCASE => REJECT];
    DBStats.Stopping[AlpineFileWritePage];
    };
  GetSize: PUBLIC PROC [f: AlpineOpenFileHandle] RETURNS [nPages: CARDINAL] = {
    size: INT;
    fileHandle: AlpF.Handle = NARROW[f];
    DBStats.Starting[AlpineFileGetSize];
    size ← fileHandle.GetSize[ !
      AlpI.Unknown => SELECT what FROM
        transID, openFileID => ERROR DBEnvironment.Aborted[fileHandle.trans];
        ENDCASE => REJECT];
    DBStats.Stopping[AlpineFileGetSize];
    RETURN [size];
    };
  SetSize: PUBLIC PROC [f: AlpineOpenFileHandle, nPages: CARDINAL] = {
    fileHandle: AlpF.Handle = NARROW[f];
    DBStats.Starting[AlpineFileSetSize];
    { ENABLE AlpI.Unknown => SELECT what FROM
        transID, openFileID => ERROR DBEnvironment.Aborted[fileHandle.trans];
        ENDCASE => REJECT;
      fileHandle.SetSize[size: nPages];
      fileHandle.WriteProperties[properties: LIST[[byteLength[byteLength: nPages*bytesPerPage]]]];
      DBStats.Stopping[AlpineFileSetSize]
      };
    DBStats.Stopping[AlpineFileSetSize];
    };
  END.