Hash :
d2f7b9cd
Author :
Date :
2025-04-04T17:29:35
rules: Do not use strto* parsers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
/*
* 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__[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1,
-1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1
};
#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