#line 637 "/home/travis/build/felix-lang/felix/src/packages/io.fdoc"
  
  class Stream {
    requires package "demux";
    requires package "faio";
  
    open Faio;
  
    if PLAT_POSIX do
      open Faio_posix;
      typedef fd_t = FileSystem::posix_file;
    else
      open Faio_win32;
      typedef fd_t = Faio_win32::fd_t;
    done
  
    // ---------------------------------------------------------------------------
  
    publish "The interface for a readable stream of bytes."
    class IByteStream[T] {
      publish "Read N bytes from the stream into the address."
      virtual proc read: T * &int * address * &bool;
    }
  
    publish "The interface for a writable stream of bytes."
    class OByteStream[T] {
      publish "Write N bytes from the address into the stream."
      virtual proc write: T * &int * address * &bool;
    }
  
    publish "The interface for a readable and writable stream of bytes."
    class IOByteStream[T] {
      inherit IByteStream[T];
      inherit OByteStream[T];
    }
  
    publish "A readable stream that can have it's read channel closed."
    class TerminalIByteStream[T] {
      inherit IByteStream[T];
  
      publish "Close the input stream."
      virtual proc iclose: T;
    }
  
    publish "A writable stream that can have it's write channel closed."
    class TerminalOByteStream[T] {
      inherit OByteStream[T];
  
      publish "Close the output stream."
      virtual proc oclose: T;
    }
  
    publish "A writable stream that can have it's channels closed."
    class TerminalIOByteStream[T] {
      inherit TerminalIByteStream[T];
      inherit TerminalOByteStream[T];
  
      publish "Close the stream."
      virtual proc ioclose: T;
    }
  
    // ---------------------------------------------------------------------------
  
    union devnull_t = DEVNULL;
  
    publish "devnull_t"
    instance IByteStream[devnull_t]
    {
      proc read(strm: devnull_t, len: &int, buf: address, eof: &bool) {
        *len = 0;
        *eof = true;
      }
    }
  
    instance OByteStream[devnull_t]
    {
      proc write(strm: devnull_t, len: &int, buf: address, eof: &bool) {
        *eof = false;
      }
    }
  
    instance IOByteStream[devnull_t] {}
    instance TerminalIByteStream[devnull_t] { proc iclose (x:devnull_t) {} }
    instance TerminalOByteStream[devnull_t] { proc oclose (x:devnull_t) {} }
    instance TerminalIOByteStream[devnull_t] { proc ioclose (x:devnull_t) {} }
  
    // ---------------------------------------------------------------------------
  
    publish "fd_t -- native file handle (disk file)"
    instance IByteStream[fd_t]
    {
      proc read(fd: fd_t, len: &int, buf: address, eof: &bool) {
        if PLAT_POSIX do
          faio_read(fd, len, buf, eof);
        else
          ReadFile(fd, len, buf, eof);
        done
      }
    }
  
    instance OByteStream[fd_t]
    {
      proc write(fd: fd_t, len: &int, buf: address, eof: &bool) {
        if PLAT_POSIX do
          faio_write(fd, len, buf, eof);
        else
          WriteFile(fd, len, buf, eof);
        done
      }
    }
  
    instance IOByteStream[fd_t] {}
  
    instance TerminalIByteStream[fd_t]
    {
      proc iclose (fd: fd_t) {
        if PLAT_POSIX do
          C_hack::ignore(FileSystem::close fd);
        else
          CloseFile fd;
        done
      }
    }
  
    instance TerminalOByteStream[fd_t]
    {
      proc oclose (fd: fd_t) {
        if PLAT_POSIX do
          C_hack::ignore(FileSystem::close fd);
        else
          CloseFile fd;
        done
      }
    }
  
    instance TerminalIOByteStream[fd_t]
    {
      proc ioclose (fd: fd_t) {
        if PLAT_POSIX do
          C_hack::ignore(FileSystem::close fd);
        else
          CloseFile fd;
        done
      }
    }
  
    // ---------------------------------------------------------------------------
  
    publish "Read the input stream to the output stream."
    proc cat[istr,ostr with IByteStream[istr], OByteStream[ostr]] (
      istream: istr,
      ostream: ostr,
      buf: address,
      bufsize: int)
    {
      var reof = false;
      var weof = false;
      var len: int;
  
      // if we finish input, stop. if output eofs, don't keep hammering on it!
      while not reof and not weof do
        len = bufsize;
        read (istream, &len, buf, &reof);
        write(ostream, &len, buf, &weof);
      done
    }
  
    publish "Read the input stream to the output stream."
    proc cat[istr,ostr with IByteStream[istr], OByteStream[ostr]] (
      istream: istr,
      ostream: ostr)
    {
      val BUFSIZE = 100000;
      var buf = C_hack::malloc(BUFSIZE);
  
      // that's some nice error checking
      cat (istream, ostream, buf, BUFSIZE);
  
      C_hack::free (buf);
    }
  
    publish "Read all the input streams to the output stream."
    proc cat[istr,ostr with IByteStream[istr], OByteStream[ostr]] (
      istreams: list[istr],
      ostream: ostr,
      buf: address,
      bufsize: int)
    {
      List::iter (proc (istream:istr) {
        cat (istream, ostream, buf, bufsize);
      }) istreams;
    }
  
    publish "Compare the results of two streams."
    proc stream_cmp[istr1,istr2 with IByteStream[istr1], IByteStream[istr2]] (
      stream1: istr1,
      stream2: istr2,
      buf1: address,
      buf2: address,
      bufsize: int,
      sign: &int)
    {
      var eof1 = false;
      var eof2 = false;
      var len1: int;
      var len2: int;
      var terminated = false;
      var cmp = 0;
  
      while cmp == 0 and not terminated do
        len1 = bufsize; read(stream1, &len1, buf1, &eof1);
        len2 = bufsize; read(stream2, &len2, buf2, &eof2);
  
        len := min(len1, len2);
  
        // It's very unfortunate that memcmp doesn't return the position of the
        // first non-equality
        cmp = Carray::memcmp(buf1, buf2, size len);
  
        if cmp == 0 do
          cmp = len1 - len2;
          if cmp == 0 do
            terminated = eof1 and eof2;
            cmp =
              // ugg: false = case 0, true = case 1
              match eof1, eof2 with
              | case 1, case 1 => 0
              | case 0, case 0 => 0
              | case 0, case 1 => 1
              | case 1, case 0 => -1
              endmatch
            ;
          done
        done
      done
  
      *sign = cmp;
    }
  
  
    publish "Compare the results of two streams."
    proc cmp[istr1, istr2 with IByteStream[istr1], IByteStream[istr2]] (
      istream1: istr1,
      istream2: istr2,
      res: &int)
    {
      val BUFSIZE = 100000;
      var buf1 = C_hack::malloc(BUFSIZE);
      var buf2 = C_hack::malloc(BUFSIZE);
      stream_cmp(istream1, istream2, buf1, buf2, BUFSIZE, res);
      C_hack::free(buf1);
      C_hack::free(buf2);
    }
  
    publish "Read the results of a stream back into it's stream."
    proc echo[iostr with IOByteStream[iostr]] (
      iostream: iostr,
      buf: address,
      bufsize: int)
    {
      // echo a = cat a a. that's deep, man.
      cat(iostream, iostream, buf, bufsize);
    }
  
    publish "Read in from a stream and write to two streams."
    proc tee[istr,ostr with IByteStream[istr], OByteStream[ostr]] (
      istream: istr,
      ostream1: ostr,
      ostream2: ostr)
    {
      var reof  = false;
      var weof1 = false;
      var weof2 = false;
      var len: int;
  
      val BUFSIZE = 10*1024;
      var buf = C_hack::malloc(BUFSIZE);
  
      // don't hammer!
      while not reof and not weof1 and not weof2 do
        len = BUFSIZE;
        read  (istream,  &len, buf, &reof);
        write (ostream1, &len, buf, &weof1);
        write (ostream2, &len, buf, &weof2);
      done
  
      C_hack::free buf;
    }
  
    // highly inefficient!
    noinline proc get_line[istr with IByteStream[istr]] (
      istream: istr,
      s: &string)
    {
  //println$ "get_line starts";
      var c: char;
      val ac = address (&c);
      var st: string="";
      var finished = false;
  
      while not finished do
        var len = 1;
        var eof: bool;
  
  //println$ "read 1 byte";
        read(istream, &len, ac, &eof);
  //println$ if eof then "EOF" else "not EOF" endif;
  //println$ "Char = " + str(ord c) + "='"+str c+"'";
        if eof or c == char '\n' do
          finished = true;
        else
          st += c;
        done
      done
      *s = st;  // pass back result
    }
  
    proc write_string[ostr with OByteStream[ostr]] (
      ostream: ostr,
      var s: string,
      eof: &bool)
    {
      var slen = s.len.int;
      var a = C_hack::cast[address]$ cstr s;
      write(ostream, &slen, a, eof);
    }
  } // class Stream