damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

pparseint.h (15931B)


      1 #ifndef PPARSEINT_H
      2 #define PPARSEINT_H
      3 
      4 #ifdef __cplusplus
      5 extern "C" {
      6 #endif
      7 
      8 /*
      9  * Type specific integer parsers:
     10  *
     11  *     const char *
     12  *     parse_<type-name>(const char *buf, size_t len, <type> *value, int *status);
     13  *
     14  *     parse_uint64, parse_int64
     15  *     parse_uint32, parse_int32
     16  *     parse_uint16, parse_int16
     17  *     parse_uint8, parse_int8
     18  *     parse_ushort,  parse_short
     19  *     parse_uint, parse_int
     20  *     parse_ulong, parse_long
     21  *
     22  * Leading space must be stripped in advance. Status argument can be
     23  * null.
     24  *
     25  * Returns pointer to end of match and a non-negative status code
     26  * on succcess (0 for unsigned, 1 for signed):
     27  *
     28  *     PARSE_INTEGER_UNSIGNED
     29  *     PARSE_INTEGER_SIGNED
     30  *
     31  * Returns null with a negative status code and unmodified value on
     32  * invalid integer formats:
     33  *
     34  *     PARSE_INTEGER_OVERFLOW
     35  *     PARSE_INTEGER_UNDERFLOW
     36  *     PARSE_INTEGER_INVALID
     37  *
     38  * Returns input buffer with negative status code and unmodified value
     39  * if first character does not start an integer (not a sign or a digit).
     40  *
     41  *     PARSE_INTEGER_UNMATCHED
     42  *     PARSE_INTEGER_END
     43  *
     44  * The signed parsers only works with two's complement architectures.
     45  *
     46  * Note: the corresponding parse_float and parse_double parsers do not
     47  * have a status argument because +/-Inf and NaN are conventionally used
     48  * for this.
     49  */
     50 
     51 #include "limits.h"
     52 #ifndef UINT8_MAX
     53 #include <stdint.h>
     54 #endif
     55 
     56 #define PARSE_INTEGER_UNSIGNED       0
     57 #define PARSE_INTEGER_SIGNED         1
     58 #define PARSE_INTEGER_OVERFLOW      -1
     59 #define PARSE_INTEGER_UNDERFLOW     -2
     60 #define PARSE_INTEGER_INVALID       -3
     61 #define PARSE_INTEGER_UNMATCHED     -4
     62 #define PARSE_INTEGER_END           -5
     63 
     64 /*
     65  * Generic integer parser that holds 64-bit unsigned values and stores
     66  * sign separately. Leading space is not valid.
     67  *
     68  * Note: this function differs from the type specific parsers like
     69  * parse_int64 by not negating the value when there is a sign. It
     70  * differs from parse_uint64 by being able to return a negative
     71  * UINT64_MAX successfully.
     72  *
     73  * This parser is used by all type specific integer parsers.
     74  *
     75  * Status argument can be null.
     76  */
     77 static const char *parse_integer(const char *buf, size_t len, uint64_t *value, int *status)
     78 {
     79     uint64_t x0, x = 0;
     80     const char *k, *end = buf + len;
     81     int sign, status_;
     82 
     83     if (!status) {
     84         status = &status_;
     85     }
     86     if (buf == end) {
     87         *status = PARSE_INTEGER_END;
     88         return buf;
     89     }
     90     k = buf;
     91     sign = *buf == '-';
     92     buf += sign;
     93     while (buf != end && *buf >= '0' && *buf <= '9') {
     94         x0 = x;
     95         x = x * 10 + (uint64_t)(*buf - '0');
     96         if (x0 > x) {
     97             *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
     98             return 0;
     99         }
    100         ++buf;
    101     }
    102     if (buf == k) {
    103         /* No number was matched, but it isn't an invalid number either. */
    104         *status = PARSE_INTEGER_UNMATCHED;
    105         return buf;
    106     }
    107     if (buf == k + sign) {
    108         *status = PARSE_INTEGER_INVALID;
    109         return 0;
    110     }
    111     if (buf != end)
    112     switch (*buf) {
    113     case 'e': case 'E': case '.': case 'p': case 'P':
    114         *status = PARSE_INTEGER_INVALID;
    115         return 0;
    116     }
    117     *value = x;
    118     *status = sign;
    119     return buf;
    120 }
    121 
    122 /*
    123  * Parse hex values like 0xff, -0xff, 0XdeAdBeaf42, cannot be trailed by '.', 'p', or 'P'.
    124  * Overflows if string is more than 16 valid hex digits. Otherwise similar to parse_integer.
    125  */
    126 static const char *parse_hex_integer(const char *buf, size_t len, uint64_t *value, int *status)
    127 {
    128     uint64_t x = 0;
    129     const char *k, *k2, *end = buf + len;
    130     int sign, status_;
    131     unsigned char c;
    132 
    133     if (!status) {
    134         status = &status_;
    135     }
    136     if (buf == end) {
    137         *status = PARSE_INTEGER_END;
    138         return buf;
    139     }
    140     sign = *buf == '-';
    141     buf += sign;
    142     if (end - buf < 2 || buf[0] != '0' || (buf[1] | 0x20) != 'x') {
    143         *status = PARSE_INTEGER_UNMATCHED;
    144         return buf - sign;
    145     }
    146     buf += 2;
    147     k = buf;
    148     k2 = end;
    149     if (end - buf > 16) {
    150         k2 = buf + 16;
    151     }
    152     while (buf != k2) {
    153         c = (unsigned char)*buf;
    154         if (c >= '0' && c <= '9') {
    155             x = x * 16 + c - '0';
    156         } else {
    157             /* Lower case. */
    158             c |= 0x20;
    159             if (c >= 'a' && c <= 'f') {
    160                 x = x * 16 + c - 'a' + 10;
    161             } else {
    162                 break;
    163             }
    164         }
    165         ++buf;
    166     }
    167     if (buf == k) {
    168         if (sign) {
    169             *status = PARSE_INTEGER_INVALID;
    170             return 0;
    171         } else {
    172             /* No number was matched, but it isn't an invalid number either. */
    173             *status = PARSE_INTEGER_UNMATCHED;
    174             return buf;
    175         }
    176     }
    177     if (buf == end) {
    178         goto done;
    179     }
    180     c = (unsigned char)*buf;
    181     if (buf == k2) {
    182         if (c >= '0' && c <= '9') {
    183             *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
    184             return 0;
    185         }
    186         c |= 0x20;
    187         if (c >= 'a' && c <= 'f') {
    188             *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
    189             return 0;
    190         }
    191     }
    192     switch (c) {
    193     case '.': case 'p': case 'P':
    194         *status = PARSE_INTEGER_INVALID;
    195         return 0;
    196     }
    197 done:
    198     *value = x;
    199     *status = sign;
    200     return buf;
    201 }
    202 
    203 
    204 #define __portable_define_parse_unsigned(NAME, TYPE, LIMIT)                 \
    205 static inline const char *parse_ ## NAME                                    \
    206         (const char *buf, size_t len, TYPE *value, int *status)             \
    207 {                                                                           \
    208     int status_ = 0;                                                        \
    209     uint64_t x;                                                             \
    210                                                                             \
    211     if (!status) {                                                          \
    212         status = &status_;                                                  \
    213     }                                                                       \
    214     buf = parse_integer(buf, len, &x, status);                              \
    215     switch (*status) {                                                      \
    216     case PARSE_INTEGER_UNSIGNED:                                            \
    217         if (x <= LIMIT) {                                                   \
    218             *value = (TYPE)x;                                               \
    219             return buf;                                                     \
    220         }                                                                   \
    221         *status = PARSE_INTEGER_OVERFLOW;                                   \
    222         return 0;                                                           \
    223     case PARSE_INTEGER_SIGNED:                                              \
    224         *status = PARSE_INTEGER_UNDERFLOW;                                  \
    225         return 0;                                                           \
    226     default:                                                                \
    227         return buf;                                                         \
    228     }                                                                       \
    229 }
    230 
    231 #define __portable_define_parse_hex_unsigned(NAME, TYPE, LIMIT)             \
    232 static inline const char *parse_hex_ ## NAME                                \
    233         (const char *buf, size_t len, TYPE *value, int *status)             \
    234 {                                                                           \
    235     int status_ = 0;                                                        \
    236     uint64_t x;                                                             \
    237                                                                             \
    238     if (!status) {                                                          \
    239         status = &status_;                                                  \
    240     }                                                                       \
    241     buf = parse_hex_integer(buf, len, &x, status);                          \
    242     switch (*status) {                                                      \
    243     case PARSE_INTEGER_UNSIGNED:                                            \
    244         if (x <= LIMIT) {                                                   \
    245             *value = (TYPE)x;                                               \
    246             return buf;                                                     \
    247         }                                                                   \
    248         *status = PARSE_INTEGER_OVERFLOW;                                   \
    249         return 0;                                                           \
    250     case PARSE_INTEGER_SIGNED:                                              \
    251         *status = PARSE_INTEGER_UNDERFLOW;                                  \
    252         return 0;                                                           \
    253     default:                                                                \
    254         return buf;                                                         \
    255     }                                                                       \
    256 }
    257 
    258 /* This assumes two's complement. */
    259 #define __portable_define_parse_signed(NAME, TYPE, LIMIT)                   \
    260 static inline const char *parse_ ## NAME                                    \
    261         (const char *buf, size_t len, TYPE *value, int *status)             \
    262 {                                                                           \
    263     int status_ = 0;                                                        \
    264     uint64_t x;                                                             \
    265                                                                             \
    266     if (!status) {                                                          \
    267         status = &status_;                                                  \
    268     }                                                                       \
    269     buf = parse_integer(buf, len, &x, status);                              \
    270     switch (*status) {                                                      \
    271     case PARSE_INTEGER_UNSIGNED:                                            \
    272         if (x <= LIMIT) {                                                   \
    273             *value = (TYPE)x;                                               \
    274             return buf;                                                     \
    275         }                                                                   \
    276         *status = PARSE_INTEGER_OVERFLOW;                                   \
    277         return 0;                                                           \
    278     case PARSE_INTEGER_SIGNED:                                              \
    279         if (x <= (uint64_t)(LIMIT) + 1) {                                   \
    280             *value = (TYPE)-(int64_t)x;                                     \
    281             return buf;                                                     \
    282         }                                                                   \
    283         *status = PARSE_INTEGER_UNDERFLOW;                                  \
    284         return 0;                                                           \
    285     default:                                                                \
    286         return buf;                                                         \
    287     }                                                                       \
    288 }
    289 
    290 /* This assumes two's complement. */
    291 #define __portable_define_parse_hex_signed(NAME, TYPE, LIMIT)               \
    292 static inline const char *parse_hex_ ## NAME                                \
    293         (const char *buf, size_t len, TYPE *value, int *status)             \
    294 {                                                                           \
    295     int status_ = 0;                                                        \
    296     uint64_t x;                                                             \
    297                                                                             \
    298     if (!status) {                                                          \
    299         status = &status_;                                                  \
    300     }                                                                       \
    301     buf = parse_hex_integer(buf, len, &x, status);                          \
    302     switch (*status) {                                                      \
    303     case PARSE_INTEGER_UNSIGNED:                                            \
    304         if (x <= LIMIT) {                                                   \
    305             *value = (TYPE)x;                                               \
    306             return buf;                                                     \
    307         }                                                                   \
    308         *status = PARSE_INTEGER_OVERFLOW;                                   \
    309         return 0;                                                           \
    310     case PARSE_INTEGER_SIGNED:                                              \
    311         if (x <= (uint64_t)(LIMIT) + 1) {                                   \
    312             *value = (TYPE)-(int64_t)x;                                     \
    313             return buf;                                                     \
    314         }                                                                   \
    315         *status = PARSE_INTEGER_UNDERFLOW;                                  \
    316         return 0;                                                           \
    317     default:                                                                \
    318         return buf;                                                         \
    319     }                                                                       \
    320 }
    321 
    322 static inline const char *parse_uint64(const char *buf, size_t len, uint64_t *value, int *status)
    323 {
    324     buf = parse_integer(buf, len, value, status);
    325     if (*status == PARSE_INTEGER_SIGNED) {
    326         *status = PARSE_INTEGER_UNDERFLOW;
    327         return 0;
    328     }
    329     return buf;
    330 }
    331 
    332 static inline const char *parse_hex_uint64(const char *buf, size_t len, uint64_t *value, int *status)
    333 {
    334     buf = parse_hex_integer(buf, len, value, status);
    335     if (*status == PARSE_INTEGER_SIGNED) {
    336         *status = PARSE_INTEGER_UNDERFLOW;
    337         return 0;
    338     }
    339     return buf;
    340 }
    341 
    342 __portable_define_parse_signed(int64, int64_t, INT64_MAX)
    343 __portable_define_parse_signed(int32, int32_t, INT32_MAX)
    344 __portable_define_parse_unsigned(uint16, uint16_t, UINT16_MAX)
    345 __portable_define_parse_signed(int16, int16_t, INT16_MAX)
    346 __portable_define_parse_unsigned(uint8, uint8_t, UINT8_MAX)
    347 __portable_define_parse_signed(int8, int8_t, INT8_MAX)
    348 
    349 __portable_define_parse_hex_signed(int64, int64_t, INT64_MAX)
    350 __portable_define_parse_hex_signed(int32, int32_t, INT32_MAX)
    351 __portable_define_parse_hex_unsigned(uint16, uint16_t, UINT16_MAX)
    352 __portable_define_parse_hex_signed(int16, int16_t, INT16_MAX)
    353 __portable_define_parse_hex_unsigned(uint8, uint8_t, UINT8_MAX)
    354 __portable_define_parse_hex_signed(int8, int8_t, INT8_MAX)
    355 
    356 __portable_define_parse_unsigned(ushort, unsigned short, USHRT_MAX)
    357 __portable_define_parse_signed(short, short, SHRT_MAX)
    358 __portable_define_parse_unsigned(uint, unsigned int, UINT_MAX)
    359 __portable_define_parse_signed(int, int, INT_MAX)
    360 __portable_define_parse_unsigned(ulong, unsigned long, ULONG_MAX)
    361 __portable_define_parse_signed(long, unsigned long, LONG_MAX)
    362 
    363 __portable_define_parse_hex_unsigned(ushort, unsigned short, USHRT_MAX)
    364 __portable_define_parse_hex_signed(short, short, SHRT_MAX)
    365 __portable_define_parse_hex_unsigned(uint, unsigned int, UINT_MAX)
    366 __portable_define_parse_hex_signed(int, int, INT_MAX)
    367 __portable_define_parse_hex_unsigned(ulong, unsigned long, ULONG_MAX)
    368 __portable_define_parse_hex_signed(long, unsigned long, LONG_MAX)
    369 
    370 #ifdef __cplusplus
    371 }
    372 #endif
    373 
    374 #endif /* PPARSEINT_H */