%%% $Id: echo.oz,v 1.0 2002/11/05 12:26:00 dada Exp $
%%% http://dada.perl.it/shootout/
%%%
%%% contributed by Isaac Gouy

%%  Usage: start from command line with
%%     ozc -x echo.oz -o echo.oz.exe
%%     echo.oz.exe 100000

functor
import System Application Open OS

define

   Data = "Hello there sailor\n"
   

proc {ServerThread Sock SPort Bytes}
      Sock = {New Open.socket server(port:SPort)}
      {ServerLoop Sock 0 Bytes}
      {Sock shutDown(how: [receive send])}{Sock close}
end

proc {ServerLoop Sock Sum Bytes}
   local Message NewSum DR DW CR ST in

         %% low-level Selects seem ~6% faster total
      {Sock getDesc(DR DW)}{OS.readSelect DR}
      {OS.read DR 1024 Message nil CR}
         %% {Sock read(list: Message)} %% normal read

      if Message == nil then %% connection has been closed
     Bytes = Sum
      else
     NewSum = {Length Message} + Sum

     {OS.writeSelect DW}{OS.write DW Message ST}
         %% {Sock write(vs: Message)} %% normal write

     {ServerLoop Sock NewSum Bytes}
      end
   end
end


proc {ClientThread SPort N}
   local Sock in 
      Sock = {New Open.socket client(port:SPort)}
      {ClientLoop Sock N}
      {Sock shutDown(how: [receive send])}{Sock close}
   end
end

proc {ClientLoop Sock N}
   local Message DR DW CR ST in
      if N > 0 then

     {Sock getDesc(DR DW)}
     {OS.writeSelect DW}{OS.write DW Data ST}     
         {OS.readSelect DR}{OS.read DR 1024 Message nil CR}
     
     %% {Sock write(vs: Data)}     %% normal write
         %% {Sock read(list: Message)} %% normal read

     if Message == Data then {ClientLoop Sock N-1} end
      end
   end
end


in
   local Args A1 A2 A3 Socket SPort Bytes ArgList Pid in
      Args = {Application.getArgs plain}

      if {Length Args} == 1 then
         %% We are the server process      

         A3|nil = Args

         thread {ServerThread Socket SPort Bytes} end

         %% Prepare to fork an OS process for the client 
         %%    automatically close cmd.exe
         %%    start echo.oz.exe
         %%    pass a flag indicating the client process
         %%    pass the open server port SPort 
         %%    pass the number of times the client should send the data

         ArgList = ["/C" "echo.oz.exe" "client" {IntToString SPort} A3]
         Pid = {New Open.pipe init(cmd: "cmd.exe" args: ArgList)}

         %% Synchronize with server thread completion and indirectly with
         %% the client process. Wait here until the dataflow variable Bytes 
         %% has been given a value in the server thread. That happens after
         %% the client process closes the socket connection, when the client
         %% process has sent all it's data and received all the replies.
 
         {System.showInfo 'server processed '#{IntToString Bytes}#' bytes'} 

      elseif {Length Args} == 3 then 
         %% We are the client process

         %% Take the flag, server port, times-to-repeat from the args
         %% and use the main thread for the client
         A1|A2|A3|nil = Args
         if A1 == "client" then
            {ClientThread {StringToInt A2} {StringToInt A3}}
         end  
      end
   end
   {Application.exit 0}
end