* Eio backend using Linux 's io_uring . 

     You will normally not use this module directly . 
     Instead , use { ! Eio_main.run } to start an event loop and then use the API in the { ! Eio } module . 

     However , it is possible to use this module directly if you only want to support recent versions of Linux .

    You will normally not use this module directly.
    Instead, use {!Eio_main.run} to start an event loop and then use the API in the {!Eio} module.

    However, it is possible to use this module directly if you only want to support recent versions of Linux. *)


  * Copyright ( C ) 2020 - 2021 Anil Madhavapeddy 
  * 
  * Permission to use , copy , modify , and distribute this software for any 
  * purpose with or without fee is hereby granted , provided that the above 
  * copyright notice and this permission notice appear in all copies . 
  * 
  * THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
  * MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
  * ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
  * WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN 
  * ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF 
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE . 
 
 * Copyright (C) 2020-2021 Anil Madhavapeddy
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *)

open Eio.Std

(** Wrap [Unix.file_descr] to track whether it has been closed. *)
module FD : sig
  type t
  (** Either a [Unix.file_descr] or nothing (if closed) .*)

  val is_open : t -> bool
  (** [is_open t] is [true] if {!close} hasn't been called yet. *)

  val close : t -> unit
  (** [close t] closes [t].
      @raise Invalid_arg if [t] is already closed. *)

  val of_unix : sw:Switch.t -> seekable:bool -> close_unix:bool -> Unix.file_descr -> t
* [ let t = of_unix ~sw ~seekable ~close_unix fd ] wraps [ fd ] as an open file descriptor . 
       This is unsafe if [ fd ] is closed directly ( before or after wrapping it ) . 
       @param sw [ t ] is closed when [ sw ] is released , if not closed manually first . 
       @param close_unix If [ true ] , closing [ t ] also closes [ fd ] . 
                         If [ false ] , the caller is responsible for closing [ fd ] , 
                         which must not happen until after [ t ] is closed . 
       @param seekable If true , we pass [ -1 ] to io_uring as the " file offset " , to use the current offset . 
                       If false , pass [ 0 ] as the file offset , which is needed for sockets .
      This is unsafe if [fd] is closed directly (before or after wrapping it).
      @param sw [t] is closed when [sw] is released, if not closed manually first.
      @param close_unix If [true], closing [t] also closes [fd].
                        If [false], the caller is responsible for closing [fd],
                        which must not happen until after [t] is closed.
      @param seekable If true, we pass [-1] to io_uring as the "file offset", to use the current offset.
                      If false, pass [0] as the file offset, which is needed for sockets. *)

  val to_unix : [< `Peek | `Take] -> t -> Unix.file_descr
* [ to_unix op t ] returns the wrapped descriptor . 
       This allows unsafe access to the FD . 
       If [ op ] is [ ` Take ] then [ t ] is marked as closed ( but the underlying FD is not actually closed ) . 
       @raise Invalid_arg if [ t ] is closed .
      This allows unsafe access to the FD.
      If [op] is [`Take] then [t] is marked as closed (but the underlying FD is not actually closed).
      @raise Invalid_arg if [t] is closed. *)
end

(** {1 Eio API} *)

type has_fd = < fd : FD.t >
type source = < Eio.Flow.source; Eio.Flow.close; has_fd >
type sink   = < Eio.Flow.sink  ; Eio.Flow.close; has_fd >

type stdenv = <
  stdin  : source;
  stdout : sink;
  stderr : sink;
  net : Eio.Net.t;
  domain_mgr : Eio.Domain_manager.t;
  clock : Eio.Time.clock;
  fs : Eio.Fs.dir Eio.Path.t;
  cwd : Eio.Fs.dir Eio.Path.t;
  secure_random : Eio.Flow.source;
  debug : Eio.Debug.t;
>

val get_fd : <has_fd; ..> -> FD.t
val get_fd_opt : #Eio.Generic.t -> FD.t option

val pipe : Switch.t -> source * sink
(** [pipe sw] is a source-sink pair [(r, w)], where data written to [w] can be read from [r].
    It is implemented as a Unix pipe. *)

(** {1 Main Loop} *)

val run :
  ?queue_depth:int ->
  ?n_blocks:int ->
  ?block_size:int ->
  ?polling_timeout:int ->
  ?fallback:([`Msg of string] -> 'a) ->
  (stdenv -> 'a) -> 'a
* Run an event loop using io_uring . 

     Uses { ! Uring.create } to create the io_uring , 
     and { ! Uring.set_fixed_buffer } to set a [ block_size * n_blocks ] fixed buffer . 

     Note that if Linux resource limits prevent the requested fixed buffer from being allocated 
     then [ run ] will continue without one ( and log a warning ) . 

     For portable code , you should use { ! Eio_main.run } instead , which will use this automatically 
     if running on Linux with a recent - enough kernel version . 

     @param fallback Call this instead if io_uring is not available for some reason . 
                     The argument is a message describing the problem ( for logging ) . 
                     The default simply raises an exception .

    Uses {!Uring.create} to create the io_uring,
    and {!Uring.set_fixed_buffer} to set a [block_size * n_blocks] fixed buffer.

    Note that if Linux resource limits prevent the requested fixed buffer from being allocated
    then [run] will continue without one (and log a warning).

    For portable code, you should use {!Eio_main.run} instead, which will use this automatically
    if running on Linux with a recent-enough kernel version.

    @param fallback Call this instead if io_uring is not available for some reason.
                    The argument is a message describing the problem (for logging).
                    The default simply raises an exception. *)

* { 1 Low - level API }

(** Low-level API for using uring directly. *)
module Low_level : sig
  val noop : unit -> unit
  (** [noop ()] performs a uring noop. This is only useful for benchmarking. *)

  (** {1 Time functions} *)

  val sleep_until : float -> unit
  (** [sleep_until time] blocks until the current time is [time]. *)

* { 1 Fixed - buffer memory allocation functions } 

       The size of the fixed buffer is set when calling { ! run } , which attempts to allocate a fixed buffer . 
       However , that may fail due to resource limits .

      The size of the fixed buffer is set when calling {!run}, which attempts to allocate a fixed buffer.
      However, that may fail due to resource limits. *)

  val alloc_fixed : unit -> Uring.Region.chunk option
  (** Allocate a chunk of memory from the fixed buffer.

      Warning: The memory is NOT zeroed out.

      Passing such memory to Linux can be faster than using normal memory, in certain cases.
      There is a limited amount of such memory, and this will return [None] if none is available at present. *)

  val alloc_fixed_or_wait : unit -> Uring.Region.chunk
  (** Like {!alloc_fixed}, but if there are no chunks available then it waits until one is. *)

  val free_fixed : Uring.Region.chunk -> unit

  val with_chunk : fallback:(unit -> 'a) -> (Uring.Region.chunk -> 'a) -> 'a
  (** [with_chunk ~fallback fn] runs [fn chunk] with a freshly allocated chunk and then frees it.

      If no chunks are available, it runs [fallback ()] instead. *)

  (** {1 File manipulation functions} *)

  val openfile : sw:Switch.t -> string -> Unix.open_flag list -> int -> FD.t
  (** Like {!Unix.open_file}. *)

  val openat2 :
    sw:Switch.t ->
    ?seekable:bool ->
    access:[`R|`W|`RW] ->
    flags:Uring.Open_flags.t ->
    perm:Unix.file_perm ->
    resolve:Uring.Resolve.t ->
    ?dir:FD.t -> string -> FD.t
* [    ~perm ~resolve  path ] opens [ dir / path ] . 

       See { ! Uring.openat2 } for details .

      See {!Uring.openat2} for details. *)

  val read_upto : ?file_offset:Optint.Int63.t -> FD.t -> Uring.Region.chunk -> int -> int
  (** [read_upto fd chunk len] reads at most [len] bytes from [fd],
      returning as soon as some data is available.

      @param file_offset Read from the given position in [fd] (default: 0).
      @raise End_of_file Raised if all data has already been read. *)

  val read_exactly : ?file_offset:Optint.Int63.t -> FD.t -> Uring.Region.chunk -> int -> unit
  (** [read_exactly fd chunk len] reads exactly [len] bytes from [fd],
      performing multiple read operations if necessary.

      @param file_offset Read from the given position in [fd] (default: 0).
      @raise End_of_file Raised if the stream ends before [len] bytes have been read. *)

  val readv : ?file_offset:Optint.Int63.t -> FD.t -> Cstruct.t list -> int
  (** [readv] is like {!read_upto} but can read into any cstruct(s),
      not just chunks of the pre-shared buffer.

      If multiple buffers are given, they are filled in order. *)

  val write : ?file_offset:Optint.Int63.t -> FD.t -> Uring.Region.chunk -> int -> unit
* [ write fd buf len ] writes exactly [ len ] bytes from [ buf ] to [ fd ] . 

       It blocks until the OS confirms the write is done , 
       and resubmits automatically if the OS does n't write all of it at once .

      It blocks until the OS confirms the write is done,
      and resubmits automatically if the OS doesn't write all of it at once. *)

  val writev : ?file_offset:Optint.Int63.t -> FD.t -> Cstruct.t list -> unit
  (** [writev] is like {!write} but can write from any cstruct(s),
      not just chunks of the pre-shared buffer.

      If multiple buffers are given, they are sent in order.
      It will make multiple OS calls if the OS doesn't write all of it at once. *)

  val writev_single : ?file_offset:Optint.Int63.t -> FD.t -> Cstruct.t list -> int
  (** [writev_single] is like [writev] but only performs a single write operation.
      It returns the number of bytes written, which may be smaller than the requested amount. *)

  val splice : FD.t -> dst:FD.t -> len:int -> int
* [ splice src ~dst ~len ] attempts to copy up to [ len ] bytes of data from [ src ] to [ dst ] . 

       @return The number of bytes copied . 
       @raise End_of_file [ src ] is at the end of the file . 
       @raise Unix . Unix_error(EINVAL , " splice " , _ ) if splice is not supported for these FDs .

      @return The number of bytes copied.
      @raise End_of_file [src] is at the end of the file.
      @raise Unix.Unix_error(EINVAL, "splice", _) if splice is not supported for these FDs. *)

  val connect : FD.t -> Unix.sockaddr -> unit
  (** [connect fd addr] attempts to connect socket [fd] to [addr]. *)

  val await_readable : FD.t -> unit
  (** [await_readable fd] blocks until [fd] is readable (or has an error). *)

  val await_writable : FD.t -> unit
  (** [await_writable fd] blocks until [fd] is writable (or has an error). *)

  val fstat : FD.t -> Unix.stats
  (** Like {!Unix.fstat}. *)

  val read_dir : FD.t -> string list
* [ read_dir dir ] reads all directory entries from [ dir ] . 
       The entries are not returned in any particular order 
       ( not even necessarily the order in which Linux returns them ) .
      The entries are not returned in any particular order
      (not even necessarily the order in which Linux returns them). *)

  (** {1 Sockets} *)

  val accept : sw:Switch.t -> FD.t -> (FD.t * Unix.sockaddr)
  (** [accept ~sw t] blocks until a new connection is received on listening socket [t].

      It returns the new connection and the address of the connecting peer.
      The new connection has the close-on-exec flag set automatically.
      The new connection is attached to [sw] and will be closed when that finishes, if
      not already closed manually by then. *)

  val shutdown : FD.t -> Unix.shutdown_command -> unit
* Like { !  } .

  val send_msg : FD.t -> ?fds:FD.t list -> ?dst:Unix.sockaddr -> Cstruct.t list -> unit
* [ send_msg socket bufs ] is like [    ] , but also allows setting the destination address 
       ( for unconnected sockets ) and attaching FDs ( for Unix - domain sockets ) .
      (for unconnected sockets) and attaching FDs (for Unix-domain sockets). *)

  val recv_msg : FD.t -> Cstruct.t list -> Uring.Sockaddr.t * int
  (** [recv_msg socket bufs] is like [readv socket bufs] but also returns the address of the sender. *)

  val recv_msg_with_fds : sw:Switch.t -> max_fds:int -> FD.t -> Cstruct.t list -> Uring.Sockaddr.t * int * FD.t list
* [ recv_msg_with_fds ] is like [ recv_msg ] but also allows receiving up to [ max_fds ] file descriptors 
       ( sent using SCM_RIGHTS over a Unix domain socket ) .
      (sent using SCM_RIGHTS over a Unix domain socket). *)

  (** {1 Randomness} *)

  val getrandom : Cstruct.t -> int
* [ getrandom buf ] reads some random bytes into [ buf ] and returns the number of bytes written . 

      It uses Linux 's [ getrandom ] call , which is like reading from /dev / urandom 
      except that it will block ( the whole domain ) if used at early boot 
      when the random system has n't been initialised yet .

     It uses Linux's [getrandom] call, which is like reading from /dev/urandom
     except that it will block (the whole domain) if used at early boot
     when the random system hasn't been initialised yet. *)

* { 1 DNS functions }

  val getaddrinfo : service:string -> string -> Eio.Net.Sockaddr.t list
* [  host ] returns a list of IP addresses for [ host ] . [ host ] is either a domain name or 
       an ipaddress .
      an ipaddress. *)

end

* Wrap [Unix.file_descr] to track whether it has been closed. 
* Either a [Unix.file_descr] or nothing (if closed) .
* [is_open t] is [true] if {!close} hasn't been called yet. 
* [close t] closes [t].
      @raise Invalid_arg if [t] is already closed. 
* {1 Eio API} 
* [pipe sw] is a source-sink pair [(r, w)], where data written to [w] can be read from [r].
    It is implemented as a Unix pipe. 
* {1 Main Loop} 
* Low-level API for using uring directly. 
* [noop ()] performs a uring noop. This is only useful for benchmarking. 
* {1 Time functions} 
* [sleep_until time] blocks until the current time is [time]. 
* Allocate a chunk of memory from the fixed buffer.

      Warning: The memory is NOT zeroed out.

      Passing such memory to Linux can be faster than using normal memory, in certain cases.
      There is a limited amount of such memory, and this will return [None] if none is available at present. 
* Like {!alloc_fixed}, but if there are no chunks available then it waits until one is. 
* [with_chunk ~fallback fn] runs [fn chunk] with a freshly allocated chunk and then frees it.

      If no chunks are available, it runs [fallback ()] instead. 
* {1 File manipulation functions} 
* Like {!Unix.open_file}. 
* [read_upto fd chunk len] reads at most [len] bytes from [fd],
      returning as soon as some data is available.

      @param file_offset Read from the given position in [fd] (default: 0).
      @raise End_of_file Raised if all data has already been read. 
* [read_exactly fd chunk len] reads exactly [len] bytes from [fd],
      performing multiple read operations if necessary.

      @param file_offset Read from the given position in [fd] (default: 0).
      @raise End_of_file Raised if the stream ends before [len] bytes have been read. 
* [readv] is like {!read_upto} but can read into any cstruct(s),
      not just chunks of the pre-shared buffer.

      If multiple buffers are given, they are filled in order. 
* [writev] is like {!write} but can write from any cstruct(s),
      not just chunks of the pre-shared buffer.

      If multiple buffers are given, they are sent in order.
      It will make multiple OS calls if the OS doesn't write all of it at once. 
* [writev_single] is like [writev] but only performs a single write operation.
      It returns the number of bytes written, which may be smaller than the requested amount. 
* [connect fd addr] attempts to connect socket [fd] to [addr]. 
* [await_readable fd] blocks until [fd] is readable (or has an error). 
* [await_writable fd] blocks until [fd] is writable (or has an error). 
* Like {!Unix.fstat}. 
* {1 Sockets} 
* [accept ~sw t] blocks until a new connection is received on listening socket [t].

      It returns the new connection and the address of the connecting peer.
      The new connection has the close-on-exec flag set automatically.
      The new connection is attached to [sw] and will be closed when that finishes, if
      not already closed manually by then. 
* [recv_msg socket bufs] is like [readv socket bufs] but also returns the address of the sender. 
* {1 Randomness} 
* Eio backend using Linux 's io_uring . 

     You will normally not use this module directly . 
     Instead , use { ! Eio_main.run } to start an event loop and then use the API in the { ! Eio } module . 

     However , it is possible to use this module directly if you only want to support recent versions of Linux .

    You will normally not use this module directly.
    Instead, use {!Eio_main.run} to start an event loop and then use the API in the {!Eio} module.

    However, it is possible to use this module directly if you only want to support recent versions of Linux. *)


  * Copyright ( C ) 2020 - 2021 Anil Madhavapeddy 
  * 
  * Permission to use , copy , modify , and distribute this software for any 
  * purpose with or without fee is hereby granted , provided that the above 
  * copyright notice and this permission notice appear in all copies . 
  * 
  * THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
  * MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
  * ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
  * WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN 
  * ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF 
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE . 
 
 * Copyright (C) 2020-2021 Anil Madhavapeddy
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *)

open Eio.Std

module FD : sig
  type t

  val is_open : t -> bool

  val close : t -> unit

  val of_unix : sw:Switch.t -> seekable:bool -> close_unix:bool -> Unix.file_descr -> t
* [ let t = of_unix ~sw ~seekable ~close_unix fd ] wraps [ fd ] as an open file descriptor . 
       This is unsafe if [ fd ] is closed directly ( before or after wrapping it ) . 
       @param sw [ t ] is closed when [ sw ] is released , if not closed manually first . 
       @param close_unix If [ true ] , closing [ t ] also closes [ fd ] . 
                         If [ false ] , the caller is responsible for closing [ fd ] , 
                         which must not happen until after [ t ] is closed . 
       @param seekable If true , we pass [ -1 ] to io_uring as the " file offset " , to use the current offset . 
                       If false , pass [ 0 ] as the file offset , which is needed for sockets .
      This is unsafe if [fd] is closed directly (before or after wrapping it).
      @param sw [t] is closed when [sw] is released, if not closed manually first.
      @param close_unix If [true], closing [t] also closes [fd].
                        If [false], the caller is responsible for closing [fd],
                        which must not happen until after [t] is closed.
      @param seekable If true, we pass [-1] to io_uring as the "file offset", to use the current offset.
                      If false, pass [0] as the file offset, which is needed for sockets. *)

  val to_unix : [< `Peek | `Take] -> t -> Unix.file_descr
* [ to_unix op t ] returns the wrapped descriptor . 
       This allows unsafe access to the FD . 
       If [ op ] is [ ` Take ] then [ t ] is marked as closed ( but the underlying FD is not actually closed ) . 
       @raise Invalid_arg if [ t ] is closed .
      This allows unsafe access to the FD.
      If [op] is [`Take] then [t] is marked as closed (but the underlying FD is not actually closed).
      @raise Invalid_arg if [t] is closed. *)
end


type has_fd = < fd : FD.t >
type source = < Eio.Flow.source; Eio.Flow.close; has_fd >
type sink   = < Eio.Flow.sink  ; Eio.Flow.close; has_fd >

type stdenv = <
  stdin  : source;
  stdout : sink;
  stderr : sink;
  net : Eio.Net.t;
  domain_mgr : Eio.Domain_manager.t;
  clock : Eio.Time.clock;
  fs : Eio.Fs.dir Eio.Path.t;
  cwd : Eio.Fs.dir Eio.Path.t;
  secure_random : Eio.Flow.source;
  debug : Eio.Debug.t;
>

val get_fd : <has_fd; ..> -> FD.t
val get_fd_opt : #Eio.Generic.t -> FD.t option

val pipe : Switch.t -> source * sink


val run :
  ?queue_depth:int ->
  ?n_blocks:int ->
  ?block_size:int ->
  ?polling_timeout:int ->
  ?fallback:([`Msg of string] -> 'a) ->
  (stdenv -> 'a) -> 'a
* Run an event loop using io_uring . 

     Uses { ! Uring.create } to create the io_uring , 
     and { ! Uring.set_fixed_buffer } to set a [ block_size * n_blocks ] fixed buffer . 

     Note that if Linux resource limits prevent the requested fixed buffer from being allocated 
     then [ run ] will continue without one ( and log a warning ) . 

     For portable code , you should use { ! Eio_main.run } instead , which will use this automatically 
     if running on Linux with a recent - enough kernel version . 

     @param fallback Call this instead if io_uring is not available for some reason . 
                     The argument is a message describing the problem ( for logging ) . 
                     The default simply raises an exception .

    Uses {!Uring.create} to create the io_uring,
    and {!Uring.set_fixed_buffer} to set a [block_size * n_blocks] fixed buffer.

    Note that if Linux resource limits prevent the requested fixed buffer from being allocated
    then [run] will continue without one (and log a warning).

    For portable code, you should use {!Eio_main.run} instead, which will use this automatically
    if running on Linux with a recent-enough kernel version.

    @param fallback Call this instead if io_uring is not available for some reason.
                    The argument is a message describing the problem (for logging).
                    The default simply raises an exception. *)

* { 1 Low - level API }

module Low_level : sig
  val noop : unit -> unit


  val sleep_until : float -> unit

* { 1 Fixed - buffer memory allocation functions } 

       The size of the fixed buffer is set when calling { ! run } , which attempts to allocate a fixed buffer . 
       However , that may fail due to resource limits .

      The size of the fixed buffer is set when calling {!run}, which attempts to allocate a fixed buffer.
      However, that may fail due to resource limits. *)

  val alloc_fixed : unit -> Uring.Region.chunk option

  val alloc_fixed_or_wait : unit -> Uring.Region.chunk

  val free_fixed : Uring.Region.chunk -> unit

  val with_chunk : fallback:(unit -> 'a) -> (Uring.Region.chunk -> 'a) -> 'a


  val openfile : sw:Switch.t -> string -> Unix.open_flag list -> int -> FD.t

  val openat2 :
    sw:Switch.t ->
    ?seekable:bool ->
    access:[`R|`W|`RW] ->
    flags:Uring.Open_flags.t ->
    perm:Unix.file_perm ->
    resolve:Uring.Resolve.t ->
    ?dir:FD.t -> string -> FD.t
* [    ~perm ~resolve  path ] opens [ dir / path ] . 

       See { ! Uring.openat2 } for details .

      See {!Uring.openat2} for details. *)

  val read_upto : ?file_offset:Optint.Int63.t -> FD.t -> Uring.Region.chunk -> int -> int

  val read_exactly : ?file_offset:Optint.Int63.t -> FD.t -> Uring.Region.chunk -> int -> unit

  val readv : ?file_offset:Optint.Int63.t -> FD.t -> Cstruct.t list -> int

  val write : ?file_offset:Optint.Int63.t -> FD.t -> Uring.Region.chunk -> int -> unit
* [ write fd buf len ] writes exactly [ len ] bytes from [ buf ] to [ fd ] . 

       It blocks until the OS confirms the write is done , 
       and resubmits automatically if the OS does n't write all of it at once .

      It blocks until the OS confirms the write is done,
      and resubmits automatically if the OS doesn't write all of it at once. *)

  val writev : ?file_offset:Optint.Int63.t -> FD.t -> Cstruct.t list -> unit

  val writev_single : ?file_offset:Optint.Int63.t -> FD.t -> Cstruct.t list -> int

  val splice : FD.t -> dst:FD.t -> len:int -> int
* [ splice src ~dst ~len ] attempts to copy up to [ len ] bytes of data from [ src ] to [ dst ] . 

       @return The number of bytes copied . 
       @raise End_of_file [ src ] is at the end of the file . 
       @raise Unix . Unix_error(EINVAL , " splice " , _ ) if splice is not supported for these FDs .

      @return The number of bytes copied.
      @raise End_of_file [src] is at the end of the file.
      @raise Unix.Unix_error(EINVAL, "splice", _) if splice is not supported for these FDs. *)

  val connect : FD.t -> Unix.sockaddr -> unit

  val await_readable : FD.t -> unit

  val await_writable : FD.t -> unit

  val fstat : FD.t -> Unix.stats

  val read_dir : FD.t -> string list
* [ read_dir dir ] reads all directory entries from [ dir ] . 
       The entries are not returned in any particular order 
       ( not even necessarily the order in which Linux returns them ) .
      The entries are not returned in any particular order
      (not even necessarily the order in which Linux returns them). *)


  val accept : sw:Switch.t -> FD.t -> (FD.t * Unix.sockaddr)

  val shutdown : FD.t -> Unix.shutdown_command -> unit
* Like { !  } .

  val send_msg : FD.t -> ?fds:FD.t list -> ?dst:Unix.sockaddr -> Cstruct.t list -> unit
* [ send_msg socket bufs ] is like [    ] , but also allows setting the destination address 
       ( for unconnected sockets ) and attaching FDs ( for Unix - domain sockets ) .
      (for unconnected sockets) and attaching FDs (for Unix-domain sockets). *)

  val recv_msg : FD.t -> Cstruct.t list -> Uring.Sockaddr.t * int

  val recv_msg_with_fds : sw:Switch.t -> max_fds:int -> FD.t -> Cstruct.t list -> Uring.Sockaddr.t * int * FD.t list
* [ recv_msg_with_fds ] is like [ recv_msg ] but also allows receiving up to [ max_fds ] file descriptors 
       ( sent using SCM_RIGHTS over a Unix domain socket ) .
      (sent using SCM_RIGHTS over a Unix domain socket). *)


  val getrandom : Cstruct.t -> int
* [ getrandom buf ] reads some random bytes into [ buf ] and returns the number of bytes written . 

      It uses Linux 's [ getrandom ] call , which is like reading from /dev / urandom 
      except that it will block ( the whole domain ) if used at early boot 
      when the random system has n't been initialised yet .

     It uses Linux's [getrandom] call, which is like reading from /dev/urandom
     except that it will block (the whole domain) if used at early boot
     when the random system hasn't been initialised yet. *)

* { 1 DNS functions }

  val getaddrinfo : service:string -> string -> Eio.Net.Sockaddr.t list
* [  host ] returns a list of IP addresses for [ host ] . [ host ] is either a domain name or 
       an ipaddress .
      an ipaddress. *)

end
