LCOV - code coverage report
Current view: top level - util/cstr - fd_cstr.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 107 144 74.3 %
Date: 2025-11-21 04:43:39 Functions: 61 16614 0.4 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_util_cstr_fd_cstr_h
       2             : #define HEADER_fd_src_util_cstr_fd_cstr_h
       3             : 
       4             : /* APIs for manipulating '\0'-terminated character strings ("cstr") */
       5             : 
       6             : #include "../bits/fd_bits.h"
       7             : 
       8             : FD_PROTOTYPES_BEGIN
       9             : 
      10             : /* cstr input *********************************************************/
      11             : 
      12             : /* fd_cstr_to_T converts the cstr pointed at by s into a T and returns
      13             :    its value.  Caller promises s is non-NULL and points at a cstr.
      14             : 
      15             :    Note fd_cstr_to_cstr just returns s.  As such the lifetime of the
      16             :    returned pointer is the lifetime s and ownership model of the
      17             :    underlying s is defined by the application.
      18             : 
      19             :    fd_cstr_to_char just returns the first character of the cstr (if cstr
      20             :    is the empty string, this will be the '\0' character ... otherwise,
      21             :    it will be a normal string character).  As char do not have a
      22             :    consistent interpretation between platforms due to issues with the
      23             :    language standard itself, the value here should just be treated as a
      24             :    character and not an integer.  Use fd_cstr_schar/fd_cstr_uchar if you
      25             :    need to treat a char as an integer.
      26             : 
      27             :    fd_cstr_to_cstr and fd_cstr_to_char exist primarily for type system
      28             :    completeness / facilitate various generic programming practices.
      29             : 
      30             :    The integer converters work in the strtol sense with base 0 (and thus
      31             :    ignore leading whitespace, handle leading signs and assume octal if
      32             :    the body is prefixed with 0, hexadecimal if prefixed with 0x and
      33             :    decimal otherwise). */
      34             : 
      35             : FD_FN_CONST char const * fd_cstr_to_cstr  ( char const * s );
      36             : FD_FN_PURE  char         fd_cstr_to_char  ( char const * s );
      37             : FD_FN_PURE  schar        fd_cstr_to_schar ( char const * s );
      38             : FD_FN_PURE  short        fd_cstr_to_short ( char const * s );
      39             : FD_FN_PURE  int          fd_cstr_to_int   ( char const * s );
      40             : FD_FN_PURE  long         fd_cstr_to_long  ( char const * s );
      41             : FD_FN_PURE  uchar        fd_cstr_to_uchar ( char const * s );
      42             : FD_FN_PURE  ushort       fd_cstr_to_ushort( char const * s );
      43             : FD_FN_PURE  uint         fd_cstr_to_uint  ( char const * s );
      44             : FD_FN_PURE  ulong        fd_cstr_to_ulong ( char const * s );
      45             : FD_FN_PURE  float        fd_cstr_to_float ( char const * s );
      46             : #if FD_HAS_DOUBLE
      47             : FD_FN_PURE  double       fd_cstr_to_double( char const * s );
      48             : #endif
      49             : 
      50             : /* fd_cstr_to_ulong_octal is the same as fd_cstr_to_ulong but assumes s
      51             :    points is octal.  This is mostly used when dealing parsing UNIX style
      52             :    file permissions. */
      53             : 
      54             : FD_FN_PURE ulong fd_cstr_to_ulong_octal( char const * s );
      55             : 
      56             : /* fd_cstr_to_ulong_seq populates seq (which has room for seq max items)
      57             :    with the sequenced specified by the given cstr.  Sequences are a
      58             :    comma separated list of ranges (e.g. "R0,R1,R2").  The ranges
      59             :    themselves can be themselves be individual integers (e.g. "5") or a
      60             :    simple range (e.g. "4-8", includes both endpoints, stop should be at
      61             :    least start), a range with a skip (e.g. "1-10/3" or "1-10:3", stop
      62             :    should be at least start and stride should be positive).  Ignores
      63             :    internal whitespace.  Robust against overflow / wrapping of ranges
      64             :    against ULONG_MAX.  Items may appear in multiple times and sequences
      65             :    can have an arbitrary order.  Caller promises seq is non-NULL if max
      66             :    is non-zero.  Returns 0 on NULL or malformed cstr or empty sequence
      67             :    (seq contents might have been arbitrarily clobbered on a malformed
      68             :    cstr). */
      69             : 
      70             : ulong                                         /* Actual sequence length, if greater than seq_max returned sequence truncated. */
      71             : fd_cstr_to_ulong_seq( char const * cstr,      /* String to parse, NULL returns 0 */
      72             :                       ulong *      seq,       /* Indexed [0,max), elements [0,min(actual sequence length,seq_max)) populated with
      73             :                                                  the leading portion of the seq.  Any remaining elements of seq are untouched. */
      74             :                       ulong        seq_max ); /* Maximum sequence length */
      75             : 
      76             : /* fd_cstr_hash hashes the cstr pointed to by key to a ulong.
      77             :    fd_cstr_hash_append updates the hash value (it will be as though the
      78             :    fd_cstr_hash was called on the string concatenation of the all the
      79             :    keys provided to hash / hash append in order).  Treats key==NULL the
      80             :    same as the empty string "".  Yields identical cross platform results
      81             :    regardless of how the platform treats the sign of char.  Based on one
      82             :    of the djb2 hash variants (public domain).
      83             : 
      84             :    FIXME: This is simple and fast and pretty good practically for string
      85             :    hashing but more robust and faster algos are probably out there. */
      86             : 
      87             : FD_FN_PURE static inline ulong
      88             : fd_cstr_hash_append( ulong        hash,
      89    10463785 :                      char const * key ) {
      90    10463788 :   if( FD_LIKELY( key ) ) {
      91    10463787 :     uchar const * p = (uchar const *)key;
      92   404832935 :     for(;;) {
      93   404832935 :       ulong c = p[0];
      94   404832935 :       if( FD_UNLIKELY( !c ) ) break;
      95   394369144 :       hash = (hash*33UL) ^ c;
      96   394369144 :       p++;
      97   394369144 :     }
      98    10463787 :   }
      99    10463785 :   return hash;
     100    10463785 : }
     101             : 
     102         291 : FD_FN_PURE static inline ulong fd_cstr_hash( char const * key ) { return fd_cstr_hash_append( 5381UL, key ); }
     103             : 
     104             : /* fd_cstr_casecmp is equivalent to strcasecmp but doesn't require
     105             :    FD_HAS_HOSTED (POSIX) support. */
     106             : 
     107             : FD_FN_PURE int
     108             : fd_cstr_casecmp( char const * a,
     109             :                  char const * b );
     110             : 
     111             : /* fd_cstr_nlen is equivalent to strnlen but doesn't require
     112             :    FD_HAS_HOSTED (POSIX) support. */
     113             : 
     114             : FD_FN_PURE ulong
     115             : fd_cstr_nlen( char const * s,
     116             :               ulong        m );
     117             : 
     118             : /* fd_cstr_ncpy is a safe version of strncpy.  d is the destination cstr
     119             :    and s is the source cstr.  d and s should not overlap.  Assumes d has
     120             :    space for up to m bytes.  Always returns d.  All bytes of d will be
     121             :    initialized.  Further, if m is not zero, d will _always_ be properly
     122             :    '\0' terminated.
     123             : 
     124             :    Specifically, if m is 0 (i.e. d has zero bytes of storage), this
     125             :    returns d.  Otherwise, if s is NULL, this will zero out all m bytes
     126             :    of d and return d.  Otherwise, this will copy up to m-1 of the
     127             :    leading non-zero bytes in s into d.  All remaining bytes of d (there
     128             :    will be at least 1) will be initialized to zero. */
     129             : 
     130             : char *
     131             : fd_cstr_ncpy( char *       d,
     132             :               char const * s,
     133             :               ulong        m );
     134             : 
     135             : /* cstr output ********************************************************/
     136             : 
     137             : /* fd_cstr_printf printf a cstr into the sz byte memory region pointed
     138             :    to by buf.  Always returns buf.
     139             : 
     140             :    If buf is non-NULL and sz is non-zero, on return, buf will point to a
     141             :    cstr such that strlen(buf)<sz.  That is, bytes [0,strlen(buf)] will
     142             :    be non-'\0', byte strlen(buf) will be '\0' and bytes (len,sz) will be
     143             :    unchanged.  If more than sz bytes are needed to hold the requested
     144             :    cstr, the cstr will be truncated to its leading bytes such that
     145             :    strlen(buf)==sz-1.  If opt_len is non-NULL, *opt_len will be set to
     146             :    the strlen(buf) on return.
     147             : 
     148             :    buf==NULL and/or sz==0UL are treated as a no-op.  (If opt_len is
     149             :    non-NULL *opt_len wll be 0UL on return ... this is debatable though
     150             :    given the strlen(buf) property above.  Might be better to this case
     151             :    as U.B., or abort if opt_len is requested when buf==NULL and sz==NULL
     152             :    or return ULONG_MAX in opt_len (-1) to indicate ill defined usage or
     153             :    ...) */
     154             : 
     155             : char *
     156             : fd_cstr_printf( char *       buf,
     157             :                 ulong        sz,
     158             :                 ulong *      opt_len,
     159             :                 char const * fmt, ... ) __attribute__((format(printf,4,5)));
     160             : 
     161             : /* fd_cstr_printf_check is the same as fd_cstr_printf except that it
     162             :    returns 1 if the entire cstr, including the NUL terminating
     163             :    character was written to buf and 0 otherwise.
     164             : 
     165             :    If the cstr was truncated, or there was an error in the printf
     166             :    formatting process, 0 will be returned.  Otherwise, on success, 1
     167             :    will be returned.  If zero bytes are written to buf because the
     168             :    format string is empty, the return value will be 1. */
     169             : 
     170             : int
     171             : fd_cstr_printf_check( char *       buf,
     172             :                       ulong        sz,
     173             :                       ulong *      opt_len,
     174             :                       char const * fmt, ... ) __attribute__((format(printf,4,5)));
     175             : 
     176             : /* fd_cstr_init start writing a cstr into buf.  Returns where the first
     177             :    character of the cstr should be written (==buf). */
     178             : 
     179     2748662 : static inline char * fd_cstr_init( char * buf ) { return buf; }
     180             : 
     181             : /* fd_cstr_fini finished writing a cstr to buf.  Assumes p is valid
     182             :    (non-NULL and room for the terminating '\0').  At this point, the buf
     183             :    passed to fd_cstr_init will be properly '\0' terminated. */
     184             : 
     185     2748764 : static inline void fd_cstr_fini( char * p ) { *p = '\0'; }
     186             : 
     187             : /* fd_cstr_append_char append character c to cstr.  Assumes p is valid
     188             :    (non-NULL and room for at least this char and a final terminating
     189             :    '\0') and c is not '\0' */
     190             : 
     191    16846412 : static inline char * fd_cstr_append_char( char * p, char c ) { *(p++) = c; return p; }
     192             : 
     193             : /* fd_cstr_append_text appends n characters of text pointed to by t to
     194             :    p.  Assumes p is valid (non-NULL and room for at least n characters
     195             :    and a final terminating '\0') and t is valid (points to n consecutive
     196             :    non-'\0' characters).  n is zero is fine. */
     197             : 
     198             : static inline char *
     199             : fd_cstr_append_text( char *       p,
     200             :                      char const * t,
     201     2400467 :                      ulong        n ) {
     202     2400467 :   fd_memcpy( p, t, n );
     203     2400467 :   return p + n;
     204     2400467 : }
     205             : 
     206             : /* fd_cstr_append_cstr appends the cstr pointed to by s to p.  Assumes p
     207             :    is valid (non-NULL and room for at least strlen( s ) characters and a
     208             :    final terminating '\0').  s==NULL is treated as a no-op. */
     209             : 
     210             : static inline char *
     211             : fd_cstr_append_cstr( char *       p,
     212       49215 :                      char const * s ) {
     213       49215 :   if( FD_UNLIKELY( !s ) ) return p;
     214       49215 :   ulong n = strlen( s );
     215       49215 :   fd_memcpy( p, s, n );
     216       49215 :   return p + n;
     217       49215 : }
     218             : 
     219             : /* fd_cstr_append_cstr_safe appends up to n chars of the cstr pointed
     220             :    to by to p.  Assumes p is valid (non-NULL and room for at least n
     221             :    characters and a final terminating '\0').  s==NULL is treated as a
     222             :    no-op. */
     223             : 
     224             : static inline char *
     225             : fd_cstr_append_cstr_safe( char *       p,
     226             :                           char const * s,
     227       10031 :                           ulong        n ) {
     228       10031 :   if( FD_UNLIKELY( !s ) ) return p;
     229       10031 :   ulong l = fd_ulong_min( strlen( s ), n );
     230       10031 :   fd_memcpy( p, s, l );
     231       10031 :   return p + l;
     232       10031 : }
     233             : 
     234             : /* fd_cstr_append_printf appends the printf of the fmt string into p.
     235             :    Assumes p is valid (non-NULL and room for printf characters and a
     236             :    final terminating '\0'). */
     237             : 
     238             : char *
     239             : fd_cstr_append_printf( char *       p,
     240             :                        char const * fmt, ... ) __attribute__((format(printf,2,3)));
     241             : 
     242             : /* fd_cstr_append_ulong_as_text pretty prints the ulong into p (and
     243             :    similarly for the other unsigned integer types).  Assumes p is valid
     244             :    (non-NULL and room for at least n characters and a final terminating
     245             :    '\0'), x is small enough to pretty print to n chars (which implies
     246             :    that n is at least 1).  ws is the character to left pad the converted
     247             :    value with.  pm is prefix character to use (e.g. '+', '-'), '\0'
     248             :    indicates no prefix.  If a prefix is requested, it will be
     249             :    immediately before the most significant converted character.
     250             : 
     251             :    fd_cstr_append_ulong_as_hex is the base-16 equivalent of the above. */
     252             : 
     253             : static inline char *
     254             : fd_cstr_append_uint_as_text( char * p,
     255             :                              char   ws,
     256             :                              char   pm,
     257             :                              uint   x,
     258    14394941 :                              ulong  n ) {
     259    14394941 :   char * p0 = p;
     260    14394941 :   p += n;
     261    14394941 :   char * q = p;
     262    28790974 :   do { uint d = x % 10U; x /= 10U; *(--q) = (char)( d + (uint)'0' ); } while( x );
     263    14394941 :   if( pm ) *(--q) = pm;
     264    19193338 :   while( p0<q ) *(p0++) = ws;
     265    14394941 :   return p;
     266    14394941 : }
     267             : 
     268             : static inline char *
     269             : fd_cstr_append_uint_as_hex( char * p,
     270             :                             char   ws,
     271             :                             uint   x,
     272           0 :                             ulong  n ) {
     273           0 :   static char const bin2hex[ 16 ] = {
     274           0 :     '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
     275           0 :   };
     276           0 :   char * p0 = p;
     277           0 :   p += n;
     278           0 :   char * q = p;
     279           0 :   do { uint d = x & 0xFU; x >>= 4; *(--q) = bin2hex[ d ]; } while( x );
     280           0 :   while( p0<q ) *(p0++) = ws;
     281           0 :   return p;
     282           0 : }
     283             : 
     284             : static inline char *
     285             : fd_cstr_append_ulong_as_text( char * p,
     286             :                               char   ws,
     287             :                               char   pm,
     288             :                               ulong  x,
     289         813 :                               ulong  n ) {
     290         813 :   char * p0 = p;
     291         813 :   p += n;
     292         813 :   char * q = p;
     293        7143 :   do { ulong d = x % 10UL; x /= 10UL; *(--q) = (char)( d + (ulong)'0' ); } while( x );
     294         813 :   if( pm ) *(--q) = pm;
     295        1065 :   while( p0<q ) *(p0++) = ws;
     296         813 :   return p;
     297         813 : }
     298             : 
     299             : static inline char *
     300             : fd_cstr_append_ulong_as_hex( char * p,
     301             :                              char   ws,
     302             :                              ulong  x,
     303           0 :                              ulong  n ) {
     304           0 :   static char const bin2hex[ 16 ] = {
     305           0 :     '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
     306           0 :   };
     307           0 :   char * p0 = p;
     308           0 :   p += n;
     309           0 :   char * q = p;
     310           0 :   do { ulong d = x & 0xFUL; x >>= 4; *(--q) = bin2hex[ d ]; } while( x );
     311           0 :   while( p0<q ) *(p0++) = ws;
     312           0 :   return p;
     313           0 : }
     314             : 
     315             : #if FD_HAS_INT128
     316             : 
     317             : static inline char *
     318             : fd_cstr_append_uint128_as_text( char *  p,
     319             :                                 char    ws,
     320             :                                 char    pm,
     321             :                                 uint128 x,
     322         960 :                                 ulong   n ) {
     323         960 :   char * p0 = p;
     324         960 :   p += n;
     325         960 :   char * q = p;
     326       19200 :   do { uint128 d = x % (uint128)10UL; x /= (uint128)10UL; *(--q) = (char)( d + (uint128)'0' ); } while( x );
     327         960 :   if( pm ) *(--q) = pm;
     328        1440 :   while( p0<q ) *(p0++) = ws;
     329         960 :   return p;
     330         960 : }
     331             : 
     332             : static inline char *
     333             : fd_cstr_append_uint128_as_hex( char * p,
     334             :                                char   ws,
     335             :                                ulong  x,
     336           0 :                                ulong  n ) {
     337           0 :   static char const bin2hex[ 16 ] = {
     338           0 :     '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
     339           0 :   };
     340           0 :   char * p0 = p;
     341           0 :   p += n;
     342           0 :   char * q = p;
     343           0 :   do { uint d = (uint)x & 0xFU; x >>= 4; *(--q) = bin2hex[ d ]; } while( x );
     344           0 :   while( p0<q ) *(p0++) = ws;
     345           0 :   return p;
     346           0 : }
     347             : 
     348             : #endif
     349             : 
     350             : static inline char *
     351             : fd_cstr_append_uchar_as_text( char * p,
     352             :                               char   ws,
     353             :                               char   pm,
     354             :                               uchar  x,
     355          96 :                               ulong  n ) {
     356          96 :   return fd_cstr_append_uint_as_text( p, ws, pm, (uint)x, n );
     357          96 : }
     358             : 
     359             : static inline char *
     360             : fd_cstr_append_ushort_as_text( char * p,
     361             :                                char   ws,
     362             :                                char   pm,
     363             :                                ushort x,
     364         150 :                                ulong  n ) {
     365         150 :   return fd_cstr_append_uint_as_text( p, ws, pm, (uint)x, n );
     366         150 : }
     367             : 
     368             : /* fd_cstr_append_fxp10_as_text same as the above but for the decimal
     369             :    fixed point value:
     370             :      x / 10^f
     371             :    Assumes p is valid (non-NULL and room for at least n characters and a
     372             :    final terminating '\0'), x / 10^f is not too large to fit within n
     373             :    characters (which implies that n is at least f+2).  ws is the
     374             :    character to left pad the converted value with.  pm is prefix
     375             :    character to use (e.g. '+', '-'), '\0' indicates no prefix.  If a
     376             :    prefix is requested, it will be immediately before the most
     377             :    significant converted character. */
     378             : 
     379             : FD_FN_UNUSED static char * /* Work around -Winline */
     380             : fd_cstr_append_fxp10_as_text( char * p,
     381             :                               char   ws,
     382             :                               char   pm,
     383             :                               ulong  f,
     384             :                               ulong  x,
     385     2401089 :                               ulong  n ) {
     386     2401089 :   char * p0 = p;
     387     2401089 :   p += n;
     388     2401089 :   char * q = p;
     389    24001818 :   while( f ) { ulong d = x % 10UL; x /= 10UL; *(--q) = (char)( d + (ulong)'0' ); f--; }
     390     2401089 :   *(--q) = '.';
     391     4738643 :   do { ulong d = x % 10UL; x /= 10UL; *(--q) = (char)( d + (ulong)'0' ); } while( x );
     392     2401089 :   if( pm ) *(--q) = pm;
     393     2475424 :   while( p0<q ) *(p0++) = ws;
     394     2401089 :   return p;
     395     2401089 : }
     396             : 
     397             : /* fd_cstr_tokenize tokenizes the cstr of the form whose first
     398             :    byte is pointed to by cstr:
     399             : 
     400             :      [WS][TOKEN 0][DELIM][WS][TOKEN 1][DELIM]...[WS][TOKEN N]{[DELIM][WS][NUL],[NUL]}
     401             : 
     402             :    in-place, into:
     403             : 
     404             :      [WS][TOKEN 0][NUL][WS][TOKEN 1][NUL]...[WS][TOKEN tok_cnt-1][NUL]
     405             : 
     406             :    and returns tok_cnt.
     407             : 
     408             :    Further, on return, tok[i] for i in [0,min(tok_cnt,tok_max)) where
     409             :    tok_cnt is the number of tokens in cstr will point to the first
     410             :    byte of each token.  Due to the tokenization, each one of these will
     411             :    be properly '\0' terminated.
     412             : 
     413             :    Above, [WS] is a sequence of zero or more whitespace characters,
     414             :    [TOKEN *] are a sequence of zero or more non-delim and non-NUL
     415             :    characters and delim is assumed to be a non-NUL non-whitespace
     416             :    character (e.g. ',').
     417             : 
     418             :    As such:
     419             :    - The original cstr is clobbered by this call.
     420             :    - tok[*] point to a properly terminated cstr into the original cstr
     421             :      on return.  They thus have the same lifetime issues as the original
     422             :      cstr.
     423             :    - If tok_cnt > tok_max, tok wasn't large enough to hold all the
     424             :      tokens found in the cstr.  Only the first max are available in
     425             :      tok[*] (the entire string was still tokenized though).
     426             :    - Found tokens will not have any leading whitespace.
     427             :    - Found tokens might have internal or trailing whitespace.
     428             :    - Zero length tokens are possible.  E.g. assuming delim==':', the cstr
     429             :      "a: b::d: :f" has the tokens: "a", "b", "", "d", "", "f".
     430             :    - If the final token is zero length, it should use an explicit
     431             :      delimiter.  E.g. assuming delim=='|':
     432             :        "a|b"     has tokens "a", "b"
     433             :        "a|b|"    has tokens "a", "b"
     434             :        "a|b| "   has tokens "a", "b"
     435             :        "a|b||"   has tokens "a", "b", ""
     436             :        "a|b| |"  has tokens "a", "b", ""
     437             :        "a|b| | " has tokens "a", "b", ""
     438             :    - This is also true if the final token is the initial token.  E.g.
     439             :      assuming delim==';':
     440             :        ""    has no tokens
     441             :        " "   has no tokens
     442             :        ";"   has the token ""
     443             :        " ;"  has the token ""
     444             :        " ; " has the token "" */
     445             : 
     446             : ulong
     447             : fd_cstr_tokenize( char ** tok,
     448             :                   ulong   tok_max,
     449             :                   char *  cstr,
     450             :                   char    delim );
     451             : 
     452             : /* fd_cstr_append_utf8 appends the UTF-8 encoding of a Unicode code
     453             :    point into p.  Assumes p is valid (non-NULL and room for 1-4 chars
     454             :    and a final terminating '\0'). */
     455             : 
     456             : static inline char *
     457             : fd_cstr_append_utf8( char * p,
     458         411 :                      uint   rune ) {
     459         411 :   if( FD_LIKELY( rune<=0x7f ) ) {
     460         387 :     *(p++) = (char)rune;
     461         387 :   } else if( rune<=0x7ff ) {
     462           6 :     *(p++) = (char)( 0xc0 |  (rune>>6)       );
     463           6 :     *(p++) = (char)( 0x80 | ((rune   )&0x3f) );
     464          18 :   } else if( rune<=0xffff ) {
     465          12 :     *(p++) = (char)( 0xe0 |  (rune>>12)       );
     466          12 :     *(p++) = (char)( 0x80 | ((rune>> 6)&0x3f) );
     467          12 :     *(p++) = (char)( 0x80 | ((rune    )&0x3f) );
     468          12 :   } else if( rune<=0x10ffff ) {
     469           6 :     *(p++) = (char)( 0xf0 |  (rune>>18)       );
     470           6 :     *(p++) = (char)( 0x80 | ((rune>>12)&0x3f) );
     471           6 :     *(p++) = (char)( 0x80 | ((rune>> 6)&0x3f) );
     472           6 :     *(p++) = (char)( 0x80 |  (rune     &0x3f) );
     473           6 :   } else {
     474             :     /* replacement char */
     475           0 :     *(p++) = (char)0xef;
     476           0 :     *(p++) = (char)0xbf;
     477           0 :     *(p++) = (char)0xbd;
     478           0 :   }
     479         411 :   return p;
     480         411 : }
     481             : 
     482             : FD_PROTOTYPES_END
     483             : 
     484             : /* The below macros guarantee the corresponding ctype.h functions return
     485             :    a value strictly in [0,1].  These still need the caller to include
     486             :    <ctype.h> to use.
     487             : 
     488             :    For context, the vast majority of developers reasonably expect
     489             :    ctype.h functions (like isspace) return 1/0 if the given character
     490             :    is/is not in the tested class.
     491             : 
     492             :    But the standard actually says these functions return non-zero/0.
     493             : 
     494             :    Many common standard libraries exploit this ambiguity.  For example,
     495             :    isspace('\n') returns 8192 on recent linux-gcc-x86_64.
     496             : 
     497             :    In most usage, this subtle distinction does not make a difference.
     498             : 
     499             :    That makes it worse.
     500             : 
     501             :    When the distinction matters, it is incredibly difficult and time
     502             :    consuming to debug.  Consider using ctype.h functions in user input
     503             :    parsing to compute the case of a switch statement.  Suddenly,
     504             :    seemingly innocous code run on seemingly innocous input generates a
     505             :    branch to a mystifying place.  Hilarity ensues.
     506             : 
     507             :    Worse still, this is a massive security risk.  Consider using ctype.h
     508             :    in a mission critical VM implementation.  Strictly deterministic
     509             :    verifiable cross platform behavior is a necessity.  Should a
     510             :    malicious smart contract be able to halt a chain because the standard
     511             :    absent mindedly gave isspace dubious flexibility on its return value?
     512             : 
     513             :    That is, this behavior is absolutely vile.
     514             : 
     515             :    This is an example of "implicitly specified behavior".  There is some
     516             :    behavior that must followed bit-for-bit.  But nobody knows what that
     517             :    behavior is because the standard was written poorly.  And then the
     518             :    library implementation blindly followed the standard and ignored
     519             :    developer usability in ambiguous situations.
     520             : 
     521             :    This also violates the core UNIX principle (and generally good idea
     522             :    in engineering or beyond) of "be generous in what you accept and
     523             :    strict in what you produce".  Given the generally absymal language
     524             :    handling of boolean values, the only practical way to handle
     525             :    true/false reliably and efficiently is accept non-zero/0 but only
     526             :    produce 1/0.  (And, since both sides have some responsibility in the
     527             :    examples above, the library implementation is more at fault as it is
     528             :    more foundational, more trusted and more reused.  Library code should
     529             :    be held to a higher standard than code built on top of it.)
     530             : 
     531             :    Any use of naked ctype.h functions should be considered harmful and
     532             :    eradicated with extreme prejudice until the language and standard
     533             :    library implementations get a clue (not holding my breath).
     534             : 
     535             :    TL;DR ctype.h but sane. */
     536             : 
     537     3492111 : #define fd_isalnum(c)  (!!isalnum((c)))
     538         768 : #define fd_isalpha(c)  (!!isalpha((c)))
     539         768 : #define fd_iscntrl(c)  (!!iscntrl((c)))
     540      182988 : #define fd_isdigit(c)  (!!isdigit((c)))
     541         768 : #define fd_isgraph(c)  (!!isgraph((c)))
     542         768 : #define fd_islower(c)  (!!islower((c)))
     543         768 : #define fd_isprint(c)  (!!isprint((c)))
     544     3152973 : #define fd_ispunct(c)  (!!ispunct((c)))
     545      336954 : #define fd_isspace(c)  (!!isspace((c)))
     546         768 : #define fd_isupper(c)  (!!isupper((c)))
     547      192369 : #define fd_isxdigit(c) (!!isxdigit((c)))
     548         768 : #define fd_isascii(c)  (!!isascii((c)))
     549         768 : #define fd_isblank(c)  (!!isblank((c)))
     550             : 
     551             : #endif /* HEADER_fd_src_util_cstr_fd_cstr_h */

Generated by: LCOV version 1.14