1
0
mirror of git://jb55.com/damus synced 2024-09-18 19:23:49 +00:00
damus/nostrdb/flatcc/portable/pparseint.h
2023-08-25 19:05:34 -07:00

375 lines
16 KiB
C

#ifndef PPARSEINT_H
#define PPARSEINT_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* Type specific integer parsers:
*
* const char *
* parse_<type-name>(const char *buf, size_t len, <type> *value, int *status);
*
* parse_uint64, parse_int64
* parse_uint32, parse_int32
* parse_uint16, parse_int16
* parse_uint8, parse_int8
* parse_ushort, parse_short
* parse_uint, parse_int
* parse_ulong, parse_long
*
* Leading space must be stripped in advance. Status argument can be
* null.
*
* Returns pointer to end of match and a non-negative status code
* on succcess (0 for unsigned, 1 for signed):
*
* PARSE_INTEGER_UNSIGNED
* PARSE_INTEGER_SIGNED
*
* Returns null with a negative status code and unmodified value on
* invalid integer formats:
*
* PARSE_INTEGER_OVERFLOW
* PARSE_INTEGER_UNDERFLOW
* PARSE_INTEGER_INVALID
*
* Returns input buffer with negative status code and unmodified value
* if first character does not start an integer (not a sign or a digit).
*
* PARSE_INTEGER_UNMATCHED
* PARSE_INTEGER_END
*
* The signed parsers only works with two's complement architectures.
*
* Note: the corresponding parse_float and parse_double parsers do not
* have a status argument because +/-Inf and NaN are conventionally used
* for this.
*/
#include "limits.h"
#ifndef UINT8_MAX
#include <stdint.h>
#endif
#define PARSE_INTEGER_UNSIGNED 0
#define PARSE_INTEGER_SIGNED 1
#define PARSE_INTEGER_OVERFLOW -1
#define PARSE_INTEGER_UNDERFLOW -2
#define PARSE_INTEGER_INVALID -3
#define PARSE_INTEGER_UNMATCHED -4
#define PARSE_INTEGER_END -5
/*
* Generic integer parser that holds 64-bit unsigned values and stores
* sign separately. Leading space is not valid.
*
* Note: this function differs from the type specific parsers like
* parse_int64 by not negating the value when there is a sign. It
* differs from parse_uint64 by being able to return a negative
* UINT64_MAX successfully.
*
* This parser is used by all type specific integer parsers.
*
* Status argument can be null.
*/
static const char *parse_integer(const char *buf, size_t len, uint64_t *value, int *status)
{
uint64_t x0, x = 0;
const char *k, *end = buf + len;
int sign, status_;
if (!status) {
status = &status_;
}
if (buf == end) {
*status = PARSE_INTEGER_END;
return buf;
}
k = buf;
sign = *buf == '-';
buf += sign;
while (buf != end && *buf >= '0' && *buf <= '9') {
x0 = x;
x = x * 10 + (uint64_t)(*buf - '0');
if (x0 > x) {
*status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
return 0;
}
++buf;
}
if (buf == k) {
/* No number was matched, but it isn't an invalid number either. */
*status = PARSE_INTEGER_UNMATCHED;
return buf;
}
if (buf == k + sign) {
*status = PARSE_INTEGER_INVALID;
return 0;
}
if (buf != end)
switch (*buf) {
case 'e': case 'E': case '.': case 'p': case 'P':
*status = PARSE_INTEGER_INVALID;
return 0;
}
*value = x;
*status = sign;
return buf;
}
/*
* Parse hex values like 0xff, -0xff, 0XdeAdBeaf42, cannot be trailed by '.', 'p', or 'P'.
* Overflows if string is more than 16 valid hex digits. Otherwise similar to parse_integer.
*/
static const char *parse_hex_integer(const char *buf, size_t len, uint64_t *value, int *status)
{
uint64_t x = 0;
const char *k, *k2, *end = buf + len;
int sign, status_;
unsigned char c;
if (!status) {
status = &status_;
}
if (buf == end) {
*status = PARSE_INTEGER_END;
return buf;
}
sign = *buf == '-';
buf += sign;
if (end - buf < 2 || buf[0] != '0' || (buf[1] | 0x20) != 'x') {
*status = PARSE_INTEGER_UNMATCHED;
return buf - sign;
}
buf += 2;
k = buf;
k2 = end;
if (end - buf > 16) {
k2 = buf + 16;
}
while (buf != k2) {
c = (unsigned char)*buf;
if (c >= '0' && c <= '9') {
x = x * 16 + c - '0';
} else {
/* Lower case. */
c |= 0x20;
if (c >= 'a' && c <= 'f') {
x = x * 16 + c - 'a' + 10;
} else {
break;
}
}
++buf;
}
if (buf == k) {
if (sign) {
*status = PARSE_INTEGER_INVALID;
return 0;
} else {
/* No number was matched, but it isn't an invalid number either. */
*status = PARSE_INTEGER_UNMATCHED;
return buf;
}
}
if (buf == end) {
goto done;
}
c = (unsigned char)*buf;
if (buf == k2) {
if (c >= '0' && c <= '9') {
*status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
return 0;
}
c |= 0x20;
if (c >= 'a' && c <= 'f') {
*status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
return 0;
}
}
switch (c) {
case '.': case 'p': case 'P':
*status = PARSE_INTEGER_INVALID;
return 0;
}
done:
*value = x;
*status = sign;
return buf;
}
#define __portable_define_parse_unsigned(NAME, TYPE, LIMIT) \
static inline const char *parse_ ## NAME \
(const char *buf, size_t len, TYPE *value, int *status) \
{ \
int status_ = 0; \
uint64_t x; \
\
if (!status) { \
status = &status_; \
} \
buf = parse_integer(buf, len, &x, status); \
switch (*status) { \
case PARSE_INTEGER_UNSIGNED: \
if (x <= LIMIT) { \
*value = (TYPE)x; \
return buf; \
} \
*status = PARSE_INTEGER_OVERFLOW; \
return 0; \
case PARSE_INTEGER_SIGNED: \
*status = PARSE_INTEGER_UNDERFLOW; \
return 0; \
default: \
return buf; \
} \
}
#define __portable_define_parse_hex_unsigned(NAME, TYPE, LIMIT) \
static inline const char *parse_hex_ ## NAME \
(const char *buf, size_t len, TYPE *value, int *status) \
{ \
int status_ = 0; \
uint64_t x; \
\
if (!status) { \
status = &status_; \
} \
buf = parse_hex_integer(buf, len, &x, status); \
switch (*status) { \
case PARSE_INTEGER_UNSIGNED: \
if (x <= LIMIT) { \
*value = (TYPE)x; \
return buf; \
} \
*status = PARSE_INTEGER_OVERFLOW; \
return 0; \
case PARSE_INTEGER_SIGNED: \
*status = PARSE_INTEGER_UNDERFLOW; \
return 0; \
default: \
return buf; \
} \
}
/* This assumes two's complement. */
#define __portable_define_parse_signed(NAME, TYPE, LIMIT) \
static inline const char *parse_ ## NAME \
(const char *buf, size_t len, TYPE *value, int *status) \
{ \
int status_ = 0; \
uint64_t x; \
\
if (!status) { \
status = &status_; \
} \
buf = parse_integer(buf, len, &x, status); \
switch (*status) { \
case PARSE_INTEGER_UNSIGNED: \
if (x <= LIMIT) { \
*value = (TYPE)x; \
return buf; \
} \
*status = PARSE_INTEGER_OVERFLOW; \
return 0; \
case PARSE_INTEGER_SIGNED: \
if (x <= (uint64_t)(LIMIT) + 1) { \
*value = (TYPE)-(int64_t)x; \
return buf; \
} \
*status = PARSE_INTEGER_UNDERFLOW; \
return 0; \
default: \
return buf; \
} \
}
/* This assumes two's complement. */
#define __portable_define_parse_hex_signed(NAME, TYPE, LIMIT) \
static inline const char *parse_hex_ ## NAME \
(const char *buf, size_t len, TYPE *value, int *status) \
{ \
int status_ = 0; \
uint64_t x; \
\
if (!status) { \
status = &status_; \
} \
buf = parse_hex_integer(buf, len, &x, status); \
switch (*status) { \
case PARSE_INTEGER_UNSIGNED: \
if (x <= LIMIT) { \
*value = (TYPE)x; \
return buf; \
} \
*status = PARSE_INTEGER_OVERFLOW; \
return 0; \
case PARSE_INTEGER_SIGNED: \
if (x <= (uint64_t)(LIMIT) + 1) { \
*value = (TYPE)-(int64_t)x; \
return buf; \
} \
*status = PARSE_INTEGER_UNDERFLOW; \
return 0; \
default: \
return buf; \
} \
}
static inline const char *parse_uint64(const char *buf, size_t len, uint64_t *value, int *status)
{
buf = parse_integer(buf, len, value, status);
if (*status == PARSE_INTEGER_SIGNED) {
*status = PARSE_INTEGER_UNDERFLOW;
return 0;
}
return buf;
}
static inline const char *parse_hex_uint64(const char *buf, size_t len, uint64_t *value, int *status)
{
buf = parse_hex_integer(buf, len, value, status);
if (*status == PARSE_INTEGER_SIGNED) {
*status = PARSE_INTEGER_UNDERFLOW;
return 0;
}
return buf;
}
__portable_define_parse_signed(int64, int64_t, INT64_MAX)
__portable_define_parse_signed(int32, int32_t, INT32_MAX)
__portable_define_parse_unsigned(uint16, uint16_t, UINT16_MAX)
__portable_define_parse_signed(int16, int16_t, INT16_MAX)
__portable_define_parse_unsigned(uint8, uint8_t, UINT8_MAX)
__portable_define_parse_signed(int8, int8_t, INT8_MAX)
__portable_define_parse_hex_signed(int64, int64_t, INT64_MAX)
__portable_define_parse_hex_signed(int32, int32_t, INT32_MAX)
__portable_define_parse_hex_unsigned(uint16, uint16_t, UINT16_MAX)
__portable_define_parse_hex_signed(int16, int16_t, INT16_MAX)
__portable_define_parse_hex_unsigned(uint8, uint8_t, UINT8_MAX)
__portable_define_parse_hex_signed(int8, int8_t, INT8_MAX)
__portable_define_parse_unsigned(ushort, unsigned short, USHRT_MAX)
__portable_define_parse_signed(short, short, SHRT_MAX)
__portable_define_parse_unsigned(uint, unsigned int, UINT_MAX)
__portable_define_parse_signed(int, int, INT_MAX)
__portable_define_parse_unsigned(ulong, unsigned long, ULONG_MAX)
__portable_define_parse_signed(long, unsigned long, LONG_MAX)
__portable_define_parse_hex_unsigned(ushort, unsigned short, USHRT_MAX)
__portable_define_parse_hex_signed(short, short, SHRT_MAX)
__portable_define_parse_hex_unsigned(uint, unsigned int, UINT_MAX)
__portable_define_parse_hex_signed(int, int, INT_MAX)
__portable_define_parse_hex_unsigned(ulong, unsigned long, ULONG_MAX)
__portable_define_parse_hex_signed(long, unsigned long, LONG_MAX)
#ifdef __cplusplus
}
#endif
#endif /* PPARSEINT_H */