/*
* Copyright © 2025 Pierre Le Marre <dev@wismill.eu>
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "config.h"
#include <stdbool.h>
#include <stdint.h>
#include "utils.h"
/*
* Define various parsers to avoid the use of strto* -- it’s slower and accepts
* a bunch of stuff we don’t want to allow, like signs, spaces, even locale stuff.
*
* But the real feature is that it does not require a NULL-terminated string, so
* it works safely *also* on any buffer, assuming the correct corresponding size
* is provided. For NULL-terminated strings, just pass `SIZE_MAX` as the length:
* the parsers will *always* stop on a NULL character.
*/
#define MAKE_PARSE_DEC_TO(type, max) \
static inline int \
parse_dec_to_##type(const char *s, size_t len, type (*out)) \
{ \
type result = 0; \
size_t i; \
for (i = 0; \
i < len && (unsigned char)(s[i] - '0') < 10U && \
result <= (max) / 10 && \
result * 10 <= (max) - (unsigned char) (s[i] - '0');\
i++) { \
result = result * 10 + (s[i] - '0'); \
} \
*out = result; \
/* Check if there is more to parse */ \
/* We can safely convert the length to int on success */ \
return (i >= len || (unsigned char)(s[i] - '0') >= 10U) \
? (int) i \
: -1; \
}
/**
* Parse a `uint32_t` in decimal format.
*
* @returns -1 on error (overflow) or the count of characters parsed.
*/
MAKE_PARSE_DEC_TO(uint32_t, UINT32_MAX)
/**
* Parse a `uint64_t` in decimal format.
*
* @returns -1 on error (overflow) or the count of characters parsed.
*/
MAKE_PARSE_DEC_TO(uint64_t, UINT64_MAX)
#undef MAKE_PARSE_DEC_TO
static const unsigned char digits__[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 10, 11, 12, 13, 14, 15, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 10, 11, 12, 13, 14, 15, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff
};
#define MAKE_PARSE_HEX_TO(type, max) \
static inline int \
parse_hex_to_##type(const char *s, size_t len, type (*out)) \
{ \
type result = 0; \
size_t i = 0; \
for (; i < len && digits__[(unsigned char) s[i]] < 16u && \
result <= (max) >> 4; \
i++) { \
result = result * 16 + digits__[(unsigned char) s[i]]; \
} \
*out = result; \
/* Check if there is more to parse */ \
/* We can safely convert the length to int on success */ \
return (i >= len || !is_xdigit(s[i])) ? (int) i : -1; \
}
/**
* Parse a `uint32_t` in hexdecimal format.
*
* @returns -1 on error (overflow) or the count of characters parsed.
*/
MAKE_PARSE_HEX_TO(uint32_t, UINT32_MAX)
/**
* Parse a `uint64_t` in hexdecimal format.
*
* @returns -1 on error (overflow) or the count of characters parsed.
*/
MAKE_PARSE_HEX_TO(uint64_t, UINT64_MAX)
#undef MAKE_PARSE_HEX_TO