Optimized string-to-number functions (#21484)
This commit is contained in:
parent
e787cdd1cb
commit
16af2148b9
@ -28,6 +28,146 @@
|
|||||||
|
|
||||||
#include "../MarlinCore.h"
|
#include "../MarlinCore.h"
|
||||||
|
|
||||||
|
#ifdef __AVR__
|
||||||
|
|
||||||
|
static FORCE_INLINE uint32_t mult10(uint32_t val) {
|
||||||
|
uint32_t tmp = val;
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"add %A[tmp], %A[tmp]\n"
|
||||||
|
"adc %B[tmp], %B[tmp]\n"
|
||||||
|
"adc %C[tmp], %C[tmp]\n"
|
||||||
|
"adc %D[tmp], %D[tmp]\n"
|
||||||
|
"add %A[tmp], %A[tmp]\n"
|
||||||
|
"adc %B[tmp], %B[tmp]\n"
|
||||||
|
"adc %C[tmp], %C[tmp]\n"
|
||||||
|
"adc %D[tmp], %D[tmp]\n"
|
||||||
|
"add %A[val], %A[tmp]\n"
|
||||||
|
"adc %B[val], %B[tmp]\n"
|
||||||
|
"adc %C[val], %C[tmp]\n"
|
||||||
|
"adc %D[val], %D[tmp]\n"
|
||||||
|
"add %A[val], %A[val]\n"
|
||||||
|
"adc %B[val], %B[val]\n"
|
||||||
|
"adc %C[val], %C[val]\n"
|
||||||
|
"adc %D[val], %D[val]\n"
|
||||||
|
: [val] "+&r" (val),
|
||||||
|
[tmp] "+&r" (tmp)
|
||||||
|
);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static FORCE_INLINE uint32_t mult10(uint32_t val) { return val * 10; }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// cheap base-10 strto(u)l.
|
||||||
|
// does not check for errors.
|
||||||
|
int32_t parse_int32(const char *buf) {
|
||||||
|
char c;
|
||||||
|
|
||||||
|
// Get a char, skipping leading spaces
|
||||||
|
do { c = *buf++; } while (c == ' ');
|
||||||
|
|
||||||
|
// check for sign
|
||||||
|
bool is_negative = (c == '-');
|
||||||
|
if (is_negative || c == '+')
|
||||||
|
c = *buf++;
|
||||||
|
|
||||||
|
// optimization for first digit (no multiplication)
|
||||||
|
uint8_t uc = c - '0';
|
||||||
|
if (uc > 9) return 0;
|
||||||
|
|
||||||
|
// read unsigned value
|
||||||
|
uint32_t uval = uc;
|
||||||
|
while (true) {
|
||||||
|
c = *buf++;
|
||||||
|
uc = c - '0';
|
||||||
|
if (uc > 9) break;
|
||||||
|
uval = mult10(uval) + uc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_negative ? -uval : uval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cheap strtof.
|
||||||
|
// does not support nan/infinity or exponent notation.
|
||||||
|
// does not check for errors.
|
||||||
|
float parse_float(const char *buf) {
|
||||||
|
char c;
|
||||||
|
|
||||||
|
// Get a char, skipping leading spaces
|
||||||
|
do { c = *buf++; } while (c == ' ');
|
||||||
|
|
||||||
|
// check for sign
|
||||||
|
bool is_negative = (c == '-');
|
||||||
|
if (is_negative || c == '+')
|
||||||
|
c = *buf++;
|
||||||
|
|
||||||
|
// read unsigned value and decimal point
|
||||||
|
uint32_t uval;
|
||||||
|
uint8_t exp_dec;
|
||||||
|
uint8_t uc = c - '0';
|
||||||
|
if (uc <= 9) {
|
||||||
|
uval = uc;
|
||||||
|
exp_dec = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (c != '.') return 0;
|
||||||
|
uval = 0;
|
||||||
|
exp_dec = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t exp = 0;
|
||||||
|
while (true) {
|
||||||
|
c = *buf++;
|
||||||
|
uc = c - '0';
|
||||||
|
if (uc <= 9) {
|
||||||
|
exp -= exp_dec;
|
||||||
|
uval = mult10(uval) + uc;
|
||||||
|
if (uval >= (UINT32_MAX - 9) / 10) {
|
||||||
|
// overflow. keep reading digits until decimal point.
|
||||||
|
while (exp_dec == 0) {
|
||||||
|
c = *buf++;
|
||||||
|
uc = c - '0';
|
||||||
|
if (uc > 9) break;
|
||||||
|
exp++;
|
||||||
|
}
|
||||||
|
goto overflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (c != '.' || exp_dec != 0) break;
|
||||||
|
exp_dec = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// early return for 0
|
||||||
|
if (uval == 0) return 0;
|
||||||
|
|
||||||
|
overflow:
|
||||||
|
|
||||||
|
// convert to float and apply sign
|
||||||
|
float fval = uval;
|
||||||
|
if (is_negative) fval *= -1;
|
||||||
|
|
||||||
|
// apply exponent (up to 1e-15 / 1e+15)
|
||||||
|
if (exp < 0) {
|
||||||
|
if (exp <= -8) { fval *= 1e-8; exp += 8; }
|
||||||
|
if (exp <= -4) { fval *= 1e-4; exp += 4; }
|
||||||
|
if (exp <= -2) { fval *= 1e-2; exp += 2; }
|
||||||
|
if (exp <= -1) { fval *= 1e-1; exp += 1; }
|
||||||
|
}
|
||||||
|
else if (exp > 0) {
|
||||||
|
if (exp >= 8) { fval *= 1e+8; exp -= 8; }
|
||||||
|
if (exp >= 4) { fval *= 1e+4; exp -= 4; }
|
||||||
|
if (exp >= 2) { fval *= 1e+2; exp -= 2; }
|
||||||
|
if (exp >= 1) { fval *= 1e+1; exp -= 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return fval;
|
||||||
|
}
|
||||||
|
|
||||||
// Must be declared for allocation and to satisfy the linker
|
// Must be declared for allocation and to satisfy the linker
|
||||||
// Zero values need no initialization.
|
// Zero values need no initialization.
|
||||||
|
|
||||||
|
@ -42,6 +42,10 @@
|
|||||||
typedef enum : uint8_t { LINEARUNIT_MM, LINEARUNIT_INCH } LinearUnit;
|
typedef enum : uint8_t { LINEARUNIT_MM, LINEARUNIT_INCH } LinearUnit;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
int32_t parse_int32(const char *buf);
|
||||||
|
float parse_float(const char *buf);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GCode parser
|
* GCode parser
|
||||||
*
|
*
|
||||||
@ -256,29 +260,12 @@ public:
|
|||||||
// The value as a string
|
// The value as a string
|
||||||
static inline char* value_string() { return value_ptr; }
|
static inline char* value_string() { return value_ptr; }
|
||||||
|
|
||||||
// Float removes 'E' to prevent scientific notation interpretation
|
// Code value as float
|
||||||
static inline float value_float() {
|
static inline float value_float() { return value_ptr ? parse_float(value_ptr) : 0.0; }
|
||||||
if (value_ptr) {
|
|
||||||
char *e = value_ptr;
|
|
||||||
for (;;) {
|
|
||||||
const char c = *e;
|
|
||||||
if (c == '\0' || c == ' ') break;
|
|
||||||
if (c == 'E' || c == 'e') {
|
|
||||||
*e = '\0';
|
|
||||||
const float ret = strtof(value_ptr, nullptr);
|
|
||||||
*e = c;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
++e;
|
|
||||||
}
|
|
||||||
return strtof(value_ptr, nullptr);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code value as a long or ulong
|
// Code value as a long or ulong
|
||||||
static inline int32_t value_long() { return value_ptr ? strtol(value_ptr, nullptr, 10) : 0L; }
|
static inline int32_t value_long() { return value_ptr ? parse_int32(value_ptr) : 0L; }
|
||||||
static inline uint32_t value_ulong() { return value_ptr ? strtoul(value_ptr, nullptr, 10) : 0UL; }
|
static inline uint32_t value_ulong() { return value_ptr ? parse_int32(value_ptr) : 0UL; }
|
||||||
|
|
||||||
// Code value for use as time
|
// Code value for use as time
|
||||||
static inline millis_t value_millis() { return value_ulong(); }
|
static inline millis_t value_millis() { return value_ulong(); }
|
||||||
|
@ -445,7 +445,7 @@ void GCodeQueue::get_serial_commands() {
|
|||||||
if (process_line_done(serial.input_state, serial.line_buffer, serial.count))
|
if (process_line_done(serial.input_state, serial.line_buffer, serial.count))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
char* command = serial.line_buffer;
|
char *command = serial.line_buffer;
|
||||||
|
|
||||||
while (*command == ' ') command++; // Skip leading spaces
|
while (*command == ' ') command++; // Skip leading spaces
|
||||||
char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line
|
char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line
|
||||||
@ -459,7 +459,7 @@ void GCodeQueue::get_serial_commands() {
|
|||||||
if (n2pos) npos = n2pos;
|
if (n2pos) npos = n2pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
const long gcode_N = strtol(npos + 1, nullptr, 10);
|
const long gcode_N = parse_int32(npos + 1);
|
||||||
|
|
||||||
if (gcode_N != serial.last_N + 1 && !M110) {
|
if (gcode_N != serial.last_N + 1 && !M110) {
|
||||||
// In case of error on a serial port, don't prevent other serial port from making progress
|
// In case of error on a serial port, don't prevent other serial port from making progress
|
||||||
@ -471,7 +471,7 @@ void GCodeQueue::get_serial_commands() {
|
|||||||
if (apos) {
|
if (apos) {
|
||||||
uint8_t checksum = 0, count = uint8_t(apos - command);
|
uint8_t checksum = 0, count = uint8_t(apos - command);
|
||||||
while (count) checksum ^= command[--count];
|
while (count) checksum ^= command[--count];
|
||||||
if (strtol(apos + 1, nullptr, 10) != checksum) {
|
if (parse_int32(apos + 1) != checksum) {
|
||||||
// In case of error on a serial port, don't prevent other serial port from making progress
|
// In case of error on a serial port, don't prevent other serial port from making progress
|
||||||
gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), p);
|
gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), p);
|
||||||
break;
|
break;
|
||||||
@ -500,7 +500,7 @@ void GCodeQueue::get_serial_commands() {
|
|||||||
if (IsStopped()) {
|
if (IsStopped()) {
|
||||||
char* gpos = strchr(command, 'G');
|
char* gpos = strchr(command, 'G');
|
||||||
if (gpos) {
|
if (gpos) {
|
||||||
switch (strtol(gpos + 1, nullptr, 10)) {
|
switch (parse_int32(gpos + 1)) {
|
||||||
case 0: case 1:
|
case 0: case 1:
|
||||||
#if ENABLED(ARC_SUPPORT)
|
#if ENABLED(ARC_SUPPORT)
|
||||||
case 2: case 3:
|
case 2: case 3:
|
||||||
|
@ -1799,7 +1799,7 @@ void get_wifi_commands() {
|
|||||||
if (IsStopped()) {
|
if (IsStopped()) {
|
||||||
char* gpos = strchr(command, 'G');
|
char* gpos = strchr(command, 'G');
|
||||||
if (gpos) {
|
if (gpos) {
|
||||||
switch (strtol(gpos + 1, nullptr, 10)) {
|
switch (parse_int32(gpos + 1)) {
|
||||||
case 0 ... 1:
|
case 0 ... 1:
|
||||||
#if ENABLED(ARC_SUPPORT)
|
#if ENABLED(ARC_SUPPORT)
|
||||||
case 2 ... 3:
|
case 2 ... 3:
|
||||||
|
Loading…
Reference in New Issue
Block a user