1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

Merge remote-tracking branch 'OFW/dev' into dev

This commit is contained in:
MX
2024-09-05 22:10:41 +03:00
52 changed files with 927 additions and 276 deletions

View File

@@ -29,6 +29,7 @@ env.Append(
File("stream/file_stream.h"),
File("stream/string_stream.h"),
File("stream/buffered_file_stream.h"),
File("strint.h"),
File("protocols/protocol_dict.h"),
File("pretty_format.h"),
File("hex.h"),

View File

@@ -1,5 +1,7 @@
#include "args.h"
#include "hex.h"
#include "strint.h"
#include "m-core.h"
size_t args_get_first_word_length(FuriString* args) {
size_t ws = furi_string_search_char(args, ' ');
@@ -21,7 +23,9 @@ bool args_read_int_and_trim(FuriString* args, int* value) {
return false;
}
if(sscanf(furi_string_get_cstr(args), "%d", value) == 1) {
int32_t temp;
if(strint_to_int32(furi_string_get_cstr(args), NULL, &temp, 10) == StrintParseNoError) {
*value = temp;
furi_string_right(args, cmd_length);
furi_string_trim(args);
return true;

121
lib/toolbox/strint.c Normal file
View File

@@ -0,0 +1,121 @@
#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)

70
lib/toolbox/strint.h Normal file
View File

@@ -0,0 +1,70 @@
/**
* @file strint.h
* Performs conversions between strings and integers.
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/** String to integer conversion error */
typedef enum {
StrintParseNoError, //!< Conversion performed successfully
StrintParseSignError, //!< Multiple leading `+` or `-` characters, or leading `-` character if the type is unsigned
StrintParseAbsentError, //!< No valid digits after the leading whitespace, sign and prefix
StrintParseOverflowError, //!< Result does not fit in the requested type
} StrintParseError;
/** See `strint_to_uint32` */
StrintParseError strint_to_uint64(const char* str, char** end, uint64_t* out, uint8_t base);
/** See `strint_to_uint32` */
StrintParseError strint_to_int64(const char* str, char** end, int64_t* out, uint8_t base);
/** Converts a string to a `uint32_t`
*
* @param[in] str Input string
* @param[out] end Pointer to first character after the number in input string
* @param[out] out Parse result
* @param[in] base Integer base
*
* @return Parse error
*
* Parses the number in the input string. The number may be surrounded by
* whitespace characters to the left and any non-digit characters to the right.
* What's considered a digit is determined by the input base in the following
* order: `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`. The number may be prefixed
* with either a `+` or a `-` to indicate its sign. The pointer to the first
* character after the leading whitespace, allowed prefixes and digits is
* assigned to `end`.
*
* If the input base is 0, the base is inferred from the leading characters of
* the number:
* - If it starts with `0x`, it's read in base 16;
* - If it starts with a `0`, it's read in base 8;
* - If it starts with `0b`, it's read in base 2.
* - Otherwise, it's read in base 10.
*
* For a description of the return codes, see `StrintParseError`. If the return
* code is something other than `StrintParseNoError`, the values at `end` and
* `out` are unaltered.
*/
StrintParseError strint_to_uint32(const char* str, char** end, uint32_t* out, uint8_t base);
/** See `strint_to_uint32` */
StrintParseError strint_to_int32(const char* str, char** end, int32_t* out, uint8_t base);
/** See `strint_to_uint32` */
StrintParseError strint_to_uint16(const char* str, char** end, uint16_t* out, uint8_t base);
/** See `strint_to_uint32` */
StrintParseError strint_to_int16(const char* str, char** end, int16_t* out, uint8_t base);
#ifdef __cplusplus
}
#endif