-- XTransportTCPImpl.mesa

DIRECTORY
  ArpaRouter,
  Environment,
  Heap,
  TcpStream,
  XConnection,
  XTransport,
  XTransportTCP;
  
XTransportTCPImpl: PROGRAM IMPORTS Heap, TcpStream EXPORTS XTransportTCP =
  BEGIN
  
  xReq:TYPE = MACHINE DEPENDENT RECORD [
                                  reqType:[0..256),
				  data:[0..256),
				  length:CARDINAL
				  ];
				  
  xTcpPortNumber:PUBLIC ArpaRouter.Port = LOOPHOLE[6000];
  zone:UNCOUNTED ZONE = Heap.systemZone;

  Listener:PUBLIC PROCEDURE =
    BEGIN
    handle:TcpStream.Handle;
    h:XTransport.Handle;
    DO
      handle ← TcpStream.Listen[localPort:xTcpPortNumber, listenTimeout:LAST[LONG CARDINAL]];
      h ← zone.NEW[XTransport.Handle ← [ClientListener, Close, Destroy, Write, LOOPHOLE[handle, LONG CARDINAL]]];
      
      XConnection.EnQueueConnection[h];
      ENDLOOP;
    END;

  ClientListener:PRIVATE PROCEDURE[h:Handle, client:LONG CARDINAL, swapped:BOOLEAN] =
    BEGIN
    handle:TcpStream.Handle ← LOOPHOLE[h.priv, TcpStream.Handle];
    req:xReq;
    len:CARDINAL;
    data:XQueue.Data;
    n:CARDINAL;
    status:TcpStream.CompletionCode;
    Process.Detach[Process.GetCurrent[]];
    DO
      block:Environment.Block;
      block ← [LOOPHOLE[LONG[@req],LONG POINTER TO UNSPECIFIED],0,SIZE[xReq]*2];
      WHILE block.startIndex # block.stopIndexPlusOne DO
        [n,status] ← handle.get[block!
	                     TcpStream.Suspended => GOTO closing;
			     TcpStream.Closed => GOTO closing;
			     ];
	IF status = closing THEN
	  GOTO closing;
	block.startIndex ← block.startIndex + n;
	ENDLOOP;
--      IF swapped THEN
--        len ← Inline.BITSHIFT[Inline.LowByte[req.length],8] + Inline.HighByte[req.length]
--      ELSE
        len ← req.length * 2;
--     len ← ( len * 2 ) - ( SIZE[xReq] );
      data ← Heap.MakeNode[zone,len];
      Inline.LongCOPY[from:@req,nwords:SIZE[xReq],to:data];
      block ← [data,SIZE[xReq]*2,len*2];
      WHILE block.startIndex # block.stopIndexPlusOne DO
        [n,status] ← handle.get[block!
	                     TcpStream.Suspended => GOTO closing;
			     TcpStream.Closed => GOTO closing;
			     ];
	IF status = closing THEN
	  GOTO closing;
	block.startIndex ← block.startIndex + n;
	ENDLOOP;
      XConnection.EnQueueRequest[client,data,len*2];
    ENDLOOP;
    EXITS
      closing =>
        BEGIN 
          XConnection.ClosingConnection[client]; 
	END;
    END;
    
  Close:PRIVATE PROCEDURE[h:Handle] =
    BEGIN
      handle:TcpStream.Handle ← LOOPHOLE[h.priv, TcpStream.Handle];
      handle.close[!TcpStream.Closed => CONTINUE;
		    TcpStream.Suspended => CONTINUE];
    END;
    
  Destroy:PRIVATE PROCEDURE[h:Handle] =
    BEGIN
      handle:TcpStream.Handle ← LOOPHOLE[h.priv, TcpStream.Handle];
      handle.destroy[handle];
    END;
    
  Write:PRIVATE PROCEDURE[h:Handle, client:LONG CARDINAL, data:Environment.Block] =
    BEGIN
      handle:TcpStream.Handle ← LOOPHOLE[h.priv, TcpStream.Handle];
      pad:PACKED ARRAY [0..4) OF Environment.Byte ← [0,0,0,0];
      handle.put[data, TRUE, FALSE!
           TcpStream.Suspended => GOTO noconnect];
      IF ( ( data.stopIndexPlusOne - data.startIndex ) MOD 4 ) > 0 THEN
        handle.put[
	   [@pad, 0, 4 - ( ( data.stopIndexPlusOne - data.startIndex ) MOD 4 )], 
	   TRUE, FALSE!TcpStream.Suspended => GOTO noconnect];

    EXITS
      noconnect => 
        BEGIN
	XConnection.ClosingConnection[client];
	END;
    END;
    
  END.