#line 1220 "/home/travis/build/felix-lang/felix/src/packages/io.fdoc"
  
  class Faio_posix  {
  header faio_posixio_hpp = '#include "faio_posixio.hpp"';
  requires package "demux";
  requires package "faio";
  open C_hack;        // cast, address
  open Faio;
  open Pthread;
  open Demux;
  open Posix_headers;
  
  header sockety_h = '#include "demux_sockety.hpp"';  // my socket utils
  header '#include "faio_posixio.hpp"';
  
  fun sys_job_queue_qbound() => 20;
  fun sys_job_queue_nthreads() =>  4;
  val sys_job_queue = Pthread::mk_job_queue(#sys_job_queue_qbound,#sys_job_queue_nthreads);
  
  // ------------ core file and socket definitions ----------------
  typedef fd_t = PosixFileSystem::posix_file;
  
  // type of a socket
  type socket_t = "int";
  
  // a size type for use in some socket functions
  // stupid confused Unix standard!
  type socklen_t="socklen_t" requires sockety_h;
  ctor socklen_t : int = "$1";
  ctor int : socklen_t = "$1";
  
  // A socket address consists of
  // 1. a port number
  // 2. an address family indicator
  // 3. the encoded address, dependent on the family
  //
  // We deal only with Internet addresses IPv4 and IPv6,
  // indicator AF_INET and AF_INET6
  //
  // type of socket address protocol family
  type sa_family_t = "sa_family_t" requires sys_socket_h;
  fun ==: sa_family_t * sa_family_t -> bool = "$1==$2";
  
  type in_port_t = "in_port_t" requires netinet_in_h;
  
  const AF_INET : sa_family_t;
  const AF_INET6 : sa_family_t;
  
  // type to allocate on stack to hold any socket address for any protocol
  // required for stack allocations
  type sockaddr_storage_t = "struct sockaddr_storage" requires sockety_h;
  fun ss_family : &sockaddr_storage_t -> sa_family_t = "$1->ss_family";
  
  // type of a socket address
  type sockaddr_t = "struct sockaddr" requires sockety_h;
  fun sa_family : &sockaddr_t -> sa_family_t = "$1->sa_family";
  
  // cast socket address storage object pointer to socket address pointer
  fun sockaddr_p : &sockaddr_storage_t -> &sockaddr_t = "(struct sockaddr*)$1";
  axiom inet_family(ss: &sockaddr_storage_t) : ss_family ss == sa_family (sockaddr_p ss);
  
  // --------------------------------------------------------------
  // IPv4
  // type containing IPv4 internet address
  type in_addr_t = "in_addr_t" requires netinet_in_h; // an integer
  type struct_in_addr = "struct in_addr";
  fun s_addr: struct_in_addr -> in_addr_t = "$1.s_addr";
  
  // type containing encoded port and IPv4 address
  type sockaddr_in_t = "struct sockaddr_in" requires sockety_h;
  fun sin_family: sockaddr_in_t -> sa_family_t= "$1.sin_family";
  fun sin_port : sockaddr_in_t -> in_port_t= "$1.sin_port";
  fun sin_addr : sockaddr_in_t -> struct_in_addr = "$1.sin_addr";
  fun sin_addr : &sockaddr_in_t -> &struct_in_addr = "&($1->sin_addr)";
  
  
  // --------------------------------------------------------------
  // IPv6
  // type containing IPv6 internet address
  type struct_in6_addr = "struct in6_addr";
  typedef ipv6_addr = uint8^16;
  fun s6_addr: struct_in6_addr -> &ipv6_addr = "$1.s6_addr";
  
  // type containing encoded socket address for IPv6
  type sockaddr_in6_t = "struct sockaddr_in6" requires sockety_h;
  fun sin6_family: sockaddr_in6_t -> sa_family_t= "$1.sin6_family";
  fun sin6_port : sockaddr_in6_t -> in_port_t = "$1.sin6_port";
  fun sin6_addr : sockaddr_in6_t -> struct_in6_addr = "$1.sin6_addr";
  fun sin6_addr : &sockaddr_in6_t -> &struct_in6_addr = "&($1->sin6_addr)";
  
  
  // convert Internet address to display format.
  // $1: Address family
  // $2: pointer to the address
  // $3: pointer to output buffer
  // $4: length of output buffer
  fun inet_ntop: sa_family_t * address * +char * socklen_t -> +char requires arpa_inet_h;;
  const INET_ADDRSTRLEN : socklen_t requires arpa_inet_h;
  const INET6_ADDRSTRLEN : socklen_t requires arpa_inet_h;
  
  // --------------------------------------------------------------
  
  instance Str[FileSystem::posix_file] {
    fun str: FileSystem::posix_file -> string = "::flx::rtl::strutil::str<int>($1)" requires package "flx_strutil";
  }
  
  instance Str[socket_t] {
    fun str: socket_t -> string = "::flx::rtl::strutil::str<int>($1)" requires package "flx_strutil";
  }
  
  fun getpeername: socket_t * &sockaddr_t * &socklen_t -> int;
  
  fun getpeername (s: socket_t) : string =
  {
    // store for encoded IP address
    var sa:sockaddr_storage_t;
    var paddr : &sockaddr_t = sockaddr_p &sa; // cast
  
    // length of encoded IP address
    var nsa = C_hack::cast[socklen_t] sizeof[sockaddr_storage_t];
  
    // get encoded peer address
    var res = getpeername (s,  paddr, &nsa);
    if res == -1 return "";
  
    var p = C_hack::cast[+char] null[char];
    var ips = "";
    var family = ss_family &sa;
    match family with
    | $(AF_INET) =>
      begin
        var buffer = C_hack::cast[+char] (C_hack::malloc INET_ADDRSTRLEN.int);
        // cast to IPv4 socket address
        var inet_sockaddr = C_hack::cast[&sockaddr_in_t] paddr;
        // extract pointer to IPv4 internet address
        var p_ipnumber : &struct_in_addr = inet_sockaddr.sin_addr;
        p = inet_ntop
          (
            family,
            C_hack::cast[address] p_ipnumber,
            buffer,
            INET_ADDRSTRLEN
          )
        ;
        if not p.isNULL do ips = str p; done
        C_hack::free (C_hack::cast[address] buffer);
      end
  
    | $(AF_INET6) =>
      begin
        var buffer = C_hack::cast[+char] (C_hack::malloc INET6_ADDRSTRLEN.int);
        // cast to IPv6 socket address
        var inet6_sockaddr = C_hack::cast[&sockaddr_in6_t] paddr;
        // extract IPv6 internet address (address of a byte array)
        var p_ip6number : &struct_in6_addr = inet6_sockaddr.sin6_addr;
        p = inet_ntop
          (
            family,
            C_hack::cast[address] p_ip6number,
            buffer,
            INET6_ADDRSTRLEN
          )
        ;
        if not p.isNULL do ips = str p; done
        C_hack::free (C_hack::cast[address] buffer);
      end
  
    | _ => ;
    endmatch
    ;
    return ips;
  
  }
  
  proc close: socket_t = 'close($1);' requires Posix_headers::unistd_h;
  proc shutdown: socket_t*int = 'shutdown($a);' requires Posix_headers::sys_socket_h;
  fun bad_socket : socket_t -> bool = "$1 == -1";
  
  // non blocking
  /*
  gen aio_ropen: string -> FileSystem::posix_file = 'open($1.c_str(), O_RDONLY | O_NONBLOCK)'
      requires fcntl_h, sys_stat_h;
  gen aio_wopen: string -> FileSystem::posix_file = ' open($1.c_str(), O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR)'
      requires fcntl_h, sys_stat_h;
  gen aio_rwopen: string -> FileSystem::posix_file = ' open($1.c_str(), O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR)'
      requires fcntl_h, sys_stat_h;
  gen aio_creat: string * posix_permissions-> FileSystem::posix_file = ' open($1.c_str(), O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, $2)'
      requires fcntl_h, sys_stat_h;
  */
  
  // socketio_request should be renamed to be async_fd_request
  type socketio_request = "::flx::faio::socketio_request";
  
  gen mk_socketio_request: demuxer * socket_t*address*int*bool -> socketio_request
      = '::flx::faio::socketio_request($1, $2, (char*)$3, $4, $5)';
  
  fun get_pb: socketio_request -> sel_param_ptr = '&$1.sv.pb';
  
  // read & write differ only by a flag
  proc async_rw(fd: socket_t, len: &int, buf: address, eof: &bool, read_flag: bool)
  {
      //println$ "faio/flx_faoi_posix.flx: async_rw (s,"+str (*len)+",buf,"+str(*eof)+", "+str read_flag+") calling mk_socketio_req ..";
      var asyncb = mk_socketio_request(sys_demux,fd, buf, *len, read_flag);
      faio_req$ &asyncb;
      //println$ "faio/flx_faoi_posix.flx: async_rw ("+ str fd+", "+str (*len)+",buf,"+str(*eof)+", "+str read_flag+") calculating eof ..";
  
      calc_eof(asyncb.get_pb, len, eof);
      //println$ "faio/flx_faoi_posix.flx: async_rw (s,"+str (*len)+",buf,"+str(*eof)+", "+str read_flag+") called mk_socketio_req ..";
  }
  
  proc async_read(fd: socket_t, len: &int, buf: address,
      eof: &bool)
  {
      async_rw(fd, len, buf, eof, true);      // read
  }
  
  proc async_write(fd: socket_t, len: &int, buf: address, eof: &bool)
  {
      //println$ "faio/flx_faoi_posix.flx: async_write(s,"+str (*len)+",buf,"+str(*eof)+" calling async_rw ..";
      async_rw(fd, len, buf, eof, false);     // write
      //println$ "faio/flx_faoi_posix.flx: async_write(s,"+str (*len)+",buf,"+str(*eof)+" call async_rw ..";
  }
  
  type flxfileio_request = "::flx::faio::flxfileio_request";
  
  // connect!
  type async_connect = '::flx::faio::connect_request';
  
  fun mk_async_connect: demuxer * +char *int-> async_connect = '::flx::faio::connect_request($a)';
  fun get_socket: async_connect -> socket_t = '$1.s';
  fun get_err: async_connect -> int = '$1.socket_err';
  
  // could do multi connects for capable drivers
  proc connect(s: &socket_t, addr: +char, port: int, err: &int)
  {
      var ac = mk_async_connect(sys_demux,addr, port);
      faio_req$ &ac;
      *err = ac.get_err;
      *s = ac.get_socket;
  }
  
  type accept_request = "::flx::faio::accept_request";
  
  fun mk_accept: demuxer * socket_t -> accept_request = '::flx::faio::accept_request($1,$2)';
  fun get_socket: accept_request -> socket_t = '$1.accepted';
  
  // arg1 = returned socket, arg2 is port, pass 0 to have one assigned
  proc mk_listener: &socket_t* &int *int
      = '*$1 = ::flx::demux::create_async_listener($2, $3);' requires sockety_h;
  
  proc accept(s: &socket_t, listener: socket_t)
  {
      var acc = mk_accept$ sys_demux,listener;
      faio_req$ &acc;
      *s = acc.get_socket;
  }
  
  // ASYNC FILE IO
  
  // offset ? let it be for a moment
  fun mk_faio: job_queue * FileSystem::posix_file *address*int*int*bool -> flxfileio_request
      = '::flx::faio::flxfileio_request($1,$2, (char*)$3, $4, $5, $6)';
  fun get_pb: flxfileio_request -> sel_param_ptr = '&$1.pb';
  
  proc faio_rw(q:job_queue, fd: FileSystem::posix_file, len: &int, buf: address, eof: &bool, read_flag: bool)
  {
      // constant offset for now, rushing to get this in stream
      var faio = mk_faio(q, fd, buf, *len, 0, read_flag);
      faio_req$ &faio;
      //print$ f"faio_rw: request %d, actual %d\n" (*len,faio.pb.bytes_done);
      calc_eof(faio.get_pb, len, eof);
  }
  proc faio_read(fd: FileSystem::posix_file, len: &int, buf: address,
      eof: &bool)
  {
  println$ "faio_posix::faio_read " + str (*len);
      faio_rw(sys_job_queue, fd, len, buf, eof, true);       // read
  println$ "faio_posix::faio_read, len="+str (*len) + ", eof=" + str (*eof);
  }
  
  proc faio_write(fd: FileSystem::posix_file, len: &int, buf: address, eof: &bool)
  {
      faio_rw(sys_job_queue, fd, len, buf, eof, false);      // write
  }
  
  } // class faio_posix