LCOV - code coverage report
Current view: top level - util/io - fd_io.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 88 88 100.0 %
Date: 2024-11-13 11:58:15 Functions: 42 13587 0.3 %

          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_buffered_{istream,ostream}_t is an opaque handle of an
     189             :    {input,output} stream with buffered {reads,writes}.  The internals
     190             :    are visible here to facilitate inlining various operations.  This is
     191             :    declaration friendly (e.g. usually should just do
     192             :    "fd_io_buffered_istream_t in[1];" on the stack to get a suitable
     193             :    memory region for the stream state).  These are not meant to be
     194             :    persistent or shared IPC. */
     195             : 
     196             : struct fd_io_buffered_istream_private {
     197             :   int     fd;         /* Open normal-ish file descriptor of stream */
     198             :   uchar * rbuf;       /* Read buffer, non-NULL, indexed [0,rbuf_sz), arb alignment */
     199             :   ulong   rbuf_sz;    /* Read buffer size, positive */
     200             :   ulong   rbuf_lo;    /* Buf bytes [0,rbuf_lo) have already been consumed */
     201             :   ulong   rbuf_ready; /* Number of buffered byte that haven't been consumed, 0<=rbuf_lo<=(rbuf_lo+rbuf_ready)<=rbuf_sz */
     202             : };
     203             : 
     204             : typedef struct fd_io_buffered_istream_private fd_io_buffered_istream_t;
     205             : 
     206             : struct fd_io_buffered_ostream_private {
     207             :   int     fd;        /* Open normal-ish file descriptor of stream */
     208             :   uchar * wbuf;      /* Write buffer, non-NULL, indexed [0,wbuf_sz), arb alignment */
     209             :   ulong   wbuf_sz;   /* Write buffer size, positive */
     210             :   ulong   wbuf_used; /* Number buffered bytes that haven't been written to fd, in [0,wbuf_sz] */
     211             : };
     212             : 
     213             : typedef struct fd_io_buffered_ostream_private fd_io_buffered_ostream_t;
     214             : 
     215             : FD_PROTOTYPES_BEGIN
     216             : 
     217             : /* fd_io_read streams at least dst_min bytes from the given file
     218             :    descriptor into the given memory region.  fd should be an open
     219             :    normal-ish file descriptor (it is okay for fd to be non-blocking).
     220             :    dst points in the caller's address space with arbitrary alignment to
     221             :    the first byte of the dst_max byte memory region to use (assumes dst
     222             :    non-NULL, and dst_min is at most dst_max).  The caller should not
     223             :    read or write this region during the call and no interest in dst is
     224             :    retained on return.  If dst_min is 0, this will try to read dst_max
     225             :    from the stream exactly once.  If dst_max is 0, is a no-op.
     226             : 
     227             :    Returns 0 on success.  On success, *_dst_sz will be the number of
     228             :    bytes read into dst.  Will be in [dst_min,dst_max].
     229             : 
     230             :    Returns a negative number if end-of-file was encountered before
     231             :    reading dst_min bytes.  *_dst_sz will be the number bytes read into
     232             :    dst when the end-of-file was encountered.  Will be in [0,dst_min).
     233             : 
     234             :    Returns an errno compatible error code on failure (note that all
     235             :    errnos are positive).  If errno is anything other than EAGAIN, the
     236             :    underlying fd should be considered to be in a failed state such that
     237             :    the only valid operation on fd is to close it.  *_dst_sz will be zero
     238             :    and the contents of dst will be undefined.  This API fixes up the
     239             :    POSIX glitches around EWOULDBLOCK / EAGAIN: if the underlying target
     240             :    has EWOULDBLOCK different from EAGAIN and read uses EWOULDBLOCK
     241             :    instead of EAGAIN, this will still just return EAGAIN.  EAGAIN will
     242             :    only be returned if the underlying fd is non-blocking and dst_min is
     243             :    zero.
     244             : 
     245             :    TL;DR
     246             : 
     247             :    - dst_min is positive:
     248             : 
     249             :        ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
     250             :        if     ( FD_LIKELY( err==0 ) ) ... success, dst_sz in [dst_min,dst_max], dst updated
     251             :        else if( FD_LIKELY( err< 0 ) ) ... EOF, dst_sz in [0,dst_min), dst updated
     252             :        else                           ... I/O error, dst_sz is zero, dst clobbered
     253             :                                       ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     254             :                                       ... fd should be considered failed
     255             : 
     256             :      This is equivalent to looping over reads of up to dst_max in size
     257             :      from fd until at least dst_min bytes are read.  It does not matter
     258             :      if fd is blocking or not.
     259             : 
     260             :    - dst_min is zero and fd is blocking:
     261             : 
     262             :        ulong dst_sz; int err = fd_io_read( fd, dst, dst_min, dst_max, &dst_sz );
     263             :        if     ( FD_LIKELY( err==0 ) ) ... success, dst_sz in [1,dst_max], dst updated
     264             :        else if( FD_LIKELY( err< 0 ) ) ... EOF, dst_sz is zero, dst unchanged
     265             :        else                           ... I/O error, dst_sz is zero, dst clobbered
     266             :                                       ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     267             :                                       ... fd should be considered failed
     268             : 
     269             :      This is equivalent to a single read of dst_max size on a blocking
     270             :      fd.
     271             : 
     272             :    - dst_min is zero and fd is non-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==EAGAIN ) ) ... no data available now, try again later, dst_sz is zero, dst unchanged
     277             :        else if( FD_LIKELY( err< 0      ) ) ... EOF, dst_sz is zero, dst unchanged
     278             :        else                                ... I/O error, dst_sz is zero, dst clobbered
     279             :                                            ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     280             :                                            ... fd should be considered failed
     281             : 
     282             :      This is equivalent to a single read of dst_max size on a
     283             :      non-blocking fd (with the POSIX glitches around EAGAIN /
     284             :      EWOULDBLOCK cleaned up). */
     285             : 
     286             : int
     287             : fd_io_read( int     fd,
     288             :             void *  dst,
     289             :             ulong   dst_min,
     290             :             ulong   dst_max,
     291             :             ulong * _dst_sz );
     292             : 
     293             : /* fd_io_write behaves virtually identical to fd_io_read but the
     294             :    direction of the transfer is from memory to the stream and there is
     295             :    no notion of EOF handling.  Assumes src is non-NULL,
     296             :    src_min<=src_max, src_sz is non-NULL and non-overlapping with src.
     297             :    If src_max is 0, is a no-op.  Summarizing:
     298             : 
     299             :    - src_min is positive:
     300             : 
     301             :        ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
     302             :        if( FD_LIKELY( err==0 ) ) ... success, src_sz in [src_min,src_max]
     303             :        else                      ... I/O error, src_sz is zero
     304             :                                  ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     305             :                                  ... fd should be considered failed
     306             : 
     307             :      This is equivalent to looping over writes of up to src_max in size
     308             :      to fd until at least src_min bytes are written.  It does not matter
     309             :      if fd is blocking or not.
     310             : 
     311             :    - src_min is zero and fd is blocking:
     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 [1,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 a single write of src_max size on a blocking
     320             :      fd.
     321             : 
     322             :    - src_min is zero and fd is non-blocking:
     323             : 
     324             :        ulong src_sz; int err = fd_io_write( fd, src, src_min, src_max, &src_sz );
     325             :        if     ( FD_LIKELY( err==0      ) ) ... success, src_sz in [1,src_max]
     326             :        else if( FD_LIKELY( err==EAGAIN ) ) ... no bytes written, try again later, src_sz is zero
     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
     332             :      non-blocking fd (with the POSIX glitches around EAGAIN /
     333             :      EWOULDBLOCK fixed up). */
     334             : 
     335             : int
     336             : fd_io_write( int          fd,
     337             :              void const * src,
     338             :              ulong        src_min,
     339             :              ulong        src_max,
     340             :              ulong *      _src_sz );
     341             : 
     342             : /* fd_io_buffered_read is like fd_io_read but can consolidate many
     343             :    tiny reads into a larger fd_io_read via the given buffer.  Unlike
     344             :    fd_io_read, dst NULL is okay if dst_sz is 0 (dst_sz 0 is a no-op that
     345             :    immediately returns success).  Will block the caller until the read
     346             :    is complete or an end-of-file was encountered.
     347             : 
     348             :    rbuf points to the first byte of a rbuf_sz size memory region in the
     349             :    caller's address space used for read buffering (assumes rbuf is
     350             :    non-NULL with arbitrary alignment and rbuf_sz is positive).  On entry
     351             :    *_rbuf_lo is where the first byte of unconsumed buffered reads are
     352             :    located and *_rbuf_ready is number of unconsumed buffered bytes.
     353             :    Assumes 0<=*_rbuf_lo<=(*_rbuf_lo+*_rbuf_ready)<=rbuf_sz.
     354             : 
     355             :    Returns 0 on success.  dst will hold dst_sz bytes from the stream.
     356             :    *_rbuf_lo and *_rbuf_ready will be updated and the above invariant on
     357             :    *_rbuf_lo and *_rbuf_ready will still hold.
     358             : 
     359             :    Returns non-zero on failure.  Failure indicates that dst_sz bytes
     360             :    could not be read because of an I/O error (return will be a positive
     361             :    errno compatible error code) or an end-of-file was encountered
     362             :    (return will be a negative number).  dst should be assumed to have
     363             :    been clobbered, *_rbuf_lo and *_rbuf_ready will be zero.  If an I/O
     364             :    error, the fd should be considered to be in a failed state such that
     365             :    the only valid operation on it is to close it.
     366             : 
     367             :    IMPORTANT!  This function will only read from fd in multiples of
     368             :    rbuf_sz (except for a potentially last incomplete block before an
     369             :    end-of-file).  This can be useful for various ultra high performance
     370             :    contexts.
     371             : 
     372             :    This API usually should not be used directly.  It is mostly useful
     373             :    for implementing higher level APIs like fd_io_buffered_istream below. */
     374             : 
     375             : int
     376             : fd_io_buffered_read( int     fd,
     377             :                      void *  dst,
     378             :                      ulong   dst_sz,
     379             :                      void *  rbuf,
     380             :                      ulong   rbuf_sz,
     381             :                      ulong * _rbuf_lo,
     382             :                      ulong * _rbuf_ready );
     383             : 
     384             : /* fd_io_buffered_skip is like fd_io_buffered_read but will skip over
     385             :    skip_sz bytes of the stream without copying them into a user buffer.
     386             :    If stream fd is seekable (e.g. a normal file), this should be O(1).
     387             :    If not (e.g. fd is pipe / socket / stdin / etc), this will block the
     388             :    caller until skip_sz bytes have been skipped or an I/O error occurs.
     389             : 
     390             :    IMPORTANT!  If stream fd is seekable, POSIX behaviors allow seeking
     391             :    past end-of-file (apparently even if fd is read only).  Whether or
     392             :    not this is a good idea is debatable.  The result though is this API
     393             :    will usually not return an error if skip_sz moves past the
     394             :    end-of-file (however, if skip_sz is so large that it causes the file
     395             :    offset to overflow, this will return EOVERFLOW).  In particular, this
     396             :    API cannot be used to detect end-of-file.
     397             : 
     398             :    IMPORTANT!  This function makes no effort to skip in multiples of
     399             :    rbuf_sz.  Such is up to the caller to do if such is desirable.
     400             : 
     401             :    This API usually should not be used directly.  It is mostly useful
     402             :    for implementing higher level APIs like fd_io_buffered_istream below. */
     403             : 
     404             : int
     405             : fd_io_buffered_skip( int     fd,
     406             :                      ulong   skip_sz,
     407             :                      void *  rbuf,
     408             :                      ulong   rbuf_sz,
     409             :                      ulong * _rbuf_lo,
     410             :                      ulong * _rbuf_ready );
     411             : 
     412             : /* fd_io_buffered_write is like fd_io_write but can consolidate many
     413             :    tiny writes into a larger fd_io_write via the given buffer.  Unlike
     414             :    fd_io_write, src NULL is okay if src_sz is 0 (src_sz 0 is a no-op
     415             :    that immediately returns success).  Will block the caller until the
     416             :    write is complete or an end-of-file was encountered.
     417             : 
     418             :    wbuf points to the first byte of a wbuf_sz size memory region in the
     419             :    caller's address space (assumes wbuf is non-NULL with arbitrary
     420             :    alignment and wbuf_sz is positive).  On entry *_wbuf_used is the
     421             :    number of bytes in wbuf from previous buffered writes that have not
     422             :    yet been streamed out.  Assumes *_wbuf_used is in [0,wbuf_sz].
     423             : 
     424             :    Returns 0 on success.  wbuf will hold *_wbuf_used bytes not yet
     425             :    written to fd by write and/or previous buffered writes.  The above
     426             :    invariant on *_wbuf_used will still hold.
     427             : 
     428             :    Returns non-zero on failure.  Failure indicates that src_sz bytes
     429             :    could not be written because of an I/O error (return will be a
     430             :    positive errno compatible error code).  fd should be considered to be
     431             :    in a failed state such that the only valid operation on it is to
     432             :    close it.  *_wbuf_used will be 0 and the contents of wbuf will be
     433             :    undefined.  Zero or more bytes of previously buffered writes and/or
     434             :    src might have been written before the failure.
     435             : 
     436             :    IMPORTANT!  This function will only write to fd in multiples of
     437             :    wbuf_sz.  This can be useful for various ultra high performance
     438             :    contexts.
     439             : 
     440             :    This API usually should not be used directly.  It is mostly useful
     441             :    for implementing higher level APIs like fd_io_buffered_ostream below. */
     442             : 
     443             : int
     444             : fd_io_buffered_write( int          fd,
     445             :                       void const * src,
     446             :                       ulong        src_sz,
     447             :                       void *       wbuf,
     448             :                       ulong        wbuf_sz,
     449             :                       ulong *      _wbuf_used );
     450             : 
     451             : /* fd_io_buffered_istream_init initializes in to do buffered reads from
     452             :    the given file descriptor.  in is an unused location that should hold
     453             :    the buffering state, fd is an open normal-ish file descriptor, rbuf
     454             :    points to the first byte in the caller's address space to an unused
     455             :    rbuf_sz size memory region to use for read buffering (assumes rbuf is
     456             :    non-NULL with arbitrary alignment and rbuf_sz is positive).  Returns
     457             :    in and on return in will be initialized.  in will have ownership of
     458             :    fd and rbuf while initialized. */
     459             : 
     460             : static inline fd_io_buffered_istream_t *
     461             : fd_io_buffered_istream_init( fd_io_buffered_istream_t * in,
     462             :                              int                        fd,
     463             :                              void *                     rbuf,
     464         339 :                              ulong                      rbuf_sz ) {
     465         339 :   in->fd         = fd;
     466         339 :   in->rbuf       = (uchar *)rbuf;
     467         339 :   in->rbuf_sz    = rbuf_sz;
     468         339 :   in->rbuf_lo    = 0UL;
     469         339 :   in->rbuf_ready = 0UL;
     470         339 :   return in;
     471         339 : }
     472             : 
     473             : /* fd_io_buffered_istream_{fd,rbuf,rbuf_sz} return the corresponding
     474             :    value used to initialize in.  Assumes in is initialized. */
     475             : 
     476           3 : FD_FN_PURE static inline int    fd_io_buffered_istream_fd     ( fd_io_buffered_istream_t const * in ) { return in->fd;      }
     477           3 : FD_FN_PURE static inline void * fd_io_buffered_istream_rbuf   ( fd_io_buffered_istream_t const * in ) { return in->rbuf;    }
     478           3 : FD_FN_PURE static inline ulong  fd_io_buffered_istream_rbuf_sz( fd_io_buffered_istream_t const * in ) { return in->rbuf_sz; }
     479             : 
     480             : /* fd_io_buffered_istream_fini finalizes a buffered input stream.
     481             :    Assumes in is initialized.  On return in will no longer be
     482             :    initialized and ownership the underlying fd and rbuf will return to
     483             :    the caller.
     484             : 
     485             :    IMPORTANT!  THIS WILL NOT REPOSITION THE UNDERLYING FD FILE OFFSET
     486             :    (SUCH MIGHT NOT EVEN BE POSSIBLE) TO "UNREAD" ANY UNCONSUMED BUFFERED
     487             :    DATA. */
     488             : 
     489             : static inline void
     490          39 : fd_io_buffered_istream_fini( fd_io_buffered_istream_t * in ) {
     491          39 :   (void)in;
     492          39 : }
     493             : 
     494             : /* fd_io_buffered_istream_read reads dst_sz bytes from in to dst,
     495             :    reading ahead as convenient.  Assumes in is initialized.  dst /
     496             :    dst_sz have the same meaning / restrictions as fd_io_buffered_read.
     497             :    Returns 0 on success and non-zero on failure.  Failure interpretation
     498             :    is the same as fd_io_buffered_read.  On failure, in and the
     499             :    underlying file descriptor should be considered to be in a failed
     500             :    state (e.g. the only valid thing to do to in is fini and the only
     501             :    valid thing to do to fd is close).
     502             : 
     503             :    IMPORTANT!  If fd_io_buffered_istream_{fetch,skip} below are never
     504             :    used (or only used to skip in multiplies of rbuf_sz), all the reads
     505             :    from the underlying stream will always be at multiples of rbuf_sz
     506             :    from the file offset when the in was initialized and a multiple of
     507             :    rbuf_sz in size (except possibly a final read to the end-of-file).
     508             :    This can be beneficial in various high performance I/O regimes. */
     509             : 
     510             : FD_FN_UNUSED static int /* Work around -Winline */
     511             : fd_io_buffered_istream_read( fd_io_buffered_istream_t * in,
     512             :                              void *                     dst,
     513       37311 :                              ulong                      dst_sz ) {
     514             :   /* We destructure in to avoid pointer escapes that might inhibit
     515             :      optimizations of other in inlines. */
     516       37311 :   ulong rbuf_lo    = in->rbuf_lo;
     517       37311 :   ulong rbuf_ready = in->rbuf_ready;
     518       37311 :   int err = fd_io_buffered_read( in->fd, dst, dst_sz, in->rbuf, in->rbuf_sz, &rbuf_lo, &rbuf_ready );
     519       37311 :   in->rbuf_lo    = rbuf_lo;
     520       37311 :   in->rbuf_ready = rbuf_ready;
     521       37311 :   return err;
     522       37311 : }
     523             : 
     524             : /* fd_io_buffered_istream_skip skips skip_sz bytes from in.  Assumes in
     525             :    is initialized.  Returns 0 on success and non-zero on failure.
     526             :    Failure interpretation is the same as fd_io_buffered_read.  On a
     527             :    failure, in and the underlying file descriptor should be considered
     528             :    to be in a failed state (e.g. the only valid thing to do to in is
     529             :    fini and the only valid thing to do to fd is close).
     530             : 
     531             :    If the fd underlying in is seekable (e.g. a file), this will be very
     532             :    fast.  If not (e.g. fd is pipe / socket / etc), this can block the
     533             :    caller until skip_sz bytes have arrived or an I/O error is detected.
     534             : 
     535             :    IMPORTANT!  See note in fd_io_buffered_istream_read above about the
     536             :    impact of this on file pointer alignment. */
     537             : 
     538             : static inline int
     539             : fd_io_buffered_istream_skip( fd_io_buffered_istream_t * in,
     540        1197 :                              ulong                      skip_sz ) {
     541             :   /* We destructure in to avoid pointer escapes that might inhibit
     542             :      optimizations of other in inlines. */
     543        1197 :   ulong rbuf_lo    = in->rbuf_lo;
     544        1197 :   ulong rbuf_ready = in->rbuf_ready;
     545        1197 :   int err = fd_io_buffered_skip( in->fd, skip_sz, in->rbuf, in->rbuf_sz, &rbuf_lo, &rbuf_ready );
     546        1197 :   in->rbuf_lo    = rbuf_lo;
     547        1197 :   in->rbuf_ready = rbuf_ready;
     548        1197 :   return err;
     549        1197 : }
     550             : 
     551             : /* fd_io_buffered_istream_peek returns a pointer in the caller's address
     552             :    space to the first byte that has been read but not yet consumed.
     553             :    Assumes in is initialized.  The returned pointer can have arbitrary
     554             :    alignment and the returned pointer lifetime is until the next read,
     555             :    fetch, or fini. */
     556             : 
     557             : FD_FN_PURE static inline void const *
     558        7578 : fd_io_buffered_istream_peek( fd_io_buffered_istream_t * in ) {
     559        7578 :   return in->rbuf + in->rbuf_lo;
     560        7578 : }
     561             : 
     562             : /* fd_io_buffered_istream_peek_sz returns the number of bytes that have
     563             :    been read but not yet consumed.  Assumes in is initialized.  Returned
     564             :    value will be in [0,rbuf_sz] and will be valid until the next read,
     565             :    fetch, seek or fini. */
     566             : 
     567             : FD_FN_PURE static inline ulong
     568       11055 : fd_io_buffered_istream_peek_sz( fd_io_buffered_istream_t * in ) {
     569       11055 :   return in->rbuf_ready;
     570       11055 : }
     571             : 
     572             : /* fd_io_buffered_istream_seek consumes sz buffered bytes from in.
     573             :    Assumes in is initialized and that sz is at most peek_sz. */
     574             : 
     575             : static inline void
     576             : fd_io_buffered_istream_seek( fd_io_buffered_istream_t * in,
     577        7578 :                              ulong                      sz ) {
     578        7578 :   in->rbuf_lo    += sz;
     579        7578 :   in->rbuf_ready -= sz;
     580        7578 : }
     581             : 
     582             : /* fd_io_buffered_istream_fetch tries to fill up the stream's read
     583             :    buffer with as many unconsumed bytes as possible.  Assumes in is
     584             :    initialized.  Returns 0 on success (rbuf is filled to rbuf_sz with
     585             :    unconsumed data) and non-zero on failure (see below for
     586             :    interpretation).  On failure, in and the underlying file descriptor
     587             :    should be considered to be in a failed state (e.g. the only valid
     588             :    thing to do out on is fini and the only valid thing to do on fd is
     589             :    close).  That is:
     590             : 
     591             :      int err = fd_io_buffered_istream_fetch( in );
     592             :      if(      FD_LIKELY( err==0      ) ) ... success,     peek_sz() updated to at most rbuf_sz
     593             :      else if( FD_LIKELY( err< 0      ) ) ... end-of-file, peek_sz() updated to at most rbuf_sz and is num unconsumed bytes to EOF
     594             :      else if( FD_LIKELY( err==EAGAIN ) ) ... try again,   peek_sz() unchanged, only possible if fd is non-blocking
     595             :      else                                ... I/O error,   peek_sz() unchanged,
     596             :                                          ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     597             :                                          ... in and fd should be considered failed
     598             : 
     599             :    IMPORTANT!  See note in fd_io_buffered_istream_read above about the
     600             :    impact of fetch on file pointer alignment. */
     601             : 
     602             : FD_FN_UNUSED static int /* Work around -Winline */
     603        3471 : fd_io_buffered_istream_fetch( fd_io_buffered_istream_t * in ) {
     604        3471 :   uchar * rbuf       = in->rbuf;
     605        3471 :   ulong   rbuf_sz    = in->rbuf_sz;
     606        3471 :   ulong   rbuf_lo    = in->rbuf_lo;
     607        3471 :   ulong   rbuf_ready = in->rbuf_ready;
     608        3471 :   if( FD_UNLIKELY( rbuf_ready>=rbuf_sz ) ) return 0; /* buffer already full */
     609        3468 :   if( FD_LIKELY( (!!rbuf_ready) & (!!rbuf_lo) ) ) memmove( rbuf, rbuf+rbuf_lo, rbuf_ready ); /* Move unconsumed to beginning */
     610        3468 :   ulong   rsz;
     611        3468 :   int     err = fd_io_read( in->fd, rbuf+rbuf_ready, 0UL, rbuf_sz-rbuf_ready, &rsz );
     612        3468 :   in->rbuf_lo    = 0UL;
     613        3468 :   in->rbuf_ready = rbuf_ready + rsz;
     614        3468 :   return err;
     615        3471 : }
     616             : 
     617             : /* fd_io_buffered_ostream_init initializes out to do buffered writes to
     618             :    the given file descriptor.  out is an unused location that should
     619             :    hold the stream state, fd is an open normal-ish file descriptor to
     620             :    buffer, wbuf points to the first byte in the caller's address space
     621             :    to an unused wbuf_sz size memory region to use for the buffering
     622             :    (assumes wbuf is non-NULL with arbitrary alignment and wbuf_sz is
     623             :    positive).  Returns out and on return out will be initialized.  On
     624             :    return out will have ownership of fd and wbuf. */
     625             : 
     626             : static inline fd_io_buffered_ostream_t *
     627             : fd_io_buffered_ostream_init( fd_io_buffered_ostream_t * out,
     628             :                              int                        fd,
     629             :                              void *                     wbuf,
     630         312 :                              ulong                      wbuf_sz ) {
     631         312 :   out->fd        = fd;
     632         312 :   out->wbuf      = (uchar *)wbuf;
     633         312 :   out->wbuf_sz   = wbuf_sz;
     634         312 :   out->wbuf_used = 0UL;
     635         312 :   return out;
     636         312 : }
     637             : 
     638             : /* fd_io_buffered_ostream_{fd,wbuf,wbuf_sz} return the corresponding
     639             :    value used to initialize out.  Assumes out is initialized. */
     640             : 
     641           3 : FD_FN_PURE static inline int    fd_io_buffered_ostream_fd     ( fd_io_buffered_ostream_t const * out ) { return out->fd;      }
     642           3 : FD_FN_PURE static inline void * fd_io_buffered_ostream_wbuf   ( fd_io_buffered_ostream_t const * out ) { return out->wbuf;    }
     643           3 : FD_FN_PURE static inline ulong  fd_io_buffered_ostream_wbuf_sz( fd_io_buffered_ostream_t const * out ) { return out->wbuf_sz; }
     644             : 
     645             : /* fd_io_buffered_ostream_fini finalizes a buffered output stream.
     646             :    Assumes out is initialized.  On return out will no longer be
     647             :    initialized and the caller will have ownership of the underlying fd
     648             :    and wbuf.
     649             : 
     650             :    IMPORTANT!  THIS WILL NOT DO ANY FINAL FLUSH OF BUFFERED BYTES.  IT
     651             :    IS THE CALLER'S RESPONSIBILITY TO DO THIS IN THE NORMAL FINI CASE. */
     652             : 
     653             : static inline void
     654          12 : fd_io_buffered_ostream_fini( fd_io_buffered_ostream_t * out ) {
     655          12 :   (void)out;
     656          12 : }
     657             : 
     658             : /* fd_io_buffered_ostream_write writes src_sz bytes from src to the
     659             :    stream, temporarily buffering zero or more bytes as convenient.
     660             :    Assume out is initialized.  src / src_sz have the same meaning /
     661             :    restrictions as fd_io_buffered_write.  Returns 0 on success and
     662             :    non-zero on failure.  Failure interpretation is the same as
     663             :    fd_io_buffered_write.  On failure, out and the underlying file
     664             :    descriptor should be considered to be in a failed state (e.g.  the
     665             :    only valid thing to do to out is fini and the only valid thing to do
     666             :    to fd is close).
     667             : 
     668             :    IMPORTANT!  If fd_io_buffered_ostream_flush is only used to do a
     669             :    final flush before fini, all the writes to the underlying stream will
     670             :    always be at multiples of wbuf_sz offset from the initial file offset
     671             :    when the out was initialized and all the write sizes (except
     672             :    potentially the final flush) will be a multiple of wbuf_sz in size.
     673             :    This can be beneficial in various high performance I/O regimes. */
     674             : 
     675             : static inline int
     676             : fd_io_buffered_ostream_write( fd_io_buffered_ostream_t * out,
     677             :                               void const *               src,
     678       36174 :                               ulong                      src_sz ) {
     679             :   /* We destructure out to avoid pointer escapes that might inhibit
     680             :      optimizations of other inlines that operate on out. */
     681       36174 :   ulong wsz = out->wbuf_used;
     682       36174 :   int   err = fd_io_buffered_write( out->fd, src, src_sz, out->wbuf, out->wbuf_sz, &wsz );
     683       36174 :   out->wbuf_used = wsz;
     684       36174 :   return err;
     685       36174 : }
     686             : 
     687             : /* fd_io_buffered_ostream_peek returns a pointer in the caller's address
     688             :    space where the caller can prepare bytes to be streamed out.  Assumes
     689             :    out is initialized.  The returned pointer can have arbitrary
     690             :    alignment and the returned pointer lifetime is until the next write,
     691             :    flush, or fini. */
     692             : 
     693             : FD_FN_PURE static inline void *
     694        3321 : fd_io_buffered_ostream_peek( fd_io_buffered_ostream_t * out ) {
     695        3321 :   return out->wbuf + out->wbuf_used;
     696        3321 : }
     697             : 
     698             : /* fd_io_buffered_istream_peek_sz returns the number of bytes available
     699             :    at the peek location.  Assumes out is initialized.  Returned value
     700             :    will be in [0,wbuf_sz] and will be valid until the next write, fetch,
     701             :    seek or fini. */
     702             : 
     703             : FD_FN_PURE static inline ulong
     704        5022 : fd_io_buffered_ostream_peek_sz( fd_io_buffered_ostream_t * out ) {
     705        5022 :   return out->wbuf_sz - out->wbuf_used;
     706        5022 : }
     707             : 
     708             : /* fd_io_buffered_istream_seek commits the next sz unused write buffer
     709             :    bytes to be streamed out.  Assumes out is initialized and that sz is
     710             :    at most peek_sz. */
     711             : 
     712             : static inline void
     713             : fd_io_buffered_ostream_seek( fd_io_buffered_ostream_t * out,
     714        3282 :                              ulong                      sz ) {
     715        3282 :   out->wbuf_used += sz;
     716        3282 : }
     717             : 
     718             : /* fd_io_buffered_ostream_flush writes any buffered bytes in the stream's
     719             :    write buffer to the underlying file descriptor.  Assume out is
     720             :    initialized.  Returns 0 on success (all buffered bytes written to fd)
     721             :    and non-zero on failure (see below for interpretation).  In both
     722             :    cases, the write buffer will be empty on return.  On failure, out and
     723             :    the underlying file descriptor should be considered to be in a failed
     724             :    state (e.g. the only valid thing to do to out is fini and the only
     725             :    valid thing to do to fd is close).
     726             : 
     727             :      int err = fd_io_buffered_ostream_flush( out );
     728             :      if( FD_LIKELY( err==0 ) ) ... success,   write buffer empty
     729             :      else                      ... I/O error, write buffer empty
     730             :                                ... err is strerror compat, err is neither EAGAIN nor EWOULDBLOCK
     731             :                                ... in and fd should be considered failed
     732             : 
     733             :    IMPORTANT!  See note in fd_io_buffered_ostream_write below about the
     734             :    impact of doing this outside a final flush. */
     735             : 
     736             : FD_FN_UNUSED static int /* Work around -Winline */
     737        2088 : fd_io_buffered_ostream_flush( fd_io_buffered_ostream_t * out ) {
     738        2088 :   ulong wbuf_used = out->wbuf_used;
     739        2088 :   if( FD_UNLIKELY( !wbuf_used ) ) return 0; /* optimize for lots of tiny writes */
     740        2070 :   out->wbuf_used = 0UL;
     741        2070 :   ulong wsz;
     742        2070 :   return fd_io_write( out->fd, out->wbuf, wbuf_used, wbuf_used, &wsz );
     743        2088 : }
     744             : 
     745             : /* Misc APIs */
     746             : 
     747             : /* fd_io_strerror converts an fd_io error code (i.e. negative ->
     748             :    end-of-file, 0 -> success, positive -> strerror compatible) into a
     749             :    human readable cstr.  Unlike strerror, the lifetime of the returned
     750             :    pointer is infinite and the call itself is thread safe.  The
     751             :    returned pointer is always to a non-NULL cstr. */
     752             : 
     753             : FD_FN_CONST char const *
     754             : fd_io_strerror( int err );
     755             : 
     756             : /* fd_io_strsignal converts a signal code (like returned by WTERMSIG)
     757             :    into a human readable cstr.  Unlike strsignal, the lifetime of the
     758             :    returned pointer is infinite and the call itself is thread safe.
     759             :    Unlike the glibc strsignal implementation in particular, it does
     760             :    not call `brk(3)` or `futex(2)` internally.  The returned pointer
     761             :    is always to a non-NULL cstr. */
     762             : FD_FN_CONST char const *
     763             : fd_io_strsignal( int err );
     764             : 
     765             : /* TODO: ASYNC IO APIS */
     766             : 
     767             : FD_PROTOTYPES_END
     768             : 
     769             : #endif /* HEADER_fd_src_util_io_fd_io_h */

Generated by: LCOV version 1.14