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:
@@ -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"),
|
||||
|
||||
@@ -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
121
lib/toolbox/strint.c
Normal 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
70
lib/toolbox/strint.h
Normal 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
|
||||
Reference in New Issue
Block a user