LCOV - code coverage report
Current view: top level - util/io - fd_io.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 92 92 100.0 %
Date: 2025-01-08 12:08:44 Functions: 34 14028 0.2 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_util_io_fd_io_h
       2             : #define HEADER_fd_src_util_io_fd_io_h
       3             : 
       4             : /* API for platform agnostic high performance stream I/O.  Summary:
       5             : 
       6             :    Read at least min bytes directly from stream fd into size max buffer
       7             :    buf (1<=min<=max):
       8             : 
       9             :      ulong rsz; int err = fd_io_read( fd, buf, min, max, &rsz );
      10             :      if     ( FD_LIKELY( err==0 ) ) ... success, rsz in [min,max], buf updated
      11             :      else if( FD_LIKELY( err< 0 ) ) ... EOF encountered, rsz is [0,min), buf updated
      12             :      else                           ... I/O error, rsz is zero, buf clobbered
      13             :                                     ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
      14             :                                     ... fd should be considered failed
      15             : 
      16             :    This usage will block (even if the stream is non-blocking) until min
      17             :    bytes are read, EOF is encountered or there is an I/O error.  As
      18             :    such, the min==1 case behaves like a POSIX read on a blocking stream.
      19             :    The min==max case will read an exact number of bytes from the stream
      20             :    and return an error if this is not possible.  The 1<min<max case is
      21             :    useful for implementing buffered I/O.
      22             : 
      23             :    A non-blocking read on a non-blocking stream is possible by setting
      24             :    min==0:
      25             : 
      26             :      ulong rsz; int err = fd_io_read( fd, buf, 0UL, max, &rsz );
      27             :      if     ( FD_LIKELY( err==0      ) ) ... success, rsz in [1,max], buf updated
      28             :      else if( FD_LIKELY( err==EAGAIN ) ) ... try again later, rsz is zero, buf unchanged
      29             :      else if( FD_LIKELY( err< 0      ) ) ... EOF encountered, rsz is zero, buf unchanged
      30             :      else                                ... I/O error, rsz is zero, buf clobbered
      31             :                                          ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
      32             :                                          ... fd should be considered failed
      33             : 
      34             :    (If min==0 and stream fd is blocking, the EAGAIN case will never
      35             :    occur.)
      36             : 
      37             :    Write is nearly symmetrical to read.
      38             : 
      39             :    Write at least min byte and at most max bytes from buf into stream fd
      40             :    (1<=min<=max):
      41             : 
      42             :      ulong wsz; int err = fd_io_write( fd, buf, min, max, &wsz );
      43             :      if( FD_LIKELY( err==0 ) ) ... success, wsz in [min,max]
      44             :      else                      ... I/O error, wsz is zero, how much of buf was streamed before the error is unknown
      45             :                                ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
      46             :                                ... fd should be considered failed
      47             : 
      48             :    This usage will block (even if the stream is non-blocking) until min
      49             :    bytes are written or there is an I/O error.  As such, the min==1 case
      50             :    behaves like a POSIX write on a stream.  The min==max case will write
      51             :    an exact number of bytes to fd and give an error if this is not
      52             :    possible.  The 1<min<max case is useful for implementing buffered
      53             :    I/O.
      54             : 
      55             :    A non-blocking write on a non-blocking stream is possible by setting
      56             :    min==0:
      57             : 
      58             :      ulong wsz; int err = fd_io_write( fd, buf, 0UL, max, &wsz );
      59             :      if     ( FD_LIKELY( err==0      ) ) ... success, wsz in [1,max]
      60             :      else if( FD_LIKELY( err==EAGAIN ) ) ... try again later, wsz is zero
      61             :      else                                ... I/O error, wsz is zero, how much of buf was streamed before the error is unknown
      62             :                                          ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
      63             :                                          ... fd should be considered failed
      64             : 
      65             :    (If min==0 and stream fd is blocking, the EAGAIN case will never
      66             :    occur.)
      67             : 
      68             :    Each call above typically requires at least one system call.  This
      69             :    can be inefficient if doing lots of tiny reads and writes.  For
      70             :    higher performance usage in cases like this, buffered I/O APIs are
      71             :    also provided.
      72             : 
      73             :    Buffered reads:
      74             : 
      75             :      ... setup buffered reads from stream fd using the rbuf_sz size
      76             :      ... buffer rbuf (rbuf_sz>0) as the read buffer
      77             : 
      78             :      fd_io_buffered_istream_t in[1];
      79             :      fd_io_buffered_istream_init( in, fd, rbuf, rbuf_sz );
      80             :      ... in is initialized and has ownership of fd and rbuf
      81             : 
      82             :      ... accessors (these return the values used to init in)
      83             : 
      84             :      int    fd      = fd_io_buffered_istream_fd     ( in );
      85             :      void * rbuf    = fd_io_buffered_istream_rbuf   ( in );
      86             :      ulong  rbuf_sz = fd_io_buffered_istream_rbuf_sz( in );
      87             : 
      88             :      ... read sz bytes from a buffered stream
      89             : 
      90             :      int err = fd_io_buffered_istream_read( in, buf, sz );
      91             :      if     ( FD_LIKELY( err==0 ) ) ... success, buf holds the next sz bytes of stream
      92             :      else if( FD_LIKELY( err< 0 ) ) ... EOF before sz bytes could be read, buf clobbered
      93             :      else                           ... I/O error, buf clobbered
      94             :                                     ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
      95             :                                     ... in and fd should be considered failed
      96             : 
      97             :      ... skip sz bytes in a buffered stream
      98             : 
      99             :      int err = fd_io_buffered_istream_skip( in, sz );
     100             :      if     ( FD_LIKELY( err==0 ) ) ... success, sz bytes skipped
     101             :      else if( FD_LIKELY( err< 0 ) ) ... EOF before sz bytes could be skipped
     102             :      else                           ... I/O error
     103             :                                     ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     104             :                                     ... in and fd should be considered failed
     105             : 
     106             :      ... zero-copy read bytes from a buffered stream
     107             : 
     108             :      ulong        peek_sz = fd_io_buffered_istream_peek_sz( in ); ... returns number of bytes currently buffered
     109             :      void const * peek    = fd_io_buffered_istream_peek   ( in ); ... returns location of current buffered bytes
     110             : 
     111             :      fd_io_buffered_istream_seek( in, sz ); ... consume sz currently buffered bytes, sz in [0,peek_sz]
     112             : 
     113             :      ... read buffering control
     114             : 
     115             :      int err = fd_io_buffered_istream_fetch( in );
     116             :      if     ( FD_LIKELY( err==0      ) ) ... success,         peek_sz updated to at most rbuf_sz
     117             :      else if( FD_LIKELY( err< 0      ) ) ... end-of-file,     peek_sz updated to unconsumed bytes remaining (at most rbuf_sz)
     118             :      else if( FD_LIKELY( err==EAGAIN ) ) ... try again later, peek_sz unchanged
     119             :      else                                ... I/O error,       peek_sz unchanged
     120             :                                          ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     121             :                                          ... in and fd should be considered failed
     122             : 
     123             :      (The EAGAIN case only applies for a non-blocking stream.)
     124             : 
     125             :      TODO: consider option to do block until a min level?
     126             : 
     127             :      ... finish using buffered stream
     128             : 
     129             :      fd_io_buffered_istream_fini( in );
     130             :      ... in is not in use and no longer has ownership of fd and rbuf.
     131             :      ... IMPORTANT! buffering might have pushed fd's file offset beyond
     132             :      ... the bytes the user has actually consumed.  It is the user's
     133             :      ... responsibility for handling this.  (Usually nothing needs to be
     134             :      ... or can be done as either the next operation is to close fd or
     135             :      ... the fd does not support seeking.)
     136             : 
     137             :    There are nearly symmetric APIs for buffered writes.
     138             : 
     139             :      ... start buffered writing to stream fd using the wbuf_sz size
     140             :      ... buffer wbuf (wbuf_sz>0) as the write buffer
     141             : 
     142             :      fd_io_buffered_ostream_t out[1];
     143             :      fd_io_buffered_ostream_init( out, fd, wbuf, wbuf_sz );
     144             :      ... out is initialized and has ownership of fd and wbuf
     145             : 
     146             :      ... accessors (these return the values used to init in)
     147             : 
     148             :      int    fd      = fd_io_buffered_ostream_fd     ( out );
     149             :      void * wbuf    = fd_io_buffered_ostream_wbuf   ( out );
     150             :      ulong  wbuf_sz = fd_io_buffered_ostream_wbuf_sz( out );
     151             : 
     152             :      ... write sz bytes to a buffered stream
     153             : 
     154             :      int err = fd_io_buffered_ostream_write( out, buf, sz );
     155             :      if( FD_LIKELY( err==0 ) ) ... success, sz bytes from have been written from the caller's POV
     156             :      else                      ... I/O error, err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     157             :                                ... out and fd should be considered failed
     158             : 
     159             :      ... zero-copy write bytes to a buffered stream
     160             : 
     161             :      ulong  peek_sz = fd_io_buffered_ostream_peek_sz( out ); ... returns amount of unused write buffer space, in [0,wbuf_sz]
     162             :      void * peek    = fd_io_buffered_ostream_peek   ( out ); ... returns location of unused write buffer space
     163             : 
     164             :      fd_io_buffered_ostream_seek( in, sz ); ... commit sz unused bytes of write buffer, sz in [0,peek_sz]
     165             : 
     166             :      ... write buffer control
     167             : 
     168             :      int err = fd_io_buffered_ostream_flush( out );
     169             :      if( FD_LIKELY( err==0 ) ) ... success, all buffered bytes have been drained to fd
     170             :      else                      ... I/O error, err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     171             :                                ... out and fd should be considered failed
     172             : 
     173             :      (This will block even for a non-blocking stream.)
     174             : 
     175             :      ... finish using buffered stream
     176             : 
     177             :      fd_io_buffered_ostream_fini( out );
     178             :      ... out is not in use and no longer has ownership of fd and wbuf.
     179             :      ... IMPORTANT! fini does not do any flushing of buffered writes (as
     180             :      ... such fini is guaranteed to always succeed, can be applied to
     181             :      ... out's that have failed, etc).  It is the users responsibility
     182             :      ... to do any final flush before calling fini.
     183             : 
     184             :    More details below. */
     185             : 
     186             : #include "../bits/fd_bits.h"
     187             : 
     188             : /* FD_IO_SEEK_TYPE_{SET,CUR,END} give supported seek types.  They have
     189             :    the same meaning as the corresponding lseek SEEK_{SET,CUR_END}. */
     190             : 
     191     3009003 : #define FD_IO_SEEK_TYPE_SET (0)
     192     2021187 : #define FD_IO_SEEK_TYPE_CUR (1)
     193             : #define FD_IO_SEEK_TYPE_END (2)
     194             : 
     195             : /* fd_io_buffered_{istream,ostream}_t is an opaque handle of an
     196             :    {input,output} stream with buffered {reads,writes}.  The internals
     197             :    are visible here to facilitate inlining various operations.  This is
     198             :    declaration friendly (e.g. usually should just do
     199             :    "fd_io_buffered_istream_t in[1];" on the stack to get a suitable
     200             :    memory region for the stream state).  These are not meant to be
     201             :    persistent or shared IPC. */
     202             : 
     203             : struct fd_io_buffered_istream_private {
     204             :   int     fd;         /* Open normal-ish file descriptor of stream */
     205             :   uchar * rbuf;       /* Read buffer, non-NULL, indexed [0,rbuf_sz), arb alignment */
     206             :   ulong   rbuf_sz;    /* Read buffer size, positive */
     207             :   ulong   rbuf_lo;    /* Buf bytes [0,rbuf_lo) have already been consumed */
     208             :   ulong   rbuf_ready; /* Number of buffered byte that haven't been consumed, 0<=rbuf_lo<=(rbuf_lo+rbuf_ready)<=rbuf_sz */
     209             : };
     210             : 
     211             : typedef struct fd_io_buffered_istream_private fd_io_buffered_istream_t;
     212             : 
     213             : struct fd_io_buffered_ostream_private {
     214             :   int     fd;        /* Open normal-ish file descriptor of stream */
     215             :   uchar * wbuf;      /* Write buffer, non-NULL, indexed [0,wbuf_sz), arb alignment */
     216             :   ulong   wbuf_sz;   /* Write buffer size, positive */
     217             :   ulong   wbuf_used; /* Number buffered bytes that haven't been written to fd, in [0,wbuf_sz] */
     218             : };
     219             : 
     220             : typedef struct fd_io_buffered_ostream_private fd_io_buffered_ostream_t;
     221             : 
     222             : /* FD_IO_MMIO_MODE_* give supported modes for memory mapped I/O */
     223             : 
     224          63 : #define FD_IO_MMIO_MODE_READ_ONLY  (0)
     225       12354 : #define FD_IO_MMIO_MODE_READ_WRITE (1)
     226             : 
     227             : FD_PROTOTYPES_BEGIN
     228             : 
     229             : /* fd_io_read streams at least dst_min bytes from the given file
     230             :    descriptor into the given memory region.  fd should be an open
     231             :    normal-ish file descriptor (it is okay for fd to be non-blocking).
     232             :    dst points in the caller's address space with arbitrary alignment to
     233             :    the first byte of the dst_max byte memory region to use (assumes dst
     234             :    non-NULL, and dst_min is at most dst_max).  The caller should not
     235             :    read or write this region during the call and no interest in dst is
     236             :    retained on return.  If dst_min is 0, this will try to read dst_max
     237             :    from the stream exactly once.  If dst_max is 0, is a no-op.
     238             : 
     239             :    Returns 0 on success.  On success, *_dst_sz will be the number of
     240             :    bytes read into dst.  Will be in [dst_min,dst_max].
     241             : 
     242             :    Returns a negative number if end-of-file was encountered before
     243             :    reading dst_min bytes.  *_dst_sz will be the number bytes read into
     244             :    dst when the end-of-file was encountered.  Will be in [0,dst_min).
     245             : 
     246             :    Returns an errno compatible error code on failure (note that all
     247             :    errnos are positive).  If errno is anything other than EAGAIN, the
     248             :    underlying fd should be considered to be in a failed state such that
     249             :    the only valid operation on fd is to close it.  *_dst_sz will be zero
     250             :    and the contents of dst will be undefined.  This API fixes up the
     251             :    POSIX glitches around EWOULDBLOCK / EAGAIN: if the underlying target
     252             :    has EWOULDBLOCK different from EAGAIN and read uses EWOULDBLOCK
     253             :    instead of EAGAIN, this will still just return EAGAIN.  EAGAIN will
     254             :    only be returned if the underlying fd is non-blocking and dst_min is
     255             :    zero.
     256             : 
     257             :    TL;DR
     258             : 
     259             :    - dst_min is positive:
     260             : 
     261             :        ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
     262             :        if     ( FD_LIKELY( err==0 ) ) ... success, dst_sz in [dst_min,dst_max], dst updated
     263             :        else if( FD_LIKELY( err< 0 ) ) ... EOF, dst_sz in [0,dst_min), dst updated
     264             :        else                           ... I/O error, dst_sz is zero, dst clobbered
     265             :                                       ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     266             :                                       ... fd should be considered failed
     267             : 
     268             :      This is equivalent to looping over reads of up to dst_max in size
     269             :      from fd until at least dst_min bytes are read.  It does not matter
     270             :      if fd is blocking or not.
     271             : 
     272             :    - dst_min is zero and fd is blocking:
     273             : 
     274             :        ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
     275             :        if     ( FD_LIKELY( err==0 ) ) ... success, dst_sz in [1,dst_max], dst updated
     276             :        else if( FD_LIKELY( err< 0 ) ) ... EOF, dst_sz is zero, dst unchanged
     277             :        else                           ... I/O error, dst_sz is zero, dst clobbered
     278             :                                       ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     279             :                                       ... fd should be considered failed
     280             : 
     281             :      This is equivalent to a single read of dst_max size on a blocking
     282             :      fd.
     283             : 
     284             :    - dst_min is zero and fd is non-blocking:
     285             : 
     286             :        ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
     287             :        if     ( FD_LIKELY( err==0      ) ) ... success, dst_sz in [1,dst_max], dst updated
     288             :        else if( FD_LIKELY( err==EAGAIN ) ) ... no data available now, try again later, dst_sz is zero, dst unchanged
     289             :        else if( FD_LIKELY( err< 0      ) ) ... EOF, dst_sz is zero, dst unchanged
     290             :        else                                ... I/O error, dst_sz is zero, dst clobbered
     291             :                                            ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     292             :                                            ... fd should be considered failed
     293             : 
     294             :      This is equivalent to a single read of dst_max size on a
     295             :      non-blocking fd (with the POSIX glitches around EAGAIN /
     296             :      EWOULDBLOCK cleaned up). */
     297             : 
     298             : int
     299             : fd_io_read( int     fd,
     300             :             void *  dst,
     301             :             ulong   dst_min,
     302             :             ulong   dst_max,
     303             :             ulong * _dst_sz );
     304             : 
     305             : /* fd_io_write behaves virtually identical to fd_io_read but the
     306             :    direction of the transfer is from memory to the stream and there is
     307             :    no notion of EOF handling.  Assumes src is non-NULL,
     308             :    src_min<=src_max, src_sz is non-NULL and non-overlapping with src.
     309             :    If src_max is 0, is a no-op.  Summarizing:
     310             : 
     311             :    - src_min is positive:
     312             : 
     313             :        ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
     314             :        if( FD_LIKELY( err==0 ) ) ... success, src_sz in [src_min,src_max]
     315             :        else                      ... I/O error, src_sz is zero
     316             :                                  ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     317             :                                  ... fd should be considered failed
     318             : 
     319             :      This is equivalent to looping over writes of up to src_max in size
     320             :      to fd until at least src_min bytes are written.  It does not matter
     321             :      if fd is blocking or not.
     322             : 
     323             :    - src_min is zero and fd is blocking:
     324             : 
     325             :        ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
     326             :        if( FD_LIKELY( err==0 ) ) ... success, src_sz in [1,src_max]
     327             :        else                      ... I/O error, src_sz is zero
     328             :                                  ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     329             :                                  ... fd should be considered failed
     330             : 
     331             :      This is equivalent to a single write of src_max size on a blocking
     332             :      fd.
     333             : 
     334             :    - src_min is zero and fd is non-blocking:
     335             : 
     336             :        ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
     337             :        if     ( FD_LIKELY( err==0      ) ) ... success, src_sz in [1,src_max]
     338             :        else if( FD_LIKELY( err==EAGAIN ) ) ... no bytes written, try again later, src_sz is zero
     339             :        else                                ... I/O error, src_sz is zero
     340             :                                            ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     341             :                                            ... fd should be considered failed
     342             : 
     343             :      This is equivalent to a single write of src_max size on a
     344             :      non-blocking fd (with the POSIX glitches around EAGAIN /
     345             :      EWOULDBLOCK fixed up). */
     346             : 
     347             : int
     348             : fd_io_write( int          fd,
     349             :              void const * src,
     350             :              ulong        src_min,
     351             :              ulong        src_max,
     352             :              ulong *      _src_sz );
     353             : 
     354             : /* fd_io_sz returns the current byte size of the file underlying the
     355             :    given file descriptor.  Note that writing to a file descriptor may
     356             :    increase the file size.  On success, returns 0 and *_sz will contain
     357             :    the current size (in [0,LONG_MAX], not ULONG_MAX for fd_io_seek
     358             :    friendliness).  On failure, returns a strerror compatible error code
     359             :    and *_sz will be 0.  Reasons for failure include the usual fstat
     360             :    reasons. */
     361             : 
     362             : int
     363             : fd_io_sz( int     fd,
     364             :           ulong * _sz );
     365             : 
     366             : /* fd_io_truncate truncates the file underlying the given file descriptor
     367             :    to be sz bytes long.  If sz is larger than the current size, the file
     368             :    will be zero padded to sz.  If smaller, the trailing size bytes will
     369             :    be discarded.  On success, returns 0.  On failure, returns a strerror
     370             :    compatible error code.  Reasons for failure include sz is too large /
     371             :    incompatible with ftruncate and the usual ftruncate reasons.
     372             :    Truncating a file to a size that is smaller than the file offset of
     373             :    any open file descriptor (file system wide) on that file has
     374             :    undefined behavior.  (POSIX has some conventions here but portable
     375             :    code should not rely on them.) */
     376             : 
     377             : int
     378             : fd_io_truncate( int   fd,
     379             :                 ulong sz );
     380             : 
     381             : /* fd_io_seek seeks the byte index of the given file descriptor
     382             :    according to rel_off and type.  Seeking to indices outside [0,sz]
     383             :    (i.e. before the SOF / first byte of the file and strictly after EOF
     384             :    / one past the last byte of the file) is not supported (POSIX has
     385             :    defined behaviors but portable code should not assume them).
     386             : 
     387             :      type==FD_IO_SEEK_TYPE_SET: the index will be seeked rel_off bytes
     388             :      from SOF / the first byte of the file.  Supported rel_off are in
     389             :      [0,sz].
     390             : 
     391             :      type==FD_IO_SEEK_TYPE_CUR: the index will be seeked rel_off bytes
     392             :      from the current byte index.  Supported rel_off are in
     393             :      [-idx,sz-idx].
     394             : 
     395             :      type==FD_IO_SEEK_TYPE_END: the index will be seeked rel_off bytes
     396             :      from EOF / one past the last byte of the file.  Supported rel_off
     397             :      are in [-sz,0].
     398             : 
     399             :   On success, returns 0 and *_idx will contain the new byte index (will
     400             :   be in [0,sz] for supported rel_off and in [0,LONG_MAX] generally).  On
     401             :   failure, returns a strerror compatible error code and *_idx will be 0.
     402             :   Reasons for failure include unsupported type, rel_off is not lseek
     403             :   compatible, and the usual lseek reasons (e.g. fd is not seekable ...
     404             :   a pipe / socket / etc).
     405             : 
     406             :   Note that, if the file descriptor supports it:
     407             : 
     408             :     fd_io_seek( fd, 0L, FD_IO_SEEK_TYPE_CUR, &idx );
     409             : 
     410             :   will return fd's current byte index (in [0,sz]) in idx. */
     411             : 
     412             : int
     413             : fd_io_seek( int     fd,
     414             :             long    rel_off,
     415             :             int     type,
     416             :             ulong * _idx );
     417             : 
     418             : /* fd_io_buffered_read is like fd_io_read but can consolidate many
     419             :    tiny reads into a larger fd_io_read via the given buffer.  Unlike
     420             :    fd_io_read, dst NULL is okay if dst_sz is 0 (dst_sz 0 is a no-op that
     421             :    immediately returns success).  Will block the caller until the read
     422             :    is complete or an end-of-file was encountered.
     423             : 
     424             :    rbuf points to the first byte of a rbuf_sz size memory region in the
     425             :    caller's address space used for read buffering (assumes rbuf is
     426             :    non-NULL with arbitrary alignment and rbuf_sz is positive).  On entry
     427             :    *_rbuf_lo is where the first byte of unconsumed buffered reads are
     428             :    located and *_rbuf_ready is number of unconsumed buffered bytes.
     429             :    Assumes 0<=*_rbuf_lo<=(*_rbuf_lo+*_rbuf_ready)<=rbuf_sz.
     430             : 
     431             :    Returns 0 on success.  dst will hold dst_sz bytes from the stream.
     432             :    *_rbuf_lo and *_rbuf_ready will be updated and the above invariant on
     433             :    *_rbuf_lo and *_rbuf_ready will still hold.
     434             : 
     435             :    Returns non-zero on failure.  Failure indicates that dst_sz bytes
     436             :    could not be read because of an I/O error (return will be a positive
     437             :    errno compatible error code) or an end-of-file was encountered
     438             :    (return will be a negative number).  dst should be assumed to have
     439             :    been clobbered, *_rbuf_lo and *_rbuf_ready will be zero.  If an I/O
     440             :    error, the fd should be considered to be in a failed state such that
     441             :    the only valid operation on it is to close it.
     442             : 
     443             :    IMPORTANT!  This function will only read from fd in multiples of
     444             :    rbuf_sz (except for a potentially last incomplete block before an
     445             :    end-of-file).  This can be useful for various ultra high performance
     446             :    contexts.
     447             : 
     448             :    This API usually should not be used directly.  It is mostly useful
     449             :    for implementing higher level APIs like fd_io_buffered_istream below. */
     450             : 
     451             : int
     452             : fd_io_buffered_read( int     fd,
     453             :                      void *  dst,
     454             :                      ulong   dst_sz,
     455             :                      void *  rbuf,
     456             :                      ulong   rbuf_sz,
     457             :                      ulong * _rbuf_lo,
     458             :                      ulong * _rbuf_ready );
     459             : 
     460             : /* fd_io_buffered_skip is like fd_io_buffered_read but will skip over
     461             :    skip_sz bytes of the stream without copying them into a user buffer.
     462             :    If stream fd is seekable (e.g. a normal file), this should be O(1).
     463             :    If not (e.g. fd is pipe / socket / stdin / etc), this will block the
     464             :    caller until skip_sz bytes have been skipped or an I/O error occurs.
     465             : 
     466             :    IMPORTANT!  If stream fd is seekable, POSIX behaviors allow seeking
     467             :    past end-of-file (apparently even if fd is read only).  Whether or
     468             :    not this is a good idea is debatable.  The result though is this API
     469             :    will usually not return an error if skip_sz moves past the
     470             :    end-of-file (however, if skip_sz is so large that it causes the file
     471             :    offset to overflow, this will return EOVERFLOW).  In particular, this
     472             :    API cannot be used to detect end-of-file.
     473             : 
     474             :    IMPORTANT!  This function makes no effort to skip in multiples of
     475             :    rbuf_sz.  Such is up to the caller to do if such is desirable.
     476             : 
     477             :    This API usually should not be used directly.  It is mostly useful
     478             :    for implementing higher level APIs like fd_io_buffered_istream below. */
     479             : 
     480             : int
     481             : fd_io_buffered_skip( int     fd,
     482             :                      ulong   skip_sz,
     483             :                      void *  rbuf,
     484             :                      ulong   rbuf_sz,
     485             :                      ulong * _rbuf_lo,
     486             :                      ulong * _rbuf_ready );
     487             : 
     488             : /* fd_io_buffered_write is like fd_io_write but can consolidate many
     489             :    tiny writes into a larger fd_io_write via the given buffer.  Unlike
     490             :    fd_io_write, src NULL is okay if src_sz is 0 (src_sz 0 is a no-op
     491             :    that immediately returns success).  Will block the caller until the
     492             :    write is complete or an end-of-file was encountered.
     493             : 
     494             :    wbuf points to the first byte of a wbuf_sz size memory region in the
     495             :    caller's address space (assumes wbuf is non-NULL with arbitrary
     496             :    alignment and wbuf_sz is positive).  On entry *_wbuf_used is the
     497             :    number of bytes in wbuf from previous buffered writes that have not
     498             :    yet been streamed out.  Assumes *_wbuf_used is in [0,wbuf_sz].
     499             : 
     500             :    Returns 0 on success.  wbuf will hold *_wbuf_used bytes not yet
     501             :    written to fd by write and/or previous buffered writes.  The above
     502             :    invariant on *_wbuf_used will still hold.
     503             : 
     504             :    Returns non-zero on failure.  Failure indicates that src_sz bytes
     505             :    could not be written because of an I/O error (return will be a
     506             :    positive errno compatible error code).  fd should be considered to be
     507             :    in a failed state such that the only valid operation on it is to
     508             :    close it.  *_wbuf_used will be 0 and the contents of wbuf will be
     509             :    undefined.  Zero or more bytes of previously buffered writes and/or
     510             :    src might have been written before the failure.
     511             : 
     512             :    IMPORTANT!  This function will only write to fd in multiples of
     513             :    wbuf_sz.  This can be useful for various ultra high performance
     514             :    contexts.
     515             : 
     516             :    This API usually should not be used directly.  It is mostly useful
     517             :    for implementing higher level APIs like fd_io_buffered_ostream below. */
     518             : 
     519             : int
     520             : fd_io_buffered_write( int          fd,
     521             :                       void const * src,
     522             :                       ulong        src_sz,
     523             :                       void *       wbuf,
     524             :                       ulong        wbuf_sz,
     525             :                       ulong *      _wbuf_used );
     526             : 
     527             : /* fd_io_buffered_istream_init initializes in to do buffered reads from
     528             :    the given file descriptor.  in is an unused location that should hold
     529             :    the buffering state, fd is an open normal-ish file descriptor, rbuf
     530             :    points to the first byte in the caller's address space to an unused
     531             :    rbuf_sz size memory region to use for read buffering (assumes rbuf is
     532             :    non-NULL with arbitrary alignment and rbuf_sz is positive).  Returns
     533             :    in and on return in will be initialized.  in will have ownership of
     534             :    fd and rbuf while initialized. */
     535             : 
     536             : static inline fd_io_buffered_istream_t *
     537             : fd_io_buffered_istream_init( fd_io_buffered_istream_t * in,
     538             :                              int                        fd,
     539             :                              void *                     rbuf,
     540          45 :                              ulong                      rbuf_sz ) {
     541          45 :   in->fd         = fd;
     542          45 :   in->rbuf       = (uchar *)rbuf;
     543          45 :   in->rbuf_sz    = rbuf_sz;
     544          45 :   in->rbuf_lo    = 0UL;
     545          45 :   in->rbuf_ready = 0UL;
     546          45 :   return in;
     547          45 : }
     548             : 
     549             : /* fd_io_buffered_istream_{fd,rbuf,rbuf_sz} return the corresponding
     550             :    value used to initialize in.  Assumes in is initialized. */
     551             : 
     552           3 : FD_FN_PURE static inline int    fd_io_buffered_istream_fd     ( fd_io_buffered_istream_t const * in ) { return in->fd;      }
     553           3 : FD_FN_PURE static inline void * fd_io_buffered_istream_rbuf   ( fd_io_buffered_istream_t const * in ) { return in->rbuf;    }
     554           3 : FD_FN_PURE static inline ulong  fd_io_buffered_istream_rbuf_sz( fd_io_buffered_istream_t const * in ) { return in->rbuf_sz; }
     555             : 
     556             : /* fd_io_buffered_istream_fini finalizes a buffered input stream.
     557             :    Assumes in is initialized.  On return in will no longer be
     558             :    initialized and ownership the underlying fd and rbuf will return to
     559             :    the caller.
     560             : 
     561             :    IMPORTANT!  THIS WILL NOT REPOSITION THE UNDERLYING FD FILE OFFSET
     562             :    (SUCH MIGHT NOT EVEN BE POSSIBLE) TO "UNREAD" ANY UNCONSUMED BUFFERED
     563             :    DATA. */
     564             : 
     565             : static inline void
     566          45 : fd_io_buffered_istream_fini( fd_io_buffered_istream_t * in ) {
     567          45 :   (void)in;
     568          45 : }
     569             : 
     570             : /* fd_io_buffered_istream_read reads dst_sz bytes from in to dst,
     571             :    reading ahead as convenient.  Assumes in is initialized.  dst /
     572             :    dst_sz have the same meaning / restrictions as fd_io_buffered_read.
     573             :    Returns 0 on success and non-zero on failure.  Failure interpretation
     574             :    is the same as fd_io_buffered_read.  On failure, in and the
     575             :    underlying file descriptor should be considered to be in a failed
     576             :    state (e.g. the only valid thing to do to in is fini and the only
     577             :    valid thing to do to fd is close).
     578             : 
     579             :    IMPORTANT!  If fd_io_buffered_istream_{fetch,skip} below are never
     580             :    used (or only used to skip in multiplies of rbuf_sz), all the reads
     581             :    from the underlying stream will always be at multiples of rbuf_sz
     582             :    from the file offset when the in was initialized and a multiple of
     583             :    rbuf_sz in size (except possibly a final read to the end-of-file).
     584             :    This can be beneficial in various high performance I/O regimes. */
     585             : 
     586             : FD_FN_UNUSED static int /* Work around -Winline */
     587             : fd_io_buffered_istream_read( fd_io_buffered_istream_t * in,
     588             :                              void *                     dst,
     589        1767 :                              ulong                      dst_sz ) {
     590             :   /* We destructure in to avoid pointer escapes that might inhibit
     591             :      optimizations of other in inlines. */
     592        1767 :   ulong rbuf_lo    = in->rbuf_lo;
     593        1767 :   ulong rbuf_ready = in->rbuf_ready;
     594        1767 :   int err = fd_io_buffered_read( in->fd, dst, dst_sz, in->rbuf, in->rbuf_sz, &rbuf_lo, &rbuf_ready );
     595        1767 :   in->rbuf_lo    = rbuf_lo;
     596        1767 :   in->rbuf_ready = rbuf_ready;
     597        1767 :   return err;
     598        1767 : }
     599             : 
     600             : /* fd_io_buffered_istream_skip skips skip_sz bytes from in.  Assumes in
     601             :    is initialized.  Returns 0 on success and non-zero on failure.
     602             :    Failure interpretation is the same as fd_io_buffered_read.  On a
     603             :    failure, in and the underlying file descriptor should be considered
     604             :    to be in a failed state (e.g. the only valid thing to do to in is
     605             :    fini and the only valid thing to do to fd is close).
     606             : 
     607             :    If the fd underlying in is seekable (e.g. a file), this will be very
     608             :    fast.  If not (e.g. fd is pipe / socket / etc), this can block the
     609             :    caller until skip_sz bytes have arrived or an I/O error is detected.
     610             : 
     611             :    IMPORTANT!  See note in fd_io_buffered_istream_read above about the
     612             :    impact of this on file pointer alignment. */
     613             : 
     614             : static inline int
     615             : fd_io_buffered_istream_skip( fd_io_buffered_istream_t * in,
     616        1242 :                              ulong                      skip_sz ) {
     617             :   /* We destructure in to avoid pointer escapes that might inhibit
     618             :      optimizations of other in inlines. */
     619        1242 :   ulong rbuf_lo    = in->rbuf_lo;
     620        1242 :   ulong rbuf_ready = in->rbuf_ready;
     621        1242 :   int err = fd_io_buffered_skip( in->fd, skip_sz, in->rbuf, in->rbuf_sz, &rbuf_lo, &rbuf_ready );
     622        1242 :   in->rbuf_lo    = rbuf_lo;
     623        1242 :   in->rbuf_ready = rbuf_ready;
     624        1242 :   return err;
     625        1242 : }
     626             : 
     627             : /* fd_io_buffered_istream_peek returns a pointer in the caller's address
     628             :    space to the first byte that has been read but not yet consumed.
     629             :    Assumes in is initialized.  The returned pointer can have arbitrary
     630             :    alignment and the returned pointer lifetime is until the next read,
     631             :    fetch, or fini. */
     632             : 
     633             : FD_FN_PURE static inline void const *
     634        7653 : fd_io_buffered_istream_peek( fd_io_buffered_istream_t * in ) {
     635        7653 :   return in->rbuf + in->rbuf_lo;
     636        7653 : }
     637             : 
     638             : /* fd_io_buffered_istream_peek_sz returns the number of bytes that have
     639             :    been read but not yet consumed.  Assumes in is initialized.  Returned
     640             :    value will be in [0,rbuf_sz] and will be valid until the next read,
     641             :    fetch, seek or fini. */
     642             : 
     643             : FD_FN_PURE static inline ulong
     644       11100 : fd_io_buffered_istream_peek_sz( fd_io_buffered_istream_t * in ) {
     645       11100 :   return in->rbuf_ready;
     646       11100 : }
     647             : 
     648             : /* fd_io_buffered_istream_seek consumes sz buffered bytes from in.
     649             :    Assumes in is initialized and that sz is at most peek_sz. */
     650             : 
     651             : static inline void
     652             : fd_io_buffered_istream_seek( fd_io_buffered_istream_t * in,
     653        7653 :                              ulong                      sz ) {
     654        7653 :   in->rbuf_lo    += sz;
     655        7653 :   in->rbuf_ready -= sz;
     656        7653 : }
     657             : 
     658             : /* fd_io_buffered_istream_fetch tries to fill up the stream's read
     659             :    buffer with as many unconsumed bytes as possible.  Assumes in is
     660             :    initialized.  Returns 0 on success (rbuf is filled to rbuf_sz with
     661             :    unconsumed data) and non-zero on failure (see below for
     662             :    interpretation).  On failure, in and the underlying file descriptor
     663             :    should be considered to be in a failed state (e.g. the only valid
     664             :    thing to do out on is fini and the only valid thing to do on fd is
     665             :    close).  That is:
     666             : 
     667             :      int err = fd_io_buffered_istream_fetch( in );
     668             :      if(      FD_LIKELY( err==0      ) ) ... success,     peek_sz() updated to at most rbuf_sz
     669             :      else if( FD_LIKELY( err< 0      ) ) ... end-of-file, peek_sz() updated to at most rbuf_sz and is num unconsumed bytes to EOF
     670             :      else if( FD_LIKELY( err==EAGAIN ) ) ... try again,   peek_sz() unchanged, only possible if fd is non-blocking
     671             :      else                                ... I/O error,   peek_sz() unchanged,
     672             :                                          ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     673             :                                          ... in and fd should be considered failed
     674             : 
     675             :    IMPORTANT!  See note in fd_io_buffered_istream_read above about the
     676             :    impact of fetch on file pointer alignment. */
     677             : 
     678             : FD_FN_UNUSED static int /* Work around -Winline */
     679        3456 : fd_io_buffered_istream_fetch( fd_io_buffered_istream_t * in ) {
     680        3456 :   uchar * rbuf       = in->rbuf;
     681        3456 :   ulong   rbuf_sz    = in->rbuf_sz;
     682        3456 :   ulong   rbuf_lo    = in->rbuf_lo;
     683        3456 :   ulong   rbuf_ready = in->rbuf_ready;
     684        3456 :   if( FD_UNLIKELY( rbuf_ready>=rbuf_sz ) ) return 0; /* buffer already full */
     685        3453 :   if( FD_LIKELY( (!!rbuf_ready) & (!!rbuf_lo) ) ) memmove( rbuf, rbuf+rbuf_lo, rbuf_ready ); /* Move unconsumed to beginning */
     686        3453 :   ulong   rsz;
     687        3453 :   int     err = fd_io_read( in->fd, rbuf+rbuf_ready, 0UL, rbuf_sz-rbuf_ready, &rsz );
     688        3453 :   in->rbuf_lo    = 0UL;
     689        3453 :   in->rbuf_ready = rbuf_ready + rsz;
     690        3453 :   return err;
     691        3456 : }
     692             : 
     693             : /* fd_io_buffered_ostream_init initializes out to do buffered writes to
     694             :    the given file descriptor.  out is an unused location that should
     695             :    hold the stream state, fd is an open normal-ish file descriptor to
     696             :    buffer, wbuf points to the first byte in the caller's address space
     697             :    to an unused wbuf_sz size memory region to use for the buffering
     698             :    (assumes wbuf is non-NULL with arbitrary alignment and wbuf_sz is
     699             :    positive).  Returns out and on return out will be initialized.  On
     700             :    return out will have ownership of fd and wbuf. */
     701             : 
     702             : static inline fd_io_buffered_ostream_t *
     703             : fd_io_buffered_ostream_init( fd_io_buffered_ostream_t * out,
     704             :                              int                        fd,
     705             :                              void *                     wbuf,
     706          12 :                              ulong                      wbuf_sz ) {
     707          12 :   out->fd        = fd;
     708          12 :   out->wbuf      = (uchar *)wbuf;
     709          12 :   out->wbuf_sz   = wbuf_sz;
     710          12 :   out->wbuf_used = 0UL;
     711          12 :   return out;
     712          12 : }
     713             : 
     714             : /* fd_io_buffered_ostream_{fd,wbuf,wbuf_sz} return the corresponding
     715             :    value used to initialize out.  Assumes out is initialized. */
     716             : 
     717           3 : FD_FN_PURE static inline int    fd_io_buffered_ostream_fd     ( fd_io_buffered_ostream_t const * out ) { return out->fd;      }
     718           3 : FD_FN_PURE static inline void * fd_io_buffered_ostream_wbuf   ( fd_io_buffered_ostream_t const * out ) { return out->wbuf;    }
     719           3 : FD_FN_PURE static inline ulong  fd_io_buffered_ostream_wbuf_sz( fd_io_buffered_ostream_t const * out ) { return out->wbuf_sz; }
     720             : 
     721             : /* fd_io_buffered_ostream_fini finalizes a buffered output stream.
     722             :    Assumes out is initialized.  On return out will no longer be
     723             :    initialized and the caller will have ownership of the underlying fd
     724             :    and wbuf.
     725             : 
     726             :    IMPORTANT!  THIS WILL NOT DO ANY FINAL FLUSH OF BUFFERED BYTES.  IT
     727             :    IS THE CALLER'S RESPONSIBILITY TO DO THIS IN THE NORMAL FINI CASE. */
     728             : 
     729             : static inline void
     730          12 : fd_io_buffered_ostream_fini( fd_io_buffered_ostream_t * out ) {
     731          12 :   (void)out;
     732          12 : }
     733             : 
     734             : /* fd_io_buffered_ostream_write writes src_sz bytes from src to the
     735             :    stream, temporarily buffering zero or more bytes as convenient.
     736             :    Assume out is initialized.  src / src_sz have the same meaning /
     737             :    restrictions as fd_io_buffered_write.  Returns 0 on success and
     738             :    non-zero on failure.  Failure interpretation is the same as
     739             :    fd_io_buffered_write.  On failure, out and the underlying file
     740             :    descriptor should be considered to be in a failed state (e.g.  the
     741             :    only valid thing to do to out is fini and the only valid thing to do
     742             :    to fd is close).
     743             : 
     744             :    IMPORTANT!  If fd_io_buffered_ostream_flush is only used to do a
     745             :    final flush before fini, all the writes to the underlying stream will
     746             :    always be at multiples of wbuf_sz offset from the initial file offset
     747             :    when the out was initialized and all the write sizes (except
     748             :    potentially the final flush) will be a multiple of wbuf_sz in size.
     749             :    This can be beneficial in various high performance I/O regimes. */
     750             : 
     751             : static inline int
     752             : fd_io_buffered_ostream_write( fd_io_buffered_ostream_t * out,
     753             :                               void const *               src,
     754         561 :                               ulong                      src_sz ) {
     755             :   /* We destructure out to avoid pointer escapes that might inhibit
     756             :      optimizations of other inlines that operate on out. */
     757         561 :   ulong wsz = out->wbuf_used;
     758         561 :   int   err = fd_io_buffered_write( out->fd, src, src_sz, out->wbuf, out->wbuf_sz, &wsz );
     759         561 :   out->wbuf_used = wsz;
     760         561 :   return err;
     761         561 : }
     762             : 
     763             : /* fd_io_buffered_ostream_peek returns a pointer in the caller's address
     764             :    space where the caller can prepare bytes to be streamed out.  Assumes
     765             :    out is initialized.  The returned pointer can have arbitrary
     766             :    alignment and the returned pointer lifetime is until the next write,
     767             :    flush, or fini. */
     768             : 
     769             : FD_FN_PURE static inline void *
     770        3471 : fd_io_buffered_ostream_peek( fd_io_buffered_ostream_t * out ) {
     771        3471 :   return out->wbuf + out->wbuf_used;
     772        3471 : }
     773             : 
     774             : /* fd_io_buffered_istream_peek_sz returns the number of bytes available
     775             :    at the peek location.  Assumes out is initialized.  Returned value
     776             :    will be in [0,wbuf_sz] and will be valid until the next write, fetch,
     777             :    seek or fini. */
     778             : 
     779             : FD_FN_PURE static inline ulong
     780        5184 : fd_io_buffered_ostream_peek_sz( fd_io_buffered_ostream_t * out ) {
     781        5184 :   return out->wbuf_sz - out->wbuf_used;
     782        5184 : }
     783             : 
     784             : /* fd_io_buffered_istream_seek commits the next sz unused write buffer
     785             :    bytes to be streamed out.  Assumes out is initialized and that sz is
     786             :    at most peek_sz. */
     787             : 
     788             : static inline void
     789             : fd_io_buffered_ostream_seek( fd_io_buffered_ostream_t * out,
     790        3444 :                              ulong                      sz ) {
     791        3444 :   out->wbuf_used += sz;
     792        3444 : }
     793             : 
     794             : /* fd_io_buffered_ostream_flush writes any buffered bytes in the stream's
     795             :    write buffer to the underlying file descriptor.  Assume out is
     796             :    initialized.  Returns 0 on success (all buffered bytes written to fd)
     797             :    and non-zero on failure (see below for interpretation).  In both
     798             :    cases, the write buffer will be empty on return.  On failure, out and
     799             :    the underlying file descriptor should be considered to be in a failed
     800             :    state (e.g. the only valid thing to do to out is fini and the only
     801             :    valid thing to do to fd is close).
     802             : 
     803             :      int err = fd_io_buffered_ostream_flush( out );
     804             :      if( FD_LIKELY( err==0 ) ) ... success,   write buffer empty
     805             :      else                      ... I/O error, write buffer empty
     806             :                                ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     807             :                                ... in and fd should be considered failed
     808             : 
     809             :    IMPORTANT!  See note in fd_io_buffered_ostream_write below about the
     810             :    impact of doing this outside a final flush. */
     811             : 
     812             : FD_FN_UNUSED static int /* Work around -Winline */
     813        1779 : fd_io_buffered_ostream_flush( fd_io_buffered_ostream_t * out ) {
     814        1779 :   ulong wbuf_used = out->wbuf_used;
     815        1779 :   if( FD_UNLIKELY( !wbuf_used ) ) return 0; /* optimize for lots of tiny writes */
     816        1779 :   out->wbuf_used = 0UL;
     817        1779 :   ulong wsz;
     818        1779 :   return fd_io_write( out->fd, out->wbuf, wbuf_used, wbuf_used, &wsz );
     819        1779 : }
     820             : 
     821             : /* Memory mapped I/O APIs */
     822             : 
     823             : /* fd_io_mmio_init starts memory mapped I/O on the file underlying
     824             :    the given file descriptor.  Specifically, it maps the file into the
     825             :    caller's address space according to the given FD_IO_MMIO_MODE.
     826             : 
     827             :    On success, returns 0.  On return, *(caller_mmio_t *)_mmio will point
     828             :    to the first byte in the caller's address space where the file was
     829             :    mapped (will be aligned at least 4096 currently) and *_mmio_sz will
     830             :    contain the region's byte size (which is the same as the file's
     831             :    size).  The mapping's lifetime will be until the corresponding fini
     832             :    or the process/thread group terminates (normally or not).  If the
     833             :    file has zero size, the region will be NULL and non-NULL otherwise.
     834             :    The usual POSIX semantics for fd and the underlying file apply.  In
     835             :    particular, fd can be closed and/or the file deleted during memory
     836             :    mapped I/O without issue.  Retains no interest in mmio or mmio_sz.
     837             : 
     838             :    On failure, returns a non-zero strerror compatible error code.  On
     839             :    return, the region will be an NULL with zero length.  Reasons for
     840             :    failure include all usual the fd_io_sz reasons and mmap reasons (e.g.
     841             :    fd is a file descriptor of a memory mappable file).  Retains no
     842             :    interest in mmio or mmio_sz.
     843             : 
     844             :    IMPORTANT SAFETY TIP!  Changes to the file via the region are not
     845             :    guaranteed visible to others until the corresponding fini.
     846             :    Conversely, changes by others to the file during memory mapped I/O
     847             :    may not become visible during memory mapped I/O.  Lastly, concurrent
     848             :    memory mapped I/O to the same file (whether via the same file
     849             :    descriptor or not and/or within the same thread group or not) is
     850             :    supported currently.  E.g. a slow but portable init/fini
     851             :    implementation under the hood could, on init, allocate a suitably
     852             :    aligned memory region, read the entire file into that region and
     853             :    return that region and, on fini, write the entire region back to the
     854             :    file (if applicable) and free it while an optimized implementation
     855             :    could use target specific virtual memory APIs to eliminate excess
     856             :    alloc/free/read/write operations. */
     857             : 
     858             : int
     859             : fd_io_mmio_init( int     fd,
     860             :                  int     mode,
     861             :                  void *  _mmio,      /* Psychologically, a "caller_mmio_t **" */
     862             :                  ulong * _mmio_sz );
     863             : 
     864             : /* fd_io_mmio_fini finishes memory mapped I/O on a file.  mmio and
     865             :    mmio_sz should be region where the memory mapped I/O is in progress.
     866             :    Memory mapped I/O will not be in progress on return.  Guaranteed not
     867             :    to fail from the caller's point of view.  See fd_shmem_mmio_init for
     868             :    more details. */
     869             : 
     870             : void
     871             : fd_io_mmio_fini( void const * mmio,
     872             :                  ulong        mmio_sz );
     873             : 
     874             : /* TODO: consider a fd_io_mmio_sync API to allow changes to an
     875             :    in-progress mmio to be made visible? */
     876             : 
     877             : /* Misc APIs */
     878             : 
     879             : /* fd_io_strerror converts an fd_io error code (i.e. negative ->
     880             :    end-of-file, 0 -> success, positive -> strerror compatible) into a
     881             :    human readable cstr.  Unlike strerror, the lifetime of the returned
     882             :    pointer is infinite and the call itself is thread safe.  The
     883             :    returned pointer is always to a non-NULL cstr. */
     884             : 
     885             : FD_FN_CONST char const *
     886             : fd_io_strerror( int err );
     887             : 
     888             : /* fd_io_strsignal converts a signal code (like returned by WTERMSIG)
     889             :    into a human readable cstr.  Unlike strsignal, the lifetime of the
     890             :    returned pointer is infinite and the call itself is thread safe.
     891             :    Unlike the glibc strsignal implementation in particular, it does
     892             :    not call `brk(3)` or `futex(2)` internally.  The returned pointer
     893             :    is always to a non-NULL cstr. */
     894             : FD_FN_CONST char const *
     895             : fd_io_strsignal( int err );
     896             : 
     897             : /* TODO: ASYNC IO APIS */
     898             : 
     899             : FD_PROTOTYPES_END
     900             : 
     901             : #endif /* HEADER_fd_src_util_io_fd_io_h */

Generated by: LCOV version 1.14