LCOV - code coverage report
Current view: top level - util/io - fd_io.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 223 369 60.4 %
Date: 2025-01-08 12:08:44 Functions: 11 12 91.7 %

          Line data    Source code
       1             : #include "fd_io.h"
       2             : 
       3             : /* TODO: try to eliminate use of FD_LOG_STYLE in log if possible in
       4             :    favor of FD_IO_STYLE here. */
       5             : 
       6             : #ifndef FD_IO_STYLE
       7             : #if FD_HAS_HOSTED
       8             : #define FD_IO_STYLE 0
       9             : #else
      10             : #error "Define FD_IO_STYLE for this platform"
      11             : #endif
      12             : #endif
      13             : 
      14             : #if FD_IO_STYLE==0 /* POSIX style */
      15             : 
      16             : #include <errno.h>
      17             : #include <signal.h>
      18             : #include <unistd.h>
      19             : #include <sys/stat.h>
      20             : #include <sys/mman.h>
      21             : 
      22             : int
      23             : fd_io_read( int     fd,
      24             :             void *  _dst,
      25             :             ulong   dst_min,
      26             :             ulong   dst_max,
      27      544785 :             ulong * _dst_sz ) {
      28             : 
      29      544785 :   if( FD_UNLIKELY( dst_max==0UL ) ) {
      30          12 :     *_dst_sz = 0UL;
      31          12 :     return 0;
      32          12 :   }
      33             : 
      34      544773 :   uchar * dst = (uchar *)_dst;
      35             : 
      36      544773 :   ulong dst_sz = 0UL;
      37      544773 :   do {
      38             : 
      39             :     /* Note: POSIX indicates read with sz larger than SSIZE_MAX (which
      40             :        is the same as LONG_MAX here) is IB.  While this is an
      41             :        impractically large value nowadays, we don't take chances. */
      42             : 
      43      544773 :     long  ssz = read( fd, dst+dst_sz, fd_ulong_min( dst_max-dst_sz, (ulong)LONG_MAX ) );
      44      544773 :     ulong rsz = (ulong)ssz;
      45             : 
      46      544773 :     if( FD_UNLIKELY( !((0L<ssz) & (rsz<=(dst_max-dst_sz))) ) ) {
      47             : 
      48             :       /* At this point, ssz is not in [1,dst_max-dst_sz] */
      49             : 
      50          30 :       if( FD_LIKELY( !ssz ) ) { /* hit EOF */
      51          24 :         *_dst_sz = dst_sz;
      52          24 :         return -1;
      53          24 :       }
      54             : 
      55             :       /* At this point, ssz is not in [0,dst_max-dst_sz].  Thus, ssz
      56             :          should be -1 and errno should be set.  If errno is set to
      57             :          EAGAIN, it appears that fd is configured as non-blocking and,
      58             :          because we have not yet read dst_min, we try again.  Because of
      59             :          glitches in the POSIX spec, read is also allowed to use
      60             :          EWOULDBLOCK for this and EWOULDBLOCK is not required to have
      61             :          the same value as EAGAIN (if EAGAIN==EWOULDBLOCK on the target,
      62             :          the compiler will almost certainly optimize out the unnecessary
      63             :          cmov). */
      64             : 
      65           6 :       int err = errno;
      66           6 :       if( err==EWOULDBLOCK ) err = EAGAIN; /* cmov / no-op */
      67           6 :       if( FD_UNLIKELY( (dst_sz<dst_min) & (err==EAGAIN) ) ) continue;
      68             : 
      69             :       /* At this point, ssz is not in [0,dst_max-dst_sz].  ssz should be
      70             :          -1 and errno set (and not EWOULDBLOCK).  If not, read does not
      71             :          seem to be following POSIX and we flag such as EPROTO (which
      72             :          should not be used as an errno case for read above and is at
      73             :          least suggestive of the issue) to make a strong guarantee to
      74             :          the caller. */
      75             : 
      76           6 :       if( !err ) err = EPROTO; /* cmov */
      77           6 :       *_dst_sz = 0UL;
      78           6 :       return err;
      79           6 :     }
      80             : 
      81             :     /* At this point, rsz is in [1,dst_max-dst_sz] */
      82             : 
      83      544743 :     dst_sz += rsz;
      84      544743 :   } while( dst_sz<dst_min );
      85             : 
      86      544743 :   *_dst_sz = dst_sz;
      87      544743 :   return 0;
      88      544773 : }
      89             : 
      90             : int
      91             : fd_io_write( int          fd,
      92             :              void const * _src,
      93             :              ulong        src_min,
      94             :              ulong        src_max,
      95      862609 :              ulong *      _src_sz ) {
      96             : 
      97      862609 :   if( FD_UNLIKELY( src_max==0UL ) ) {
      98          12 :     *_src_sz = 0UL;
      99          12 :     return 0;
     100          12 :   }
     101             : 
     102             :   /* Note: this is virtually identical to read.  See read for more
     103             :      details. */
     104             : 
     105      862597 :   uchar const * src = (uchar const *)_src;
     106             : 
     107      862597 :   ulong src_sz = 0UL;
     108      862597 :   do {
     109             : 
     110      862597 :     long  ssz = write( fd, src+src_sz, fd_ulong_min( src_max-src_sz, (ulong)LONG_MAX ) );
     111      862597 :     ulong wsz = (ulong)ssz;
     112             : 
     113      862597 :     if( FD_UNLIKELY( !((0L<ssz) & (wsz<=(src_max-src_sz))) ) ) {
     114             : 
     115             :       /* Note: The POSIX spec explicitly indicates _reads_ use ssz
     116             :          -1 and errno EAGAIN/EWOULDBLOCK for no-data-available-now and
     117             :          ssz 0 for end-of-file to explicitly disambiguate these cases.
     118             :          It also specifically indicates that _writes_ follow the same
     119             :          convention to keep read and write call return handling similar
     120             :          even though there is no POSIX concept of an end-of-file for
     121             :          writes.  At the same time, there seem to be no cases in the
     122             :          standard where a write with a positive size (as it is above)
     123             :          should return 0.  So, we skip "EOF" handling here.  If a zero
     124             :          ssz unexpectedly occurs, it will be likely be treated as an
     125             :          EPROTO below. */
     126             : 
     127             : #     if 0
     128             :       if( FD_UNLIKELY( !ssz ) ) {
     129             :         *_src_sz = src_sz;
     130             :         return -1;
     131             :       }
     132             : #     endif
     133             : 
     134           6 :       int err = errno;
     135           6 :       if( err==EWOULDBLOCK ) err = EAGAIN;
     136           6 :       if( FD_UNLIKELY( (src_sz<src_min) & (err==EAGAIN) ) ) continue;
     137             : 
     138           6 :       if( !err ) err = EPROTO;
     139           6 :       *_src_sz = 0UL;
     140           6 :       return err;
     141           6 :     }
     142             : 
     143      862591 :     src_sz += wsz;
     144      862591 :   } while( src_sz<src_min );
     145             : 
     146      862591 :   *_src_sz = src_sz;
     147      862591 :   return 0;
     148      862597 : }
     149             : 
     150             : int
     151             : fd_io_sz( int     fd,
     152     3051951 :           ulong * _sz ) {
     153     3051951 :   struct stat stat[1];
     154             : 
     155     3051951 :   int   ret = fstat( fd, stat );
     156     3051951 :   off_t sz  = stat->st_size;
     157     3051951 :   if( FD_UNLIKELY( !((ret==0) & (((off_t)0)<=sz) & (((ulong)sz)<=((ulong)LONG_MAX))) ) ) {
     158           9 :     int err = errno;
     159           9 :     if( !err ) err = EPROTO;
     160           9 :     *_sz = 0UL;
     161           9 :     return err;
     162           9 :   }
     163             : 
     164     3051942 :   *_sz = (ulong)sz;
     165     3051942 :   return 0;
     166     3051951 : }
     167             : 
     168             : int
     169             : fd_io_truncate( int   fd,
     170       12294 :                 ulong sz ) {
     171       12294 :   if( FD_UNLIKELY( (sz>(ulong)LONG_MAX) | (sz!=(ulong)(off_t)sz) ) ) return EINVAL;
     172       12291 :   if( FD_UNLIKELY( ftruncate( fd, (off_t)sz ) ) ) {
     173           3 :     int err = errno;
     174           3 :     if( !err ) err = EPROTO;
     175           3 :     return err;
     176           3 :   }
     177       12288 :   return 0;
     178       12291 : }
     179             : 
     180             : int
     181             : fd_io_seek( int     fd,
     182             :             long    rel_off,
     183             :             int     type,
     184     3099774 :             ulong * _idx ) {
     185     3099774 :   static int const whence[3] = { SEEK_SET, SEEK_CUR, SEEK_END };
     186             : 
     187     3099774 :   if( FD_UNLIKELY( !((0<=type) & (type<=3) & (rel_off==(long)(off_t)rel_off)) ) ) {
     188           3 :     *_idx = 0UL;
     189           3 :     return EINVAL;
     190           3 :   }
     191             : 
     192     3099771 :   off_t idx = lseek( fd, (off_t)rel_off, whence[ type ] );
     193             : 
     194     3099771 :   if( FD_UNLIKELY( !((((off_t)0)<=idx) & (((ulong)idx)<=((ulong)LONG_MAX))) ) ) {
     195           6 :     int err = errno;
     196           6 :     if( !err ) err = EPROTO;
     197           6 :     *_idx = 0UL;
     198           6 :     return err;
     199           6 :   }
     200             : 
     201     3099765 :   *_idx = (ulong)idx;
     202     3099765 :   return 0;
     203     3099771 : }
     204             : 
     205             : int
     206             : fd_io_buffered_read( int     fd,
     207             :                      void *  _dst,
     208             :                      ulong   dst_sz,
     209             :                      void *  _rbuf,
     210             :                      ulong   rbuf_sz,
     211             :                      ulong * _rbuf_lo,
     212      802908 :                      ulong * _rbuf_ready ) {
     213             : 
     214      802908 :   if( FD_UNLIKELY( !dst_sz ) ) return 0; /* Nothing to do ... optimize for non-trivial read */
     215             : 
     216      802812 :   uchar * dst        = (uchar *)_dst;
     217      802812 :   uchar * rbuf       = (uchar *)_rbuf;
     218      802812 :   ulong   rbuf_lo    = *_rbuf_lo;
     219      802812 :   ulong   rbuf_ready = *_rbuf_ready;
     220             : 
     221      802812 :   ulong rsz;
     222             : 
     223      802812 :   if( FD_LIKELY( rbuf_ready ) ) { /* Optimize for lots of tiny reads */
     224             : 
     225             :     /* At this point we have at least one byte already buffered and one
     226             :        byte to write.  Copy as many bytes as possible from rbuf into
     227             :        dst. */
     228             : 
     229      791880 :     ulong cpy_sz = fd_ulong_min( dst_sz, rbuf_ready ); /* At least 1 and either dst_sz or rbuf_ready */
     230      791880 :     fd_memcpy( dst, rbuf + rbuf_lo, cpy_sz );
     231      791880 :     dst    += cpy_sz;
     232      791880 :     dst_sz -= cpy_sz;
     233             : 
     234             :     /* If this completed the read, we are done. */
     235             : 
     236      791880 :     if( FD_LIKELY( !dst_sz ) ) { /* Optimize for lots of tiny reads */
     237      710607 :       *_rbuf_lo    = rbuf_lo    + cpy_sz;
     238      710607 :       *_rbuf_ready = rbuf_ready - cpy_sz;
     239      710607 :       return 0;
     240      710607 :     }
     241             : 
     242             :     /* At this point we have more bytes to read, which implies cpy_sz
     243             :        was less than dst_sz, which implies cpy_sz was rbuf_ready, which
     244             :        implies rbuf is empty because it was drained above. */
     245      791880 :   }
     246             : 
     247             :   /* At this point, rbuf is empty and we have at least one byte to read. */
     248             : 
     249       92205 :   if( FD_UNLIKELY( dst_sz>=rbuf_sz ) ) { /* If we have a large amount of data to read ... (optimize for tiny reads) */
     250             : 
     251             : #   if 0 /* This implementation guarantees at most one fd read per call but will not block fd reads */
     252             : 
     253             :     /* Read it directly into dst */
     254             : 
     255             :     *_rbuf_lo    = 0UL;
     256             :     *_rbuf_ready = 0UL;
     257             :     return fd_io_read( fd, dst, dst_sz, dst_sz, &rsz );
     258             : 
     259             : #   else /* This implementation will block fd reads into multiples of rbuf_sz */
     260             : 
     261             :     /* Read the largest rbuf_sz multiple directly into dst. */
     262             : 
     263       15561 :     ulong bulk_sz = rbuf_sz*(dst_sz/rbuf_sz); /* TODO: require rbuf_sz to be a power of 2 for faster performance here? */
     264             : 
     265       15561 :     int err = fd_io_read( fd, dst, bulk_sz, bulk_sz, &rsz );
     266       15561 :     if( FD_UNLIKELY( err ) ) {
     267           9 :       *_rbuf_lo    = 0UL;
     268           9 :       *_rbuf_ready = 0UL;
     269           9 :       return err;
     270           9 :     }
     271             : 
     272       15552 :     dst    += bulk_sz;
     273       15552 :     dst_sz -= bulk_sz;
     274             : 
     275             :     /* If this completed the read, we are done. */
     276             : 
     277       15552 :     if( FD_LIKELY( !dst_sz ) ) { /* Optimize for tiny reads */
     278         225 :       *_rbuf_lo    = 0UL;
     279         225 :       *_rbuf_ready = 0UL;
     280         225 :       return 0;
     281         225 :     }
     282             : 
     283             :     /* At this point, we have dst_sz in [1,rbuf_sz) bytes to read */
     284             : 
     285       15552 : #   endif
     286             : 
     287       15552 :   }
     288             : 
     289             :   /* At this point, we have dst_sz in [1,rbuf_sz).  Fill up rbuf
     290             :      as much as we can and return the results from there. */
     291             : 
     292       91971 :   int err = fd_io_read( fd, rbuf, dst_sz, rbuf_sz, &rsz );
     293       91971 :   if( FD_UNLIKELY( err ) ) { /* failed (err>0,rsz==0) or EOF (err<0,rsz<dst_sz), either way, we can't handle the request */
     294           0 :     *_rbuf_lo    = 0UL;
     295           0 :     *_rbuf_ready = 0UL;
     296           0 :     return err;
     297           0 :   }
     298             : 
     299       91971 :   fd_memcpy( dst, rbuf, dst_sz );
     300       91971 :   *_rbuf_lo    = dst_sz;
     301       91971 :   *_rbuf_ready = rsz - dst_sz;
     302       91971 :   return 0;
     303       91971 : }
     304             : 
     305             : int
     306             : fd_io_buffered_skip( int     fd,
     307             :                      ulong   skip_sz,
     308             :                      void *  rbuf,
     309             :                      ulong   rbuf_sz,
     310             :                      ulong * _rbuf_lo,
     311        1242 :                      ulong * _rbuf_ready ) {
     312             : 
     313             :   /* For large skips, flush rbuf and lseek the fd for the remainder.
     314             :      TODO: Consider a variant where fd lseek is aligned to a rbuf_sz
     315             :      like the above (such might require this function to do some
     316             :      buffering of data if the skip_sz isn't an rbuf_sz multiple). */
     317             : 
     318        1242 :   ulong rbuf_ready = *_rbuf_ready;
     319             : 
     320        1242 :   if( FD_UNLIKELY( skip_sz>rbuf_ready ) ) { /* Optimize for tiny skips */
     321             : 
     322        1152 :     skip_sz -= rbuf_ready; /* At least 1 */
     323        1152 :     do {
     324             : 
     325             :       /* Note: lseek allows seeking past EOF (even on a RDONLY fd). */
     326             : 
     327        1152 :       ulong lseek_sz = fd_ulong_min( skip_sz, (ulong)LONG_MAX ); /* Workaround POSIX sign / unsigned glitches */
     328        1152 :       if( FD_UNLIKELY( lseek( fd, (long)lseek_sz, SEEK_CUR )==-1L ) ) {
     329             : 
     330           0 :         int err = errno;
     331             : 
     332           0 :         if( FD_UNLIKELY( err==ESPIPE ) ) {
     333             : 
     334             :           /* It appears the stream isn't seekable ... skip over via
     335             :              actual reads.  It is kinda gross that we do this every time
     336             :              we have to skip on an unseekable stream.  At the same time,
     337             :              such usages are likely so low bandwidth and so rare that
     338             :              the perf hit from just doing the spurious lseeks is
     339             :              probably in the noise and not worth the extra overhead /
     340             :              complexity to do more elaborate handling. */
     341             : 
     342           0 :           do {
     343           0 :             ulong read_sz = fd_ulong_min( rbuf_sz, skip_sz );
     344           0 :             ulong rsz;
     345           0 :             err = fd_io_read( fd, rbuf, read_sz, read_sz, &rsz );
     346           0 :             if( FD_UNLIKELY( !((!err) | (err==EAGAIN)) ) ) break;
     347           0 :             skip_sz -= rsz;
     348           0 :           } while( skip_sz );
     349             : 
     350           0 :           *_rbuf_lo    = 0UL;
     351           0 :           *_rbuf_ready = 0UL;
     352           0 :           return err;
     353             : 
     354           0 :         }
     355             : 
     356           0 :         if( !err ) err = EPROTO; /* cmov, paranoia for non-conform to provide strong guarantees to caller */
     357             : 
     358           0 :         *_rbuf_lo    = 0UL;
     359           0 :         *_rbuf_ready = 0UL;
     360           0 :         return err;
     361           0 :       }
     362             : 
     363        1152 :       skip_sz -= lseek_sz;
     364        1152 :     } while( FD_UNLIKELY( skip_sz ) );
     365             : 
     366        1152 :     *_rbuf_lo    = 0UL;
     367        1152 :     *_rbuf_ready = 0UL;
     368        1152 :     return 0;
     369        1152 :   }
     370             : 
     371             :   /* Skip is purely over buffered bytes */
     372             : 
     373          90 :   *_rbuf_lo    += skip_sz;
     374          90 :   *_rbuf_ready  = rbuf_ready - skip_sz;
     375          90 :   return 0;
     376        1242 : }
     377             : 
     378             : int
     379             : fd_io_buffered_write( int          fd,
     380             :                       void const * _src,
     381             :                       ulong        src_sz,
     382             :                       void *       _wbuf,
     383             :                       ulong        wbuf_sz,
     384      796953 :                       ulong *      _wbuf_used ) {
     385             : 
     386      796953 :   if( FD_UNLIKELY( !src_sz ) ) return 0; /* Nothing to do ... optimize for non-trivial writes */
     387             : 
     388      796926 :   uchar const * src  = (uchar const *)_src;
     389      796926 :   uchar *       wbuf = (uchar *)      _wbuf;
     390             : 
     391      796926 :   ulong wsz;
     392             : 
     393      796926 :   ulong wbuf_used = *_wbuf_used;
     394             : 
     395      796926 :   if( FD_LIKELY( wbuf_used ) ) { /* Optimize for lots of tiny writes */
     396             : 
     397             :     /* At this point, we have at least one byte already buffered and one
     398             :        byte to write.  Copy as many bytes as possible from src into
     399             :        wbuf. */
     400             : 
     401      705141 :     ulong cpy_sz = fd_ulong_min( wbuf_sz - wbuf_used, src_sz ); /* cpy_sz>=1, cpy_sz is either wbuf_free or src_sz */
     402             : 
     403      705141 :     if( FD_LIKELY( cpy_sz ) ) fd_memcpy( wbuf + wbuf_used, src, cpy_sz );
     404             : 
     405      705141 :     src       += cpy_sz;
     406      705141 :     src_sz    -= cpy_sz;
     407      705141 :     wbuf_used += cpy_sz;
     408             : 
     409             :     /* If this filled up the buffer, flush it */
     410             : 
     411      705141 :     if( FD_UNLIKELY( wbuf_used >= wbuf_sz ) ) { /* Optimize for lots of tiny writes */
     412       57858 :       int err = fd_io_write( fd, wbuf, wbuf_sz, wbuf_sz, &wsz );
     413       57858 :       if( FD_UNLIKELY( err ) ) {
     414           0 :         *_wbuf_used = 0UL;
     415           0 :         return err;
     416           0 :       }
     417       57858 :       wbuf_used = 0UL;
     418       57858 :     }
     419             : 
     420             :     /* If this completed the write, we are done. */
     421             : 
     422      705141 :     if( FD_LIKELY( !src_sz ) ) { /* Optimize for lots of tiny writes */
     423      647313 :       *_wbuf_used = wbuf_used;
     424      647313 :       return 0;
     425      647313 :     }
     426             : 
     427             :     /* At this point, we have more bytes to write, which implies cpy_sz
     428             :        was less than src_sz, which implies cpy_sz was wbuf_free, which
     429             :        implies wbuf is empty because it was flushed above. */
     430             : 
     431      705141 :   }
     432             : 
     433             :   /* At this point, wbuf is empty and we have at least one byte to
     434             :      write. */
     435             : 
     436      149613 :   if( FD_UNLIKELY( src_sz>=wbuf_sz ) ) { /* If we have a large amount of data to write ... (optimize for tiny writes) */
     437             : 
     438             : #   if 0 /* This implementation guarantees at most one fd write per call but will not block fd writes */
     439             : 
     440             :     /* Write it directly from src */
     441             : 
     442             :     *_wbuf_used = 0UL;
     443             :     return fd_io_write( fd, src, src_sz, src_sz, &wsz );
     444             : 
     445             : #   else /* This implementation will block fd writes into multiples of wbuf_sz */
     446             : 
     447             :     /* Write the largest wbuf_sz multiple directly into src. */
     448             : 
     449       14043 :     ulong bulk_sz = wbuf_sz*(src_sz/wbuf_sz); /* TODO: require wbuf_sz to be a power of 2 for faster performance here? */
     450             : 
     451       14043 :     int err = fd_io_write( fd, src, bulk_sz, bulk_sz, &wsz );
     452       14043 :     if( FD_UNLIKELY( err ) ) {
     453           0 :       *_wbuf_used = 0UL;
     454           0 :       return err;
     455           0 :     }
     456             : 
     457       14043 :     src    += bulk_sz;
     458       14043 :     src_sz -= bulk_sz;
     459             : 
     460             :     /* If this completed the write, we are done. */
     461             : 
     462       14043 :     if( FD_LIKELY( !src_sz ) ) { /* Optimize for tiny writes */
     463          93 :       *_wbuf_used = 0UL;
     464          93 :       return 0;
     465          93 :     }
     466             : 
     467             :     /* At this point, we have src_sz in [1,wbuf_sz) bytes to write. */
     468             : 
     469       14043 : #   endif
     470             : 
     471       14043 :   }
     472             : 
     473             :   /* At this point, we have src_sz in [1,wbuf_sz) and an empty
     474             :      buffer.  Buffer these bytes. */
     475             : 
     476      149520 :   fd_memcpy( wbuf, src, src_sz );
     477      149520 :   *_wbuf_used = src_sz;
     478      149520 :   return 0;
     479      149613 : }
     480             : 
     481             : int
     482             : fd_io_mmio_init( int     fd,
     483             :                  int     mode,
     484             :                  void *  _mmio,
     485       12357 :                  ulong * _mmio_sz ) {
     486             : 
     487             :   /* Check input args */
     488             : 
     489       12357 :   if( FD_UNLIKELY( (mode!=FD_IO_MMIO_MODE_READ_ONLY) & (mode!=FD_IO_MMIO_MODE_READ_WRITE) ) ) {
     490           3 :     *(void **)_mmio = NULL;
     491           3 :     *_mmio_sz       = 0UL;
     492           3 :     return EINVAL;
     493           3 :   }
     494             : 
     495       12354 :   int rw = (mode==FD_IO_MMIO_MODE_READ_WRITE);
     496             : 
     497             :   /* Determine the file size.  If this is a zero length file, just give
     498             :      the caller a mapping to nothing. */
     499             : 
     500       12354 :   ulong mmio_sz;
     501       12354 :   int   err = fd_io_sz( fd, &mmio_sz );
     502       12354 :   if( FD_UNLIKELY( (!!err) | (!mmio_sz) ) ) {
     503          15 :     *(void **)_mmio = NULL;
     504          15 :     *_mmio_sz       = 0UL;
     505          15 :     return err;
     506          15 :   }
     507             : 
     508             :   /* mmap the file into the caller's address space.  Note: memory mapped
     509             :      I/O TLB and NUMA optimization options in Linux / POSIX are
     510             :      extremely limited.  (No support or limited support or buggy support
     511             :      or conflicting standards or different handling across different
     512             :      OS/kernels/file systems or ... for requesting huge page backing,
     513             :      gigantic page backing, NUMA affinities, etc.   Page sizes other
     514             :      than normal and/or NUMA multicore systems were just not a thing
     515             :      when VM APIs and implementations ossified.)  We currently don't
     516             :      even try here for portability and simplicity.
     517             : 
     518             :      FIXME: consider widening to expose features like MAP_POPULATE
     519             :      and/or MAP_FIXED.  Also consider widening to allow callers to
     520             :      specify their TLB and NUMA needs in hopes that, in a glorious
     521             :      future, virtual memory APIs and subsystems will come around to the
     522             :      fact that, as virtual memory is really a distributed file system
     523             :      under the hood these days where TLB and NUMA optimizations are
     524             :      critical, TLB and NUMA optimizations are equally critical for file
     525             :      system files mapped into virtual memory ... this would allow
     526             :      callers would get the benefits for free once support is available
     527             :      Probably would need to move these APIs to shmem. */
     528             : 
     529       12339 :   void * mmio = mmap( NULL, mmio_sz, rw ? (PROT_READ|PROT_WRITE) : PROT_READ, MAP_SHARED, fd, (off_t)0 );
     530       12339 :   if( FD_UNLIKELY( (mmio==MAP_FAILED) | (!mmio) ) ) {
     531           0 :     int err = errno;
     532           0 :     if( !err ) err = EPROTO; /* cmov */
     533           0 :     *(void **)_mmio = NULL;
     534           0 :     *_mmio_sz       = 0UL;
     535           0 :     return err;
     536           0 :   }
     537             : 
     538             :   /* At this point, we've mapped the file */
     539             : 
     540       12339 :   *(void **)_mmio = mmio;
     541       12339 :   *_mmio_sz       = mmio_sz;
     542       12339 :   return 0;
     543       12339 : }
     544             : 
     545             : void
     546             : fd_io_mmio_fini( void const * mmio,
     547       12357 :                  ulong        mmio_sz ) {
     548             : 
     549             :   /* Check input args and handle zero length files */
     550             : 
     551       12357 :   if( FD_UNLIKELY( (!mmio) | (!mmio_sz) ) ) return;
     552             : 
     553             :   /* Handle non-zero length files */
     554             : 
     555       12339 :   munmap( (void *)mmio, mmio_sz ); /* note: fd_log not available here so we don't error trap as it makes no difference */
     556       12339 : }
     557             : 
     558             : char const *
     559         213 : fd_io_strerror( int err ) {
     560             : 
     561             :   /* This covers the standard POSIX-2008 errnos.  We handle the POSIX
     562             :      glitches around EWOULDBLOCK / EAGAIN and EOPNOTSUPP / ENOTSUP so
     563             :      this will build fine regardless of whether these map to the same
     564             :      or different error codes (they typically map to the same nowadays).
     565             :      We also throw in negative values for EOF as that is how the above
     566             :      handles such. */
     567             : 
     568         213 :   if( err<0 ) return "end-of-file";
     569             : 
     570         210 :   if( err==EWOULDBLOCK ) err = EAGAIN;  /* cmov / no-op */
     571         210 :   if( err==EOPNOTSUPP  ) err = ENOTSUP; /* cmov / no-op */
     572             : 
     573         210 :   switch( err ) {
     574           3 :   case 0              : return "success";
     575           0 :   case E2BIG          : return "E2BIG-argument list too long";
     576         147 :   case EACCES         : return "EACCES-permission denied";
     577           0 :   case EADDRINUSE     : return "EADDRINUSE-address already in use";
     578           0 :   case EADDRNOTAVAIL  : return "EADDRNOTAVAIL-cannot assign requested address";
     579           0 :   case EAFNOSUPPORT   : return "EAFNOSUPPORT-address family not supported by protocol";
     580           0 :   case EAGAIN         : return "EAGAIN-resource temporarily unavailable";
     581           0 :   case EALREADY       : return "EALREADY-operation already in progress";
     582           3 :   case EBADF          : return "EBADF-bad file descriptor";
     583           0 :   case EBADMSG        : return "EBADMSG-bad message";
     584           0 :   case EBUSY          : return "EBUSY-device or resource busy";
     585           0 :   case ECANCELED      : return "ECANCELED-operation canceled";
     586           0 :   case ECHILD         : return "ECHILD-no child processes";
     587           0 :   case ECONNABORTED   : return "ECONNABORTED-software caused connection abort";
     588           0 :   case ECONNREFUSED   : return "ECONNREFUSED-connection refused";
     589           0 :   case ECONNRESET     : return "ECONNRESET-connection reset by peer";
     590           0 :   case EDEADLK        : return "EDEADLK-resource deadlock avoided";
     591           0 :   case EDESTADDRREQ   : return "EDESTADDRREQ-destination address required";
     592           0 :   case EDOM           : return "EDOM-numerical argument out of domain";
     593          15 :   case EEXIST         : return "EEXIST-file exists";
     594           0 :   case EFAULT         : return "EFAULT-bad address";
     595           0 :   case EFBIG          : return "EFBIG-file too large";
     596           0 :   case EHOSTUNREACH   : return "EHOSTUNREACH-no route to host";
     597           0 :   case EIDRM          : return "EIDRM-identifier removed";
     598           0 :   case EILSEQ         : return "EILSEQ-invalid or incomplete multibyte or wide character";
     599           0 :   case EINPROGRESS    : return "EINPROGRESS-operation now in progress";
     600           0 :   case EINTR          : return "EINTR-interrupted system call";
     601           3 :   case EINVAL         : return "EINVAL-invalid argument";
     602           0 :   case EIO            : return "EIO-input/output error";
     603           0 :   case EISCONN        : return "EISCONN-transport endpoint is already connected";
     604           0 :   case EISDIR         : return "EISDIR-is a directory";
     605           0 :   case ELOOP          : return "ELOOP-too many levels of symbolic links";
     606           0 :   case EMFILE         : return "EMFILE-too many open files";
     607           0 :   case EMLINK         : return "EMLINK-too many links";
     608           0 :   case EMSGSIZE       : return "EMSGSIZE-message too long";
     609           0 :   case ENAMETOOLONG   : return "ENAMETOOLONG-file name too long";
     610           0 :   case ENETDOWN       : return "ENETDOWN-network is down";
     611           0 :   case ENETRESET      : return "ENETRESET-network dropped connection on reset";
     612           0 :   case ENETUNREACH    : return "ENETUNREACH-network is unreachable";
     613           0 :   case ENFILE         : return "ENFILE-too many open files in system";
     614           0 :   case ENOBUFS        : return "ENOBUFS-no buffer space available";
     615           0 :   case ENODEV         : return "ENODEV-no such device";
     616           3 :   case ENOENT         : return "ENOENT-no such file or directory";
     617           0 :   case ENOEXEC        : return "ENOEXEC-exec format error";
     618           0 :   case ENOLCK         : return "ENOLCK-no locks available";
     619          36 :   case ENOMEM         : return "ENOMEM-cannot allocate memory";
     620           0 :   case ENOMSG         : return "ENOMSG-no message of desired type";
     621           0 :   case ENOPROTOOPT    : return "ENOPROTOOPT-protocol not available";
     622           0 :   case ENOSPC         : return "ENOSPC-no space left on device";
     623           0 :   case ENOSYS         : return "ENOSYS-function not implemented";
     624           0 :   case ENOTCONN       : return "ENOTCONN-transport endpoint is not connected";
     625           0 :   case ENOTDIR        : return "ENOTDIR-not a directory";
     626           0 :   case ENOTEMPTY      : return "ENOTEMPTY-directory not empty";
     627           0 :   case ENOTRECOVERABLE: return "ENOTRECOVERABLE-state not recoverable";
     628           0 :   case ENOTSOCK       : return "ENOTSOCK-socket operation on non-socket";
     629           0 :   case ENOTSUP        : return "ENOTSUP-operation not supported";
     630           0 :   case ENOTTY         : return "ENOTTY-inappropriate ioctl for device";
     631           0 :   case ENXIO          : return "ENXIO-no such device or address";
     632           0 :   case EOVERFLOW      : return "EOVERFLOW-value too large for defined data type";
     633           0 :   case EOWNERDEAD     : return "EOWNERDEAD-owner died";
     634           0 :   case EPERM          : return "EPERM-operation not permitted";
     635           0 :   case EPIPE          : return "EPIPE-broken pipe";
     636           0 :   case EPROTONOSUPPORT: return "EPROTONOSUPPORT-protocol not supported";
     637           0 :   case EPROTO         : return "EPROTO-protocol error";
     638           0 :   case EPROTOTYPE     : return "EPROTOTYPE-protocol wrong type for socket";
     639           0 :   case ERANGE         : return "ERANGE-numerical result out of range";
     640           0 :   case EROFS          : return "EROFS-read-only file system";
     641           0 :   case ESPIPE         : return "ESPIPE-illegal seek";
     642           0 :   case ESRCH          : return "ESRCH-no such process";
     643           0 :   case ETIMEDOUT      : return "ETIMEDOUT-connection timed out";
     644           0 :   case ETXTBSY        : return "ETXTBSY-text file busy";
     645           0 :   case EXDEV          : return "EXDEV-invalid cross-device link";
     646           0 :   default: break;
     647         210 :   }
     648             : 
     649           0 :   return "unknown";
     650         210 : }
     651             : 
     652             : char const *
     653           0 : fd_io_strsignal( int sig ) {
     654           0 :   switch( sig ) {
     655           0 :   case 0              : return "success";
     656           0 :   case SIGHUP         : return "SIGHUP-Hangup";
     657           0 :   case SIGINT         : return "SIGINT-Interrupt";
     658           0 :   case SIGQUIT        : return "SIGQUIT-Quit";
     659           0 :   case SIGILL         : return "SIGILL-Illegal instruction";
     660           0 :   case SIGTRAP        : return "SIGTRAP-Trace/breakpoint trap";
     661           0 :   case SIGABRT        : return "SIGABRT-Aborted";
     662           0 :   case SIGBUS         : return "SIGBUS-Bus error";
     663           0 :   case SIGFPE         : return "SIGFPE-Arithmetic exception";
     664           0 :   case SIGKILL        : return "SIGKILL-Killed";
     665           0 :   case SIGUSR1        : return "SIGUSR1-User defined signal 1";
     666           0 :   case SIGSEGV        : return "SIGSEGV-Segmentation fault";
     667           0 :   case SIGUSR2        : return "SIGUSR2-User defined signal 2";
     668           0 :   case SIGPIPE        : return "SIGPIPE-Broken pipe";
     669           0 :   case SIGALRM        : return "SIGALRM-Alarm clock";
     670           0 :   case SIGTERM        : return "SIGTERM-Terminated";
     671           0 : #if defined(SIGSTKFLT)
     672           0 :   case SIGSTKFLT      : return "SIGSTKFLT-Stack fault";
     673             : #elif defined(SIGEMT)
     674             :   case SIGEMT         : return "SIGEMT-Emulator trap";
     675             : #endif
     676           0 :   case SIGCHLD        : return "SIGCHLD-Child process status";
     677           0 :   case SIGCONT        : return "SIGCONT-Continued";
     678           0 :   case SIGSTOP        : return "SIGSTOP-Stopped (signal)";
     679           0 :   case SIGTSTP        : return "SIGTSTP-Stopped";
     680           0 :   case SIGTTIN        : return "SIGTTIN-Stopped (tty input)";
     681           0 :   case SIGTTOU        : return "SIGTTOU-Stopped (tty output)";
     682           0 :   case SIGURG         : return "SIGURG-Urgent I/O condition";
     683           0 :   case SIGXCPU        : return "SIGXCPU-CPU time limit exceeded";
     684           0 :   case SIGXFSZ        : return "SIGXFSZ-File size limit exceeded";
     685           0 :   case SIGVTALRM      : return "SIGVTALRM-Virtual timer expired";
     686           0 :   case SIGPROF        : return "SIGPROF-Profiling timer expired";
     687           0 : #if defined(SIGWINCH)
     688           0 :   case SIGWINCH       : return "SIGWINCH-Window changed";
     689           0 : #endif
     690           0 : #if defined(SIGPOLL)
     691           0 :   case SIGPOLL        : return "SIGPOLL-I/O possible";
     692           0 : #endif
     693           0 : #if defined(SIGPWR)
     694           0 :   case SIGPWR         : return "SIGPWR-Power failure";
     695           0 : #endif
     696           0 :   case SIGSYS         : return "SIGSYS-Bad system call";
     697           0 :   default: break;
     698           0 :   }
     699             : 
     700           0 :   return "unknown";
     701           0 : }
     702             : 
     703             : #else
     704             : #error "Unknown FD_IO_STYLE"
     705             : #endif

Generated by: LCOV version 1.14