aboutsummaryrefslogtreecommitdiff
path: root/flatcc/include/flatcc/portable/pparseint.h
diff options
context:
space:
mode:
Diffstat (limited to 'flatcc/include/flatcc/portable/pparseint.h')
1 files changed, 374 insertions, 0 deletions
diff --git a/flatcc/include/flatcc/portable/pparseint.h b/flatcc/include/flatcc/portable/pparseint.h
new file mode 100644
index 0000000..96cc99f
--- /dev/null
+++ b/flatcc/include/flatcc/portable/pparseint.h
@@ -0,0 +1,374 @@
+#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 */