Skip to content

Commit

Permalink
♻️ Simple string class
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkyhead committed Jun 28, 2022
1 parent 93ffd57 commit 9752310
Show file tree
Hide file tree
Showing 26 changed files with 441 additions and 176 deletions.
2 changes: 1 addition & 1 deletion Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,7 @@ void setup() {
#if ENABLED(MARLIN_DEV_MODE)
auto log_current_ms = [&](PGM_P const msg) {
SERIAL_ECHO_START();
SERIAL_CHAR('['); SERIAL_ECHO(millis()); SERIAL_ECHOPGM("] ");
TS('[', millis(), F("] ")).echo();
SERIAL_ECHOLNPGM_P(msg);
};
#define SETUP_LOG(M) log_current_ms(PSTR(M))
Expand Down
219 changes: 219 additions & 0 deletions Marlin/src/core/mstring.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once

/**
* Lightweight Arduino string class providing operators for all common tasks
* and conversion from F() and PSTR() strings into SRAM strings, that reside
* on the stack or persistently.
*
* Usage:
* Create a string object with the usual construction methods.
*/

#include "types.h"
#include "utility.h" // AXIS_CHAR
#include "../lcd/fontutils.h"

#ifndef DEFAULT_MSTRING_SIZE
#define DEFAULT_MSTRING_SIZE 20
#endif

// Light declarations for serial.h
template <typename T> void SERIAL_ECHO(T x);
template <typename T> void SERIAL_ECHOLN(T x);

//#define DJB2_HASH // 32-bit hash with Djb2 algorithm

#define START_OF_UTF8_CHAR(C) (((C) & 0xC0u) != 0x80U)

typedef struct { float value; int prec; } prec_float_t;

/**
* A simple string template class to compose temporary strings on the stack
*/
template <int SIZE=DEFAULT_MSTRING_SIZE>
class MString {
public:
char str[SIZE+1];
MString() { str[0] = '\0'; str[SIZE] = '\0'; }

template<typename T>
MString(const T v) { set(v); }

static_assert(SIZE > 0, "Bad SIZE for MString <= 0");

// Chainable String Setters
MString& set() { str[0] = '\0'; return *this; }
MString& set(const char *s) { strncpy(str, s, SIZE); return *this; }
MString& set_P(PGM_P const s) { strncpy_P(str, s, SIZE); return *this; }
MString& set(FSTR_P const f) { set_P(FTOP(f)); return *this; }
MString& set(const MString &s) { strncpy(str, s.str, SIZE); return *this; }
MString& set(const bool &b) { set(b ? F("true") : F("false")); return *this; }
MString& set(const char c) { str[0] = c; str[1] = '\0'; return *this; }
MString& set(const short &i) { snprintf(str, SIZE, "%d", i); return *this; }
MString& set(const int &i) { snprintf(str, SIZE, "%d", i); return *this; }
MString& set(const long &l) { snprintf(str, SIZE, "%ld", l); return *this; }
MString& set(const unsigned char &i) { snprintf(str, SIZE, "%u", i); return *this; }
MString& set(const unsigned short &i) { snprintf(str, SIZE, "%u", i); return *this; }
MString& set(const unsigned int &i) { snprintf(str, SIZE, "%u", i); return *this; }
MString& set(const unsigned long &l) { snprintf(str, SIZE, "%lu", l); return *this; }
MString& set(const float &f) { set(prec_float_t({ f, SERIAL_FLOAT_PRECISION })); return *this; }
MString& set(const prec_float_t &pf) { char f1[12]; set(dtostrf(pf.value, 0, pf.prec, f1)); return *this; }
MString& set(const serial_char_t &v) { set(char(v.c)); return *this; }
MString& set(const xyz_pos_t &v) { set(); append(v); return *this; }
MString& set(const xyze_pos_t &v) { set(); append(v); return *this; }

// Chainable String appenders
MString& append() { return *this; } // for macros that might emit no output
MString& append(const char *s) { strncat(str, s, SIZE); return *this; }
MString& append(const MString &s) { append(s.str); return *this; }
MString& append_P(PGM_P const s) { strncat_P(str, s, SIZE); return *this; }
MString& append(FSTR_P const f) { append_P(FTOP(f)); return *this; }
MString& append(const bool &b) { append(b ? F("true") : F("false")); return *this; }
MString& append(const char c) { const int last = strlen(str); if (last < SIZE) { str[last] = c; str[last+1] = '\0'; }
return *this; }
MString& append(const short &i) { char buf[32]; sprintf(buf, "%d", i); append(buf); return *this; }
MString& append(const int &i) { char buf[32]; sprintf(buf, "%d", i); append(buf); return *this; }
MString& append(const long &l) { char buf[32]; sprintf(buf, "%ld", l); append(buf); return *this; }
MString& append(const unsigned char &i) { char buf[32]; sprintf(buf, "%u", i); append(buf); return *this; }
MString& append(const unsigned short &i) { char buf[32]; sprintf(buf, "%u", i); append(buf); return *this; }
MString& append(const unsigned int &i) { char buf[32]; sprintf(buf, "%u", i); append(buf); return *this; }
MString& append(const unsigned long &l) { char buf[32]; sprintf(buf, "%lu", l); append(buf); return *this; }
MString& append(const float &f) { append(prec_float_t({ f, SERIAL_FLOAT_PRECISION })); return *this; }
MString& append(const prec_float_t &pf) { char f1[12]; append(dtostrf(pf.value, 0, pf.prec, f1)); return *this; }
MString& append(const serial_char_t &v) { append(char(v.c)); return *this; }
MString& append(const xyz_pos_t &v) { LOOP_NUM_AXES(i) append(AXIS_CHAR(i), v[i], ' '); return *this; }
MString& append(const xyze_pos_t &v) { LOOP_LOGICAL_AXES(i) append(AXIS_CHAR(i), v[i], ' '); return *this; }

MString& eol() { append('\n'); }

// Instantiate with a list of things
template <typename T, typename... Args>
MString(T arg1, Args... more) { set(arg1); append(more...); }

// Take a list of any number of arguments and append them to the string
template<typename T, typename... Args>
MString& append(T arg1, Args... more) { return append(arg1).append(more...); }

// Take a list of any number of arguments and set them in the string
template<typename T, typename... Args>
MString& set(T arg1, Args... more) { return set(arg1).append(more...); }

MString& operator=(const char *s) { set(s); return *this; }
MString& operator=(FSTR_P const f) { set(f); return *this; }
MString& operator=(const MString &s) { set(s.str); return *this; }
MString& operator=(const bool &b) { set(b); return *this; }
MString& operator=(const char &c) { set(c); return *this; }
MString& operator=(const short &i) { set(i); return *this; }
MString& operator=(const int &i) { set(i); return *this; }
MString& operator=(const long &l) { set(l); return *this; }
MString& operator=(const unsigned char &i) { set(i); return *this; }
MString& operator=(const unsigned short &i) { set(i); return *this; }
MString& operator=(const unsigned int &i) { set(i); return *this; }
MString& operator=(const unsigned long &l) { set(l); return *this; }
MString& operator=(const float &f) { set(f); return *this; }
MString& operator=(const prec_float_t &p) { set(p); return *this; }
MString& operator=(const serial_char_t &c) { set(c); return *this; }
MString& operator=(const xyz_pos_t &v) { set(v); return *this; }
MString& operator=(const xyze_pos_t &v) { set(v); return *this; }

MString& operator+=(const char *s) { append(s); return *this; }
MString& operator+=(FSTR_P const f) { append(f); return *this; }
MString& operator+=(const MString &s) { append(s); return *this; }
MString& operator+=(const bool &b) { append(b); return *this; }
MString& operator+=(const char &c) { append(c); return *this; }
MString& operator+=(const short &i) { append(i); return *this; }
MString& operator+=(const int &i) { append(i); return *this; }
MString& operator+=(const long &l) { append(l); return *this; }
MString& operator+=(const unsigned char &i) { append(i); return *this; }
MString& operator+=(const unsigned short &i) { append(i); return *this; }
MString& operator+=(const unsigned int &i) { append(i); return *this; }
MString& operator+=(const unsigned long &l) { append(l); return *this; }
MString& operator+=(const float &f) { append(f); return *this; }
MString& operator+=(const prec_float_t &p) { append(p); return *this; }
MString& operator+=(const serial_char_t &c) { append(c); return *this; }
MString& operator+=(const xyz_pos_t &v) { append(v); return *this; }
MString& operator+=(const xyze_pos_t &v) { append(v); return *this; }

MString operator+(const char *s) { return MString(str, s); }
MString operator+(FSTR_P const f) { return MString(str, f); }
MString operator+(const MString &s) { return MString(str, s); }
MString operator+(const bool &b) { return MString(str, b); }
MString operator+(const char c) { return MString(str, c); }
MString operator+(const short &i) { return MString(str, i); }
MString operator+(const int &i) { return MString(str, i); }
MString operator+(const long &l) { return MString(str, l); }
MString operator+(const unsigned char &i) { return MString(str, i); }
MString operator+(const unsigned short &i) { return MString(str, i); }
MString operator+(const unsigned int &i) { return MString(str, i); }
MString operator+(const unsigned long &l) { return MString(str, l); }
MString operator+(const float &f) { return MString(str, f); }
MString operator+(const prec_float_t &p) { return MString(str, p); }
MString operator+(const serial_char_t &c) { return MString(str, c); }
MString operator+(const xyz_pos_t &v) { return MString(str, v); }
MString operator+(const xyze_pos_t &v) { return MString(str, v); }

#ifndef __AVR__
MString(const double d) { set(d); }
MString& set(const double &d) { char d1[12]; dtostrf(d, 0, SERIAL_FLOAT_PRECISION, d1); return set(d1); }
MString& append(const double &d) { char d1[12]; dtostrf(d, 0, SERIAL_FLOAT_PRECISION, d1); return append(d1); }
MString& operator=(const double &d) { set(d); return *this; }
MString& operator+=(const double &d) { append(d); return *this; }
MString operator+(const double &d) { return MString(str, d); }
#endif

char operator[](const int i) const { return str[i]; }

char* buffer() { return str; }
size_t length() const { return strlen(str); }
int glyphs() { return utf8_strlen(str); }
bool empty() { return !str[0]; }

// Quick hash to detect change (e.g., to avoid expensive drawing)
typedef IF<ENABLED(DJB2_HASH), uint32_t, uint16_t>::type hash_t;
hash_t hash() const {
#if ENABLED(DJB2_HASH)
hash_t hval = 5381;
char c;
while ((c = *str++)) hval += (hval << 5) + c; // = hval * 33 + c
#else
const size_t len = length();
hash_t hval = hash_t(len);
for (size_t i = 0; i < len; i++) hval = ((hval << 1) | (hval << 15)) ^ str[i]; // ROL, XOR
#endif
return hval;
}

void copyto(char * const dst) const { strcpy(dst, str); }
void copyto(char * const dst, const size_t len) const { strncpy(dst, str, len); }

MString& clear() { return set(); }
MString& concat(const int &i) { if (i <= SIZE) str[i] = '\0'; return *this; }
MString& echo() { SERIAL_ECHO(str); return *this; }
MString& echoln() { SERIAL_ECHOLN(str); return *this; }
};

#ifndef TS_SIZE
#define TS_SIZE 20
#endif
#define TS(V...) MString<TS_SIZE>(V)
7 changes: 3 additions & 4 deletions Marlin/src/core/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ template <typename T>
void SERIAL_ECHO(T x) { SERIAL_IMPL.print(x); }

// Wrapper for ECHO commands to interpret a char
typedef struct SerialChar { char c; SerialChar(char n) : c(n) { } } serial_char_t;
inline void SERIAL_ECHO(serial_char_t x) { SERIAL_IMPL.write(x.c); }
#define AS_CHAR(C) serial_char_t(C)
#define AS_DIGIT(C) AS_CHAR('0' + (C))
Expand All @@ -169,11 +168,11 @@ inline void SERIAL_FLUSH() { SERIAL_IMPL.flush(); }
inline void SERIAL_FLUSHTX() { SERIAL_IMPL.flushTX(); }

// Serial echo and error prefixes
#define SERIAL_ECHO_START() serial_echo_start()
#define SERIAL_ERROR_START() serial_error_start()
#define SERIAL_ECHO_START() serial_echo_start()
#define SERIAL_ERROR_START() serial_error_start()

// Serial end-of-line
#define SERIAL_EOL() SERIAL_CHAR('\n')
#define SERIAL_EOL() SERIAL_CHAR('\n')

// Print a single PROGMEM, PGM_P, or PSTR() string.
void serial_print_P(PGM_P str);
Expand Down
3 changes: 3 additions & 0 deletions Marlin/src/core/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ typedef const_float_t const_celsius_float_t;
#define MMM_TO_MMS(MM_M) feedRate_t(static_cast<float>(MM_M) / 60.0f)
#define MMS_TO_MMM(MM_S) (static_cast<float>(MM_S) * 60.0f)

// Packaged character for AS_CHAR macro and other usage
typedef struct SerialChar { char c; SerialChar(char n) : c(n) { } } serial_char_t;

//
// Coordinates structures for XY, XYZ, XYZE...
//
Expand Down
4 changes: 4 additions & 0 deletions Marlin/src/core/utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
#include "../MarlinCore.h"
#include "../module/temperature.h"

#if ENABLED(MARLIN_DEV_MODE)
MarlinError marlin_error_number; // Error Number - Marlin can beep X times periodically, display, and emit...
#endif

void safe_delay(millis_t ms) {
while (ms > 50) {
ms -= 50;
Expand Down
19 changes: 19 additions & 0 deletions Marlin/src/core/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,22 @@ const xyze_char_t axis_codes LOGICAL_AXIS_ARRAY('E', 'X', 'Y', 'Z', AXIS4_NAME,
#define AXIS_CHAR(A) axis_codes[A]
#define IAXIS_CHAR(A) iaxis_codes[A]
#endif

#if ENABLED(MARLIN_DEV_MODE)
enum MarlinError : uint8_t {
ERR_NONE,
ERR_STRING_RANGE, // A string buffer was too small to set the whole blob
ERR_ASSERTION, // An assertion was triggered
ERR_MALFUNCTION,
ERR_MEMORY_LEAK,
ERR_COMMS_SERIAL,
ERR_COMMS_SPI,
ERR_PLANNER_STARVED,
ERR_TMC_SHUTDOWN,
ERR_PROCEDURE_FAILED,
ERR_TOO_WACK,
ERR_PLAID_IN_SUMMER
};
extern MarlinError marlin_error_number; // Error Number - Marlin can beep, display, and emit...
inline void error(const MarlinError err) { marlin_error_number = err; }
#endif
52 changes: 25 additions & 27 deletions Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1606,12 +1606,11 @@ void unified_bed_leveling::smart_fill_mesh() {
vector_3 normal = vector_3(lsf_results.A, lsf_results.B, 1).get_normal();

if (param.V_verbosity > 2) {
SERIAL_ECHOPAIR_F("bed plane normal = [", normal.x, 7);
SERIAL_CHAR(',');
SERIAL_ECHO_F(normal.y, 7);
SERIAL_CHAR(',');
SERIAL_ECHO_F(normal.z, 7);
SERIAL_ECHOLNPGM("]");
MString<100>(F("bed plane normal = ["),
prec_float_t({normal.x, 7}), ',',
prec_float_t({normal.y, 7}), ',',
prec_float_t({normal.z, 7}), ']'
).echoln();
}

matrix_3x3 rotation = matrix_3x3::create_look_at(vector_3(lsf_results.A, lsf_results.B, 1));
Expand All @@ -1620,24 +1619,22 @@ void unified_bed_leveling::smart_fill_mesh() {
float mx = get_mesh_x(i), my = get_mesh_y(j), mz = z_values[i][j];

if (DEBUGGING(LEVELING)) {
DEBUG_ECHOPAIR_F("before rotation = [", mx, 7);
DEBUG_CHAR(',');
DEBUG_ECHO_F(my, 7);
DEBUG_CHAR(',');
DEBUG_ECHO_F(mz, 7);
DEBUG_ECHOPGM("] ---> ");
MString<100>(F("before rotation = ["),
prec_float_t({mx, 7}), ',',
prec_float_t({my, 7}), ',',
prec_float_t({mz, 7}), F("] ---> ")
).echo();
DEBUG_DELAY(20);
}

rotation.apply_rotation_xyz(mx, my, mz);

if (DEBUGGING(LEVELING)) {
DEBUG_ECHOPAIR_F("after rotation = [", mx, 7);
DEBUG_CHAR(',');
DEBUG_ECHO_F(my, 7);
DEBUG_CHAR(',');
DEBUG_ECHO_F(mz, 7);
DEBUG_ECHOLNPGM("]");
MString<100>(F("after rotation = ["),
prec_float_t({mx, 7}), ',',
prec_float_t({my, 7}), ',',
prec_float_t({mz, 7}), ']'
).echoln();
DEBUG_DELAY(20);
}

Expand All @@ -1647,17 +1644,18 @@ void unified_bed_leveling::smart_fill_mesh() {

if (DEBUGGING(LEVELING)) {
rotation.debug(F("rotation matrix:\n"));
DEBUG_ECHOPAIR_F("LSF Results A=", lsf_results.A, 7);
DEBUG_ECHOPAIR_F(" B=", lsf_results.B, 7);
DEBUG_ECHOLNPAIR_F(" D=", lsf_results.D, 7);
MString<100>(
F("LSF Results A="), prec_float_t({lsf_results.A, 7}),
F(" B="), prec_float_t({lsf_results.B, 7}),
F(" D="), prec_float_t({lsf_results.D, 7})
).echoln();
DEBUG_DELAY(55);

DEBUG_ECHOPAIR_F("bed plane normal = [", normal.x, 7);
DEBUG_CHAR(',');
DEBUG_ECHO_F(normal.y, 7);
DEBUG_CHAR(',');
DEBUG_ECHO_F(normal.z, 7);
DEBUG_ECHOLNPGM("]");
MString<100>(F("bed plane normal = ["),
prec_float_t({normal.x, 7}), ',',
prec_float_t({normal.y, 7}), ',',
prec_float_t({normal.z, 7}), ']'
).echoln();
DEBUG_EOL();

/**
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/feature/cancel_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void CancelObject::set_active_object(const int8_t obj) {

#if BOTH(HAS_STATUS_MESSAGE, CANCEL_OBJECTS_REPORTING)
if (active_object >= 0)
ui.status_printf(0, F(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object));
ui.set_status(TS(GET_TEXT_F(MSG_PRINTING_OBJECT), int(active_object)).str);
else
ui.reset_status();
#endif
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/feature/stepper_driver_safety.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void stepper_driver_backward_error(FSTR_P const fstr) {
SERIAL_ERROR_START();
SERIAL_ECHOF(fstr);
SERIAL_ECHOLNPGM(" driver is backward!");
ui.status_printf(2, F(S_FMT S_FMT), FTOP(fstr), GET_TEXT(MSG_DRIVER_BACKWARD));
ui.set_status(TS(fstr, GET_TEXT_F(MSG_DRIVER_BACKWARD)).str, 2);
}

void stepper_driver_backward_check() {
Expand Down
Loading

0 comments on commit 9752310

Please sign in to comment.