// PupEFTPSend.bcpl -- by J. Shoch
// Copyright Xerox Corporation 1979

// Last modified June 3, 1979  12:45 PM by Taft

get "PupEFTP.decl"

external
[
// outgoing procedures
SendEFTPBlock; SendEFTPEnd

// incoming procedures
SendEFTPAbort
GetPBI; ReleasePBI; CompletePup
SetTimer; TimerHasExpired; Block
Dequeue; MultEq; Max; Min; MoveBlock
]


//----------------------------------------------------------------------------
let SendEFTPBlock(soc, addr, byteCnt, timeout) = valof
//----------------------------------------------------------------------------
[
let result = SendEFTPPacket(soc, addr, byteCnt, timeout, typeEFTPData)
if result ge 0 then soc>>EFTPSoc.SeqNum = soc>>EFTPSoc.SeqNum +1
resultis result
]


//----------------------------------------------------------------------------
and SendEFTPEnd(soc, timeout) = valof
//----------------------------------------------------------------------------
//The end sequence requires a complete ack for the first
// outgoing end, and then an extra end is sent, as part of the
// three way handshake.
[
if SendEFTPPacket(soc, 0, 0, timeout, typeEFTPEnd) ls 0 resultis false

//first end sent and acked ok. Send second End but don't wait for ack
soc>>EFTPSoc.SeqNum = soc>>EFTPSoc.SeqNum +1
SendEFTPPacket(soc, 0, 0, 0, typeEFTPEnd)
resultis true
]

//----------------------------------------------------------------------------
and SendEFTPPacket(soc, addr, dataBytes, timeout, type) = valof
//----------------------------------------------------------------------------
// This is a general routine, usable for data or end packets
// Will handle retransmissions here. Passes in a long timeout
// Returns the number of data bytes sent (>=0) if OK;
// Returns an error code (< 0) if there was an error
[
SetTimer(lv soc>>EFTPSoc.startTime, 0)  //remember start time
let longTimer = nil; SetTimer(lv longTimer, timeout)

   [ // repeat
   // construct the outgoing packet
   let pbi = GetPBI(soc)
   pbi>>PBI.pup.id↑2 = soc>>EFTPSoc.SeqNum
   MoveBlock(lv pbi>>PBI.pup.words↑1, addr, (dataBytes+1) rshift 1)
   CompletePup(pbi, type, dataBytes+pupOvBytes)
   let shortTimer = nil
   SetTimer(lv shortTimer, soc>>EFTPSoc.currentTimeout)

      [ // repeat
      Block()
      if timeout ne -1 & TimerHasExpired(lv longTimer) then
         resultis EFTPTimeout   //he is not answering now
      if TimerHasExpired(lv shortTimer) then
         [
         //exponentially age retransmission interval
         AgeTimeout(soc); break    //re-transmit
         ]
      pbi = Dequeue(lv soc>>EFTPSoc.iQ); if pbi eq 0 loop

      //Check the source of this packet
      unless MultEq(lv soc>>EFTPSoc.frnPort.socket,
       lv pbi>>PBI.pup.sPort.socket) do
         [ ReleasePBI(pbi); loop ]

      //Got a packet really for us from our partner
      AgeTimeout(soc)
      switchon pbi>>PBI.pup.type into
         [
         case typeEFTPAck:
            [
            if pbi>>PBI.pup.id↑2 eq soc>>EFTPSoc.SeqNum then
               [ ReleasePBI(pbi); resultis dataBytes ]
            if pbi>>PBI.pup.id↑2 gr soc>>EFTPSoc.SeqNum then
               [
               SendEFTPAbort(soc, OutOfSynchAbort,
                "Your receiver has gotten out of synch.", pbi)
               ReleasePBI(pbi)
               resultis EFTPAbortSent
               ]
            ReleasePBI(pbi)
            endcase
            ]
         case typeEFTPAbort:
            [
            if soc>>EFTPSoc.AbortPBI ne 0 then
               ReleasePBI(soc>>EFTPSoc.AbortPBI)
            soc>>EFTPSoc.AbortPBI = pbi
            resultis EFTPAbortReceived // bail out
            ]
         default: ReleasePBI(pbi)
         ]   
      ] repeat
   ] repeat
]

//----------------------------------------------------------------------------
and AgeTimeout(soc) be
//----------------------------------------------------------------------------
//update the adaptive timeout
// = 2 times the average response time, exponentially aged over
// the last 8 samples.
[
let t = nil; SetTimer(lv t, 0)
compiletest alto
   ifso [ t = (t-soc>>EFTPSoc.startTime) lshift 3 +4 ]
   ifnot [ t = (t-soc>>EFTPSoc.startTime) lshift 1 +1 ]
soc>>EFTPSoc.currentTimeout = (7*soc>>EFTPSoc.currentTimeout +
 Max(minTimeout, Min(maxTimeout, t))) rshift 3
]