-- File: PupByteStreams.mesa,  Last Edit:
-- HGM  January 5, 1980  4:13 PM
-- MAS  August 8, 1980  2:20 PM

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  ByteBltDefs: FROM "ByteBltDefs" USING [ByteBlt],
  Stream: FROM "Stream" USING [
    Byte, Block, CompletionCode, defaultInputOptions,
    Handle, InputOptions, LongBlock, Object, ShortBlock,
    SSTChange, SubSequenceType, TimeOut],
  CommUtilDefs: FROM "CommUtilDefs" USING [SelfDestruct],
  DriverDefs: FROM "DriverDefs" USING [doDebug, Glitch],
  PupPktDefs: FROM "PupPktDefs" USING [PupPktStream, PupPktStreamDestroy, PupPktStreamMake],
  PupStream: FROM "PupStream" USING [PupOpenMode],
  PupDefs: FROM "PupDefs" USING [
    GetFreePupBuffer, ReturnFreePupBuffer, GetPupContentsBytes, SetPupContentsBytes,
    Pair, PupAddress, PupBuffer, PupSocketID, Tocks],
  PupTypes: FROM "PupTypes" USING [fillInSocketID];

PupByteStreams: MONITOR
  IMPORTS ByteBltDefs, Stream, CommUtilDefs, DriverDefs, PupPktDefs, PupDefs
  EXPORTS PupStream =
BEGIN OPEN DriverDefs, PupDefs;

myPupByteStream: Stream.Object ← [
  options: Stream.defaultInputOptions,
  get: GetBlock,
  put: PutBlock,
  setSST: SendMark,
  sendAttention: SendAttention,
  waitAttention: WaitAttention,
  delete: Destroy ];

myPs: PupPktDefs.PupPktStream;

inputBuffer: PupBuffer ← NIL;
inputFinger: CARDINAL;
inputSST: Stream.SubSequenceType ← 0;
outputBuffer: PupBuffer ← NIL;
outputFinger: CARDINAL ← 0;  -- 0 if aData/aMark sent
outputBufferSize: CARDINAL ← 0;

HandleLooksLikeGarbage: PUBLIC ERROR = CODE;


PupByteStreamCreate: PUBLIC PROCEDURE [remote: PupAddress, ticks: Tocks]
  RETURNS [Stream.Handle] =
  BEGIN
  RETURN[PupByteStreamMake[PupTypes.fillInSocketID,remote,ticks,sendRfc,[0,0]]];
  END;

PupByteStreamMake: PUBLIC PROCEDURE [
    local: PupSocketID, remote: PupAddress, ticks: Tocks,
    mode: PupStream.PupOpenMode, id: Pair]
  RETURNS [Stream.Handle] =
  BEGIN
  newPs: PupPktDefs.PupPktStream;
  him: POINTER TO FRAME[PupByteStreams];
  -- It is ok to UNWIND here if PupPktStreamMake doesn't work.
  newPs ← PupPktDefs.PupPktStreamMake[local,remote,ticks,mode,id];
  him ← NEW PupByteStreams;
  START him;  -- Do initialization code
  him.myPs ← newPs;
  RETURN[@him.myPupByteStream];
  END;

Destroy: PUBLIC --ENTRY-- PROCEDURE [bs: Stream.Handle] =  
  BEGIN ENABLE UNWIND => NULL;
  IF doDebug AND bs.delete#Destroy THEN Glitch[HandleLooksLikeGarbage];
  bs.delete ← LOOPHOLE[3];
  PupPktDefs.PupPktStreamDestroy[myPs];
  IF inputBuffer#NIL THEN ReturnBuffer[@inputBuffer];
  IF outputBuffer#NIL THEN ReturnBuffer[@outputBuffer];
  CommUtilDefs.SelfDestruct[];
  END;


SendAttention: PROCEDURE [sH: Stream.Handle, byte: Stream.Byte] =
  BEGIN
  myPs.sendAttention[];
  END;

WaitAttention: PROCEDURE [sH: Stream.Handle] RETURNS [Stream.Byte] =
  BEGIN
  myPs.waitForAttention[];
  RETURN[0];
  END;

GetBlock: --ENTRY-- PROCEDURE[sH: Stream.Handle, block: Stream.Block, options: Stream.InputOptions] RETURNS[bytesTransferred: CARDINAL, why: Stream.CompletionCode, sst: Stream.SubSequenceType] =
  -- NB: block has been passed by VALUE, so we are upadting our copy, not the clients
  BEGIN ENABLE UNWIND => NULL;
  input: Stream.Block;
  moved: CARDINAL;
  bytesTransferred ← 0;
  sst ← inputSST;
  why ← normal;
  WHILE block.startIndex<block.stopIndexPlusOne DO
    UNTIL inputBuffer#NIL DO
      inputFinger ← 0;
      inputBuffer ← myPs.get[];
      IF inputBuffer=NIL THEN SIGNAL Stream.TimeOut[block.startIndex]
      ELSE IF inputBuffer.pupType=mark OR inputBuffer.pupType=aMark THEN
        BEGIN
        sst ← inputSST ← inputBuffer.pupBytes[0];
        ReturnBuffer[@inputBuffer];
        IF options.signalSSTChange THEN
          SIGNAL Stream.SSTChange[inputSST,block.startIndex]
        ELSE
          BEGIN
          why ← sstChange;
          RETURN;
          END;
        END;
      ENDLOOP;
    input ← [
      blockPointer: @inputBuffer.pupBytes,
      startIndex: inputFinger,
      stopIndexPlusOne: GetPupContentsBytes[inputBuffer] ];
    moved ← ByteBltDefs.ByteBlt[block,input];
    bytesTransferred ← bytesTransferred+moved;
    block.startIndex ← block.startIndex+moved;
    inputFinger ← inputFinger+moved;
    IF inputFinger=input.stopIndexPlusOne THEN
      BEGIN
      ReturnBuffer[@inputBuffer];
      END;
    IF inputBuffer=NIL AND block.startIndex<block.stopIndexPlusOne
    AND options.signalLongBlock THEN
      BEGIN
      SIGNAL Stream.LongBlock[block.startIndex];
      END;
    IF inputBuffer=NIL AND options.terminateOnEndPhysicalRecord THEN
      BEGIN
      why ← endRecord;
      EXIT;
      END;
    ENDLOOP;
  IF inputBuffer#NIL AND options.signalShortBlock THEN
    BEGIN
    ERROR Stream.ShortBlock;
    END;
  END;

PutBlock: --ENTRY-- PROCEDURE [sH: Stream.Handle, block: Stream.Block, endPhysicalRecord: BOOLEAN] =
  -- NB: Block has been passed by VALUE, so we are updating our copy, not the clients
  BEGIN ENABLE UNWIND => NULL;
  output: Stream.Block;
  moved: CARDINAL;
  IF outputBufferSize=0 THEN outputBufferSize←myPs.getSenderSizeLimit[];
  WHILE block.startIndex<block.stopIndexPlusOne DO
    IF outputFinger=outputBufferSize THEN FlushOutputBuffer[];
    IF outputBuffer=NIL THEN
      BEGIN
      outputBuffer ← GetFreePupBuffer[];
      outputFinger ← 0;
      END;
    output ← [
      blockPointer: @outputBuffer.pupBytes,
      startIndex: outputFinger,
      stopIndexPlusOne: outputBufferSize ];
    moved ← ByteBltDefs.ByteBlt[output,block];
    block.startIndex ← block.startIndex+moved;
    outputFinger ← outputFinger+moved;
    ENDLOOP;
  IF (endPhysicalRecord AND outputFinger#0) OR outputFinger=outputBufferSize THEN
    BEGIN
    IF outputBuffer=NIL THEN 
      BEGIN
      outputBuffer ← GetFreePupBuffer[];
      outputFinger ← 0;
      END;
    IF endPhysicalRecord THEN outputBuffer.pupType ← aData;
    FlushOutputBuffer[];
    IF endPhysicalRecord THEN outputFinger ← 0;
    END;
  END;

SendMark: --ENTRY-- PROCEDURE [sH: Stream.Handle, sst: Stream.SubSequenceType] =
  BEGIN ENABLE UNWIND => NULL;
  FlushOutputBuffer[];
  outputFinger ← 0;
  myPs.putMark[sst];
  END;

FlushOutputBuffer: PROCEDURE =
  BEGIN
  b: PupBuffer;  -- don't leave outputBuffer dangling in case of StreamClosing
  IF outputBuffer=NIL THEN RETURN;
  b ← outputBuffer; outputBuffer ← NIL;
  SetPupContentsBytes[b,outputFinger];
  myPs.put[b];
  END;

ReturnBuffer: PROCEDURE [p: POINTER TO PupBuffer] =
  BEGIN
  b: PupBuffer;
  b ← p↑;
  p↑ ← NIL;
  ReturnFreePupBuffer[b];
  END;


END.