mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-23 13:03:13 +00:00
c9791a280a
* feat: strint_to_uint32 and tests * fix: permit explicit bases and prefixes * feat: strint_to_{int32,uint16,int16} * feat: strint_to_u?int64 * refactor: replace strtol, strtoul, sscanf with strint_to_* * fix: api symbols * docs: document parameter `end` of strint_to_uint_32 * style: apply changes requested by hedger * refactor: fix pvs-studio diagnostic * style: apply changes requested by CookiePLMonster * fix: unused var * fix: pointer type * refactor: convert atoi to strint_to_* * fix: strint_to_uint8 doesn't actually exist ._ . * fix: memory leak * style: address review comments * Toolbox: couple small comments in the code and doxygen comment update. SubGhz, Loader: fix strint usage. * Loader: fix incorrect cast Co-authored-by: あく <alleteam@gmail.com>
121 lines
4.2 KiB
C
121 lines
4.2 KiB
C
#include "strint.h"
|
|
|
|
#include <string.h>
|
|
|
|
// Splitting out the actual parser helps reduce code size. The manually
|
|
// monomorphized `strint_to_*`s are just wrappers around this generic
|
|
// implementation.
|
|
/**
|
|
* @brief Converts a string to a `uint64_t` and an auxillary sign bit, checking
|
|
* the bounds of the integer.
|
|
* @param [in] str Input string
|
|
* @param [out] end Pointer to first character after the number in input string
|
|
* @param [out] abs_out Absolute part of result
|
|
* @param [out] negative_out Sign part of result (true=negative, false=positive)
|
|
* @param [in] base Integer base
|
|
* @param [in] max_abs_negative Largest permissible absolute part of result if
|
|
* the sign is negative
|
|
* @param [in] max_positive Largest permissible absolute part of result if the
|
|
* sign is positive
|
|
*/
|
|
StrintParseError strint_to_uint64_internal(
|
|
const char* str,
|
|
char** end,
|
|
uint64_t* abs_out,
|
|
bool* negative_out,
|
|
uint8_t base,
|
|
uint64_t max_abs_negative,
|
|
uint64_t max_positive) {
|
|
// skip whitespace
|
|
while(((*str >= '\t') && (*str <= '\r')) || *str == ' ') {
|
|
str++;
|
|
}
|
|
|
|
// read sign
|
|
bool negative = false;
|
|
if(*str == '+' || *str == '-') {
|
|
if(*str == '-') negative = true;
|
|
str++;
|
|
}
|
|
if(*str == '+' || *str == '-') return StrintParseSignError;
|
|
if(max_abs_negative == 0 && negative) return StrintParseSignError;
|
|
|
|
// infer base
|
|
// not assigning directly to `base' to permit prefixes with explicit bases
|
|
uint8_t inferred_base = 0;
|
|
if(strncasecmp(str, "0x", 2) == 0) {
|
|
inferred_base = 16;
|
|
str += 2;
|
|
} else if(strncasecmp(str, "0b", 2) == 0) {
|
|
inferred_base = 2;
|
|
str += 2;
|
|
} else if(*str == '0') {
|
|
inferred_base = 8;
|
|
str++;
|
|
} else {
|
|
inferred_base = 10;
|
|
}
|
|
if(base == 0) base = inferred_base;
|
|
|
|
// read digits
|
|
uint64_t limit = negative ? max_abs_negative : max_positive;
|
|
uint64_t mul_limit = limit / base;
|
|
uint64_t result = 0;
|
|
int read_total = 0;
|
|
while(*str != 0) {
|
|
int digit_value;
|
|
if(*str >= '0' && *str <= '9') {
|
|
digit_value = *str - '0';
|
|
} else if(*str >= 'A' && *str <= 'Z') {
|
|
digit_value = *str - 'A' + 10;
|
|
} else if(*str >= 'a' && *str <= 'z') {
|
|
digit_value = *str - 'a' + 10;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
if(digit_value >= base) {
|
|
break;
|
|
}
|
|
|
|
if(result > mul_limit) return StrintParseOverflowError;
|
|
result *= base;
|
|
if(result > limit - digit_value) return StrintParseOverflowError;
|
|
result += digit_value;
|
|
|
|
read_total++;
|
|
str++;
|
|
}
|
|
|
|
if(read_total == 0) {
|
|
if(inferred_base == 8) {
|
|
// there's just a single zero
|
|
result = 0;
|
|
} else {
|
|
return StrintParseAbsentError;
|
|
}
|
|
}
|
|
|
|
if(abs_out) *abs_out = result;
|
|
if(negative_out) *negative_out = negative;
|
|
if(end) *end = (char*)str; // rabbit hole: https://c-faq.com/ansi/constmismatch.html
|
|
return StrintParseNoError;
|
|
}
|
|
|
|
#define STRINT_MONO(name, ret_type, neg_abs_limit, pos_limit) \
|
|
StrintParseError name(const char* str, char** end, ret_type* out, uint8_t base) { \
|
|
uint64_t absolute; \
|
|
bool negative; \
|
|
StrintParseError err = strint_to_uint64_internal( \
|
|
str, end, &absolute, &negative, base, (neg_abs_limit), (pos_limit)); \
|
|
if(err) return err; \
|
|
if(out) *out = (negative ? (-(ret_type)absolute) : ((ret_type)absolute)); \
|
|
return StrintParseNoError; \
|
|
}
|
|
|
|
STRINT_MONO(strint_to_uint64, uint64_t, 0, UINT64_MAX)
|
|
STRINT_MONO(strint_to_int64, int64_t, (uint64_t)INT64_MAX + 1, INT64_MAX)
|
|
STRINT_MONO(strint_to_uint32, uint32_t, 0, UINT32_MAX)
|
|
STRINT_MONO(strint_to_int32, int32_t, (uint64_t)INT32_MAX + 1, INT32_MAX)
|
|
STRINT_MONO(strint_to_uint16, uint16_t, 0, UINT16_MAX)
|
|
STRINT_MONO(strint_to_int16, int16_t, (uint64_t)INT16_MAX + 1, INT16_MAX)
|