* `Sendmail` upgraded to ESP-Mail-Client v3.4.9 from v1.2.0, using BearSSL instead of MbedTLS * Fix compilation on ESP8266 * Fix compilation * fix compilation
2075 lines
50 KiB
C++
2075 lines
50 KiB
C++
|
|
/**
|
|
* Mobizt's SRAM/PSRAM supported String, version 1.2.9
|
|
*
|
|
* Created December 3, 2022
|
|
*
|
|
* Changes Log
|
|
*
|
|
* v1.2.9
|
|
* - substring optimization
|
|
*
|
|
* v1.2.8
|
|
* - Add support StringSumHelper class in Arduino
|
|
*
|
|
* v1.2.7
|
|
* - Fix string sub type checking issue
|
|
*
|
|
* v1.2.6
|
|
* - Update trim() function
|
|
*
|
|
* v1.2.5
|
|
* - Fixed double string issue and add support long double
|
|
*
|
|
* v1.2.4
|
|
* - Check PSRAM availability before allocating the memory
|
|
*
|
|
* v1.2.3
|
|
* - Fixed flash string F and PSTR handle
|
|
*
|
|
* v1.2.2
|
|
* - Add supports more MCUs.
|
|
*
|
|
* v1.2.1
|
|
* - Add flash string manipulation functions.
|
|
*
|
|
* v1.2.0
|
|
* - Add supports bool, integer and float manipulation.
|
|
*
|
|
* v1.1.2
|
|
* - Fix substring with zero length returns the original string issue.
|
|
*
|
|
* v1.1.1
|
|
* - Fix possible ESP8266 code exit without resetting the external heap stack
|
|
*
|
|
* v1.1.0
|
|
* - Add support ESP8266 external virtual RAM (SRAM or PSRAM)
|
|
*
|
|
* v1.0.1
|
|
* - Add trim function
|
|
* - Add version enum
|
|
*
|
|
* v1.0.0
|
|
* - Initial release
|
|
*
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2023 K. Suwatchai (Mobizt)
|
|
*
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person returning a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifndef MB_String_H
|
|
#define MB_String_H
|
|
|
|
#include <Arduino.h>
|
|
#if !defined(__AVR__)
|
|
#include <string>
|
|
#include <strings.h>
|
|
#include <algorithm>
|
|
#endif
|
|
|
|
#define MB_STRING_MAJOR 1
|
|
#define MB_STRING_MINOR 2
|
|
#define MB_STRING_PATCH 5
|
|
|
|
#if defined(ESP8266) && defined(MMU_EXTERNAL_HEAP) && defined(MB_STRING_USE_PSRAM)
|
|
#include <umm_malloc/umm_malloc.h>
|
|
#include <umm_malloc/umm_heap_select.h>
|
|
#define ESP8266_USE_EXTERNAL_HEAP
|
|
#endif
|
|
|
|
#if defined(ESP8266) || defined(ESP32)
|
|
#define MBSTRING_FLASH_MCR FPSTR
|
|
#elif defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT)
|
|
#define MBSTRING_FLASH_MCR PSTR
|
|
#else
|
|
#define MBSTRING_FLASH_MCR(s) (s)
|
|
#endif
|
|
|
|
class MB_String;
|
|
|
|
#define pgm2Str(p) (MB_String().appendP(p).c_str())
|
|
#define num2Str(v, p) (MB_String().appendNum(v, p).c_str())
|
|
|
|
#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32F4)
|
|
#define MB_IS_SAME std::is_same
|
|
#define MB_ENABLE_IF std::enable_if
|
|
#else
|
|
#define MB_IS_SAME is_same
|
|
#define MB_ENABLE_IF enable_if
|
|
#endif
|
|
|
|
namespace mb_string
|
|
{
|
|
enum mb_string_sub_type
|
|
{
|
|
mb_string_sub_type_none,
|
|
mb_string_sub_type_bool,
|
|
mb_string_sub_type_float,
|
|
mb_string_sub_type_double,
|
|
mb_string_sub_type_int8,
|
|
mb_string_sub_type_uint8,
|
|
mb_string_sub_type_int16,
|
|
mb_string_sub_type_uint16,
|
|
mb_string_sub_type_int32,
|
|
mb_string_sub_type_uint32,
|
|
mb_string_sub_type_int64,
|
|
mb_string_sub_type_uint64,
|
|
mb_string_sub_type_cstring,
|
|
mb_string_sub_type_chars,
|
|
mb_string_sub_type_mb_string,
|
|
mb_string_sub_type_arduino_string,
|
|
mb_string_sub_type_std_string,
|
|
mb_string_sub_type_fptr,
|
|
mb_string_sub_type_string_sum_helper
|
|
|
|
};
|
|
|
|
typedef struct mb_string_ptr_t
|
|
{
|
|
|
|
public:
|
|
mb_string_ptr_t(uint32_t addr = 0, mb_string_sub_type type = mb_string_sub_type_cstring, int precision = -1, const StringSumHelper *s = nullptr)
|
|
{
|
|
_addr = addr;
|
|
_type = type;
|
|
_precision = precision;
|
|
_ssh = s;
|
|
}
|
|
int precision() { return _precision; }
|
|
mb_string_sub_type type() { return _type; }
|
|
uint32_t address() { return _addr; }
|
|
const StringSumHelper *stringsumhelper() { return _ssh; }
|
|
|
|
private:
|
|
mb_string_sub_type _type = mb_string_sub_type_none;
|
|
int _precision = -1;
|
|
uint32_t _addr = 0;
|
|
const StringSumHelper *_ssh = nullptr;
|
|
|
|
} MB_StringPtr;
|
|
|
|
template <bool, typename T = void>
|
|
struct enable_if
|
|
{
|
|
};
|
|
template <typename T>
|
|
struct enable_if<true, T>
|
|
{
|
|
typedef T type;
|
|
};
|
|
template <typename T, typename U>
|
|
struct is_same
|
|
{
|
|
static bool const value = false;
|
|
};
|
|
template <typename T>
|
|
struct is_same<T, T>
|
|
{
|
|
static bool const value = true;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_int8
|
|
{
|
|
static bool const value = MB_IS_SAME<T, int8_t>::value || MB_IS_SAME<T, signed char>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_uint8
|
|
{
|
|
static bool const value = MB_IS_SAME<T, uint8_t>::value || MB_IS_SAME<T, unsigned char>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_int16
|
|
{
|
|
static bool const value = MB_IS_SAME<T, int16_t>::value || MB_IS_SAME<T, signed short>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_uint16
|
|
{
|
|
static bool const value = MB_IS_SAME<T, uint16_t>::value || MB_IS_SAME<T, unsigned short>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_int32
|
|
{
|
|
static bool const value = MB_IS_SAME<T, signed int>::value || MB_IS_SAME<T, int>::value ||
|
|
MB_IS_SAME<T, int32_t>::value || MB_IS_SAME<T, long>::value ||
|
|
MB_IS_SAME<T, signed long>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_uint32
|
|
{
|
|
static bool const value = MB_IS_SAME<T, unsigned int>::value || MB_IS_SAME<T, uint32_t>::value ||
|
|
MB_IS_SAME<T, unsigned long>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_int64
|
|
{
|
|
static bool const value = MB_IS_SAME<T, int64_t>::value || MB_IS_SAME<T, signed long long>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_uint64
|
|
{
|
|
static bool const value = MB_IS_SAME<T, uint64_t>::value || MB_IS_SAME<T, unsigned long long>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_neg_int
|
|
{
|
|
static bool const value = is_num_int8<T>::value || is_num_int16<T>::value ||
|
|
is_num_int32<T>::value || is_num_int64<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_pos_int
|
|
{
|
|
static bool const value = is_num_uint8<T>::value || is_num_uint16<T>::value ||
|
|
is_num_uint32<T>::value || is_num_uint64<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_int
|
|
{
|
|
static bool const value = is_num_pos_int<T>::value || is_num_neg_int<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_num_float
|
|
{
|
|
static bool const value = MB_IS_SAME<T, float>::value || MB_IS_SAME<T, double>::value || MB_IS_SAME<T, long double>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_bool
|
|
{
|
|
static bool const value = MB_IS_SAME<T, bool>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct cs_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, char *>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct ccs_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, const char *>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct as_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, String>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct cas_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, const String>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct ss_t
|
|
{
|
|
#if !defined(__AVR__)
|
|
static bool const value = MB_IS_SAME<T, std::string>::value;
|
|
#else
|
|
static bool const value = false;
|
|
#endif
|
|
};
|
|
|
|
template <typename T>
|
|
struct css_t
|
|
{
|
|
#if !defined(__AVR__)
|
|
static bool const value = MB_IS_SAME<T, const std::string>::value;
|
|
#else
|
|
static bool const value = false;
|
|
#endif
|
|
};
|
|
|
|
template <typename T>
|
|
struct ssh_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, StringSumHelper>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct fs_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, const __FlashStringHelper *>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct mbs_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, MB_String>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct cmbs_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, const MB_String>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct pgm_t
|
|
{
|
|
static bool const value = MB_IS_SAME<T, PGM_P>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_const_chars
|
|
{
|
|
static bool const value = cs_t<T>::value || ccs_t<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_arduino_string
|
|
{
|
|
static bool const value = as_t<T>::value || cas_t<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_std_string
|
|
{
|
|
static bool const value = ss_t<T>::value || css_t<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_mb_string
|
|
{
|
|
static bool const value = mbs_t<T>::value || cmbs_t<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_arduino_string_sum_helper
|
|
{
|
|
static bool const value = ssh_t<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_arduino_flash_string_helper
|
|
{
|
|
static bool const value = fs_t<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_string
|
|
{
|
|
static bool const value = is_const_chars<T>::value || is_arduino_string<T>::value ||
|
|
is_arduino_string_sum_helper<T>::value || is_arduino_flash_string_helper<T>::value ||
|
|
is_std_string<T>::value || is_mb_string<T>::value;
|
|
};
|
|
|
|
template <typename T>
|
|
uint32_t toAddr(T &v) { return reinterpret_cast<uint32_t>(&v); }
|
|
|
|
#if defined(__AVR__)
|
|
template <typename T>
|
|
T addrTo(int address)
|
|
{
|
|
return reinterpret_cast<T>(address);
|
|
}
|
|
#else
|
|
template <typename T>
|
|
auto addrTo(int address) -> typename MB_ENABLE_IF<!MB_IS_SAME<T, nullptr_t>::value, T>::type
|
|
{
|
|
return reinterpret_cast<T>(address);
|
|
}
|
|
#endif
|
|
|
|
template <typename T>
|
|
auto getSubType(T val) -> typename MB_ENABLE_IF<is_num_int<T>::value || is_num_float<T>::value || MB_IS_SAME<T, bool>::value || is_const_chars<T>::value || is_arduino_flash_string_helper<T>::value || is_arduino_string<T>::value || is_std_string<T>::value || is_mb_string<T>::value || MB_IS_SAME<T, StringSumHelper>::value, mb_string_sub_type>::type
|
|
{
|
|
if (is_num_uint64<T>::value)
|
|
return mb_string_sub_type_uint64;
|
|
else if (is_num_int64<T>::value)
|
|
return mb_string_sub_type_int64;
|
|
else if (is_num_uint32<T>::value)
|
|
return mb_string_sub_type_uint32;
|
|
else if (is_num_int32<T>::value)
|
|
return mb_string_sub_type_int32;
|
|
else if (is_num_uint16<T>::value)
|
|
return mb_string_sub_type_uint16;
|
|
else if (is_num_int16<T>::value)
|
|
return mb_string_sub_type_int16;
|
|
else if (is_num_uint8<T>::value)
|
|
return mb_string_sub_type_uint8;
|
|
else if (is_num_int8<T>::value)
|
|
return mb_string_sub_type_int8;
|
|
else if (MB_IS_SAME<T, bool>::value)
|
|
return mb_string_sub_type_bool;
|
|
else if (MB_IS_SAME<T, float>::value)
|
|
return mb_string_sub_type_float;
|
|
else if (MB_IS_SAME<T, double>::value)
|
|
return mb_string_sub_type_double;
|
|
else if (is_arduino_string<T>::value)
|
|
return mb_string_sub_type_arduino_string;
|
|
else if (is_std_string<T>::value)
|
|
return mb_string_sub_type_std_string;
|
|
else if (is_mb_string<T>::value)
|
|
return mb_string_sub_type_mb_string;
|
|
else if (is_arduino_flash_string_helper<T>::value)
|
|
return mb_string_sub_type_fptr;
|
|
else if (MB_IS_SAME<T, StringSumHelper>::value)
|
|
return mb_string_sub_type_string_sum_helper;
|
|
else if (ccs_t<T>::value)
|
|
return mb_string_sub_type_cstring;
|
|
else if (cs_t<T>::value)
|
|
return mb_string_sub_type_chars;
|
|
|
|
return mb_string_sub_type_int8;
|
|
}
|
|
|
|
template <typename T>
|
|
auto getSubType(T *val) -> typename MB_ENABLE_IF<is_num_int<T>::value || is_num_float<T>::value || MB_IS_SAME<T, bool>::value || is_const_chars<T>::value || is_arduino_flash_string_helper<T>::value || is_arduino_string<T>::value || is_std_string<T>::value || is_mb_string<T>::value || MB_IS_SAME<T, StringSumHelper>::value, mb_string_sub_type>::type
|
|
{
|
|
return getSubType(*val);
|
|
}
|
|
|
|
template <typename T>
|
|
auto toStringPtr(const T &val) -> typename MB_ENABLE_IF<is_std_string<T>::value || is_arduino_string<T>::value || is_mb_string<T>::value, MB_StringPtr>::type
|
|
{
|
|
return MB_StringPtr(reinterpret_cast<uint32_t>(&val), getSubType(val));
|
|
}
|
|
|
|
template <typename T>
|
|
auto toStringPtr(const T &val) -> typename MB_ENABLE_IF<MB_IS_SAME<T, StringSumHelper>::value, MB_StringPtr>::type
|
|
{
|
|
#if defined(ESP8266)
|
|
return MB_StringPtr(reinterpret_cast<uint32_t>(&val), getSubType(val), -1);
|
|
|
|
#else
|
|
return MB_StringPtr(reinterpret_cast<uint32_t>(&val), getSubType(val), -1, &val);
|
|
#endif
|
|
}
|
|
|
|
template <typename T>
|
|
auto toStringPtr(T val) -> typename MB_ENABLE_IF<is_const_chars<T>::value, MB_StringPtr>::type { return MB_StringPtr(reinterpret_cast<uint32_t>(val), getSubType(val)); }
|
|
|
|
template <typename T>
|
|
auto toStringPtr(T &val) -> typename MB_ENABLE_IF<is_arduino_flash_string_helper<T>::value, MB_StringPtr>::type { return MB_StringPtr(reinterpret_cast<uint32_t>(val), getSubType(val)); }
|
|
|
|
#if !defined(__AVR__)
|
|
template <typename T>
|
|
auto toStringPtr(T val) -> typename MB_ENABLE_IF<MB_IS_SAME<T, std::nullptr_t>::value, MB_StringPtr>::type
|
|
{
|
|
return MB_StringPtr();
|
|
}
|
|
#endif
|
|
|
|
template <typename T>
|
|
auto toStringPtr(T val) -> typename MB_ENABLE_IF<MB_IS_SAME<T, MB_StringPtr>::value, MB_StringPtr>::type
|
|
{
|
|
return val;
|
|
}
|
|
|
|
template <typename T>
|
|
auto toStringPtr(T &val, int precision = -1) -> typename MB_ENABLE_IF<is_num_int<T>::value || is_num_float<T>::value || MB_IS_SAME<T, bool>::value, MB_StringPtr>::type { return MB_StringPtr(reinterpret_cast<uint32_t>(&val), getSubType(val), precision); }
|
|
}
|
|
|
|
using namespace mb_string;
|
|
|
|
class MB_String
|
|
{
|
|
public:
|
|
MB_String()
|
|
{
|
|
#if defined(ESP8266_USE_EXTERNAL_HEAP)
|
|
// reserve default 1 byte external heap to refer to its pointer later
|
|
reset(1);
|
|
#endif
|
|
};
|
|
|
|
~MB_String()
|
|
{
|
|
allocate(0, false);
|
|
};
|
|
|
|
MB_String(const char *cstr)
|
|
{
|
|
if (cstr)
|
|
copy(cstr, strlen_P(cstr));
|
|
}
|
|
|
|
MB_String(const MB_String &value)
|
|
{
|
|
*this = value;
|
|
}
|
|
|
|
MB_String(const __FlashStringHelper *str)
|
|
{
|
|
*this = str;
|
|
}
|
|
|
|
#if !defined(ESP8266)
|
|
MB_String(StringSumHelper rval)
|
|
{
|
|
*this = rval;
|
|
}
|
|
#endif
|
|
|
|
MB_String(MB_StringPtr value)
|
|
{
|
|
*this = value;
|
|
}
|
|
|
|
MB_String(String value)
|
|
{
|
|
*this = value;
|
|
}
|
|
|
|
MB_String(bool value)
|
|
{
|
|
appendNum(value);
|
|
}
|
|
|
|
MB_String(unsigned char value, unsigned char base = 10)
|
|
{
|
|
int len = 1 + 8 * sizeof(unsigned char);
|
|
reserve(len);
|
|
|
|
if (bufLen > 0)
|
|
utoa(value, buf, base);
|
|
}
|
|
|
|
MB_String(int value, unsigned char base = 10)
|
|
{
|
|
int len = 2 + 8 * sizeof(int);
|
|
reserve(len);
|
|
|
|
if (bufLen > 0)
|
|
{
|
|
if (base == 10)
|
|
sprintf(buf, (const char *)MBSTRING_FLASH_MCR("%d"), value);
|
|
else
|
|
itoa(value, buf, base);
|
|
}
|
|
}
|
|
|
|
MB_String(unsigned int value, unsigned char base = 10)
|
|
{
|
|
int len = 1 + 8 * sizeof(unsigned int);
|
|
reserve(len);
|
|
|
|
if (bufLen > 0)
|
|
utoa(value, buf, base);
|
|
}
|
|
|
|
MB_String(long value, unsigned char base = 10)
|
|
{
|
|
int len = 2 + 8 * sizeof(long);
|
|
reserve(len);
|
|
|
|
if (bufLen > 0)
|
|
{
|
|
if (base == 10)
|
|
sprintf(buf, (const char *)MBSTRING_FLASH_MCR("%ld"), value);
|
|
else
|
|
ltoa(value, buf, base);
|
|
}
|
|
}
|
|
|
|
MB_String(unsigned long value, unsigned char base = 10)
|
|
{
|
|
int len = 1 + 8 * sizeof(unsigned long);
|
|
reserve(len);
|
|
|
|
if (bufLen > 0)
|
|
ultoa(value, buf, base);
|
|
}
|
|
|
|
MB_String(float value, unsigned char decimalPlaces = 2)
|
|
{
|
|
reserve(33);
|
|
if (bufLen > 0)
|
|
{
|
|
char *v = toFloatStr(value, 0, decimalPlaces);
|
|
if (v)
|
|
{
|
|
strcpy(buf, v);
|
|
delP(&v);
|
|
}
|
|
}
|
|
}
|
|
|
|
MB_String(double value, unsigned char decimalPlaces = 3)
|
|
{
|
|
reserve(33);
|
|
|
|
if (bufLen > 0)
|
|
{
|
|
char *v = toFloatStr(value, 1, decimalPlaces);
|
|
if (v)
|
|
{
|
|
strcpy(buf, v);
|
|
delP(&v);
|
|
}
|
|
}
|
|
}
|
|
|
|
MB_String(long double value, unsigned char decimalPlaces = 3)
|
|
{
|
|
reserve(65);
|
|
if (bufLen > 0)
|
|
{
|
|
char *v = toFloatStr(value, 2, decimalPlaces);
|
|
if (v)
|
|
{
|
|
strcpy(buf, v);
|
|
delP(&v);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(__AVR__)
|
|
MB_String &operator=(const std::string &rhs)
|
|
{
|
|
if (rhs.length() > 0)
|
|
copy(rhs.c_str(), rhs.length());
|
|
else
|
|
clear();
|
|
|
|
return *this;
|
|
}
|
|
#endif
|
|
MB_String &operator=(const String &rhs)
|
|
{
|
|
|
|
if (rhs.length() > 0)
|
|
copy(rhs.c_str(), rhs.length());
|
|
else
|
|
clear();
|
|
|
|
return *this;
|
|
}
|
|
|
|
MB_String &operator=(const __FlashStringHelper *str)
|
|
{
|
|
if (str)
|
|
appendF(str, true);
|
|
|
|
return *this;
|
|
}
|
|
|
|
#if !defined(ESP8266)
|
|
MB_String &operator=(StringSumHelper rval)
|
|
{
|
|
String temp = rval;
|
|
*this = temp;
|
|
return *this;
|
|
}
|
|
|
|
MB_String &operator+=(StringSumHelper rval)
|
|
{
|
|
String temp = rval;
|
|
*this += temp;
|
|
return *this;
|
|
}
|
|
#endif
|
|
|
|
MB_String &operator+=(const __FlashStringHelper *str)
|
|
{
|
|
if (str)
|
|
appendF(str);
|
|
|
|
return *this;
|
|
}
|
|
|
|
unsigned char operator==(const MB_String &rhs) const
|
|
{
|
|
return equals(rhs);
|
|
}
|
|
|
|
unsigned char operator==(const char *cstr) const
|
|
{
|
|
return equals(cstr);
|
|
}
|
|
|
|
unsigned char operator!=(const MB_String &rhs) const
|
|
{
|
|
return !equals(rhs);
|
|
}
|
|
|
|
unsigned char operator!=(const char *cstr) const
|
|
{
|
|
return !equals(cstr);
|
|
}
|
|
#if !defined(__AVR__)
|
|
MB_String &operator+=(const std::string &rhs)
|
|
{
|
|
concat(rhs.c_str());
|
|
return (*this);
|
|
}
|
|
#endif
|
|
MB_String &operator+=(const String &rhs)
|
|
{
|
|
concat(rhs.c_str());
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &operator=(const MB_String &rhs)
|
|
{
|
|
if (this == &rhs)
|
|
return *this;
|
|
|
|
if (rhs.length() > 0)
|
|
copy(rhs.buf, rhs.length());
|
|
else
|
|
clear();
|
|
|
|
return *this;
|
|
}
|
|
|
|
MB_String &operator+=(const MB_String &rhs)
|
|
{
|
|
concat(rhs);
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &operator+=(const char *cstr)
|
|
{
|
|
size_t len = strlen_P(cstr);
|
|
size_t slen = length();
|
|
|
|
if (_reserve(slen + len, false))
|
|
{
|
|
strcat_P(buf, (PGM_P)cstr);
|
|
*(buf + slen + len) = '\0';
|
|
}
|
|
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &operator+=(char cstr)
|
|
{
|
|
append(1, cstr);
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &operator+=(bool value)
|
|
{
|
|
appendNum(value);
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &operator=(MB_StringPtr ptr)
|
|
{
|
|
setPtr(ptr);
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &operator+=(MB_StringPtr ptr)
|
|
{
|
|
appendPtr(ptr);
|
|
return (*this);
|
|
}
|
|
|
|
template <typename T = int>
|
|
auto operator=(T value) -> typename MB_ENABLE_IF<is_num_int<T>::value || is_num_float<T>::value || is_bool<T>::value, MB_String &>::type
|
|
{
|
|
clear();
|
|
appendNum(value);
|
|
return (*this);
|
|
}
|
|
|
|
template <typename T = int>
|
|
auto operator+=(T value) -> typename MB_ENABLE_IF<is_num_int<T>::value || is_num_float<T>::value || is_bool<T>::value, MB_String &>::type
|
|
{
|
|
appendNum(value);
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &appendP(PGM_P pgms, bool clear = false)
|
|
{
|
|
if (clear)
|
|
this->clear();
|
|
|
|
char *t = pgmStr(pgms);
|
|
if (t)
|
|
{
|
|
*this += t;
|
|
delP(&t);
|
|
}
|
|
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &appendF(const __FlashStringHelper *pstr, bool clear = false)
|
|
{
|
|
if (clear)
|
|
this->clear();
|
|
|
|
int len = strlen_P((PGM_P)pstr);
|
|
if (len > 0)
|
|
{
|
|
unsigned int newlen = length() + len;
|
|
reserve(newlen);
|
|
|
|
if (bufLen > 0)
|
|
memcpy_P(buf + length(), (PGM_P)pstr, len + 1);
|
|
}
|
|
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &setPtr(MB_StringPtr src)
|
|
{
|
|
clear();
|
|
appendPtr(src);
|
|
return (*this);
|
|
};
|
|
|
|
MB_String &appendPtr(MB_StringPtr src)
|
|
{
|
|
if (src.type() == mb_string_sub_type_fptr)
|
|
appendF(addrTo<const __FlashStringHelper *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_cstring || src.type() == mb_string_sub_type_chars)
|
|
*this += addrTo<const char *>(src.address());
|
|
else if (src.type() == mb_string_sub_type_arduino_string)
|
|
*this += *addrTo<String *>(src.address());
|
|
else if (src.type() == mb_string_sub_type_string_sum_helper)
|
|
#if !defined(ESP8266)
|
|
*this += *src.stringsumhelper();
|
|
#else
|
|
*this += *addrTo<String *>(src.address());
|
|
#endif
|
|
#if !defined(__AVR__)
|
|
else if (src.type() == mb_string_sub_type_std_string)
|
|
*this += *addrTo<std::string *>(src.address());
|
|
#endif
|
|
else if (src.type() == mb_string_sub_type_mb_string)
|
|
*this += *addrTo<MB_String *>(src.address());
|
|
else if (src.type() == mb_string_sub_type_uint64)
|
|
appendNum(*addrTo<uint64_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_int64)
|
|
appendNum(*addrTo<int64_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_uint32)
|
|
appendNum(*addrTo<uint32_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_int32)
|
|
appendNum(*addrTo<int32_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_uint16)
|
|
appendNum(*addrTo<uint16_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_int16)
|
|
appendNum(*addrTo<int16_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_uint8)
|
|
appendNum(*addrTo<uint8_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_int8)
|
|
appendNum(*addrTo<int8_t *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_bool)
|
|
appendNum(*addrTo<bool *>(src.address()));
|
|
else if (src.type() == mb_string_sub_type_float)
|
|
appendNum(*addrTo<float *>(src.address()), src.precision());
|
|
else if (src.type() == mb_string_sub_type_double)
|
|
appendNum(*addrTo<double *>(src.address()), src.precision());
|
|
|
|
return (*this);
|
|
};
|
|
|
|
template <typename T = int>
|
|
auto appendNum(T value, int precision = 0) -> typename MB_ENABLE_IF<is_num_int<T>::value || is_bool<T>::value, MB_String &>::type
|
|
{
|
|
char *s = NULL;
|
|
|
|
if (is_bool<T>::value)
|
|
s = boolStr(value);
|
|
else if (is_num_neg_int<T>::value)
|
|
{
|
|
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT)
|
|
s = int32Str(value);
|
|
#else
|
|
s = int64Str(value);
|
|
#endif
|
|
}
|
|
else if (is_num_pos_int<T>::value)
|
|
{
|
|
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT)
|
|
s = uint32Str(value);
|
|
#else
|
|
s = uint64Str(value);
|
|
#endif
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
*this += s;
|
|
delP(&s);
|
|
}
|
|
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &appendNum(float value, int precision = 5)
|
|
{
|
|
if (precision < 0)
|
|
precision = 5;
|
|
|
|
char *s = toFloatStr(value, 0, precision);
|
|
if (s)
|
|
{
|
|
*this += s;
|
|
delP(&s);
|
|
}
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &appendNum(double value, int precision = 9)
|
|
{
|
|
if (precision < 0)
|
|
precision = 9;
|
|
|
|
char *s = toFloatStr(value, 1, precision);
|
|
if (s)
|
|
{
|
|
*this += s;
|
|
delP(&s);
|
|
}
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &appendNum(long double value, int precision = 9)
|
|
{
|
|
if (precision < 0)
|
|
precision = 9;
|
|
|
|
char *s = toFloatStr(value, 2, precision);
|
|
if (s)
|
|
{
|
|
*this += s;
|
|
delP(&s);
|
|
}
|
|
return (*this);
|
|
}
|
|
|
|
MB_String &operator=(const char *cstr)
|
|
{
|
|
if (cstr)
|
|
copy(cstr, strlen_P(cstr));
|
|
else
|
|
clear();
|
|
|
|
return *this;
|
|
}
|
|
|
|
MB_String &operator=(char c)
|
|
{
|
|
clear();
|
|
if (_reserve(1, false))
|
|
{
|
|
*(buf) = c;
|
|
*(buf + 1) = '\0';
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
void trim()
|
|
{
|
|
int p1 = 0, p2 = length() - 1;
|
|
while (p1 < (int)length())
|
|
{
|
|
if (buf[p1] > 32)
|
|
break;
|
|
p1++;
|
|
}
|
|
|
|
while (p2 >= 0)
|
|
{
|
|
if (buf[p2] > 32)
|
|
break;
|
|
p2--;
|
|
}
|
|
|
|
if (p1 == (int)length() && p2 < 0)
|
|
{
|
|
clear();
|
|
return;
|
|
}
|
|
|
|
if (p2 >= p1 && p2 >= 0 && p1 < (int)length())
|
|
{
|
|
memmove(buf, buf + p1, p2 - p1 + 1);
|
|
buf[p2 - p1 + 1] = '\0';
|
|
_reserve(p2 - p1 + 1, true);
|
|
}
|
|
}
|
|
|
|
void append(const char *cstr, size_t n)
|
|
{
|
|
if (!cstr)
|
|
return;
|
|
|
|
size_t slen = length();
|
|
|
|
if (n > strlen(cstr))
|
|
n = strlen(cstr);
|
|
|
|
if (_reserve(slen + n, false))
|
|
{
|
|
memmove(buf + slen, cstr, n);
|
|
*(buf + slen + n) = '\0';
|
|
}
|
|
}
|
|
|
|
void append(size_t n, char c)
|
|
{
|
|
size_t slen = length();
|
|
|
|
if (_reserve(slen + n, false))
|
|
{
|
|
for (size_t i = 0; i < n; i++)
|
|
*(buf + slen + i) = c;
|
|
*(buf + slen + n) = '\0';
|
|
}
|
|
}
|
|
|
|
void prepend(char c)
|
|
{
|
|
size_t slen = length();
|
|
size_t len = 1;
|
|
|
|
if (maxLength() < slen + len)
|
|
_reserve(slen + len, false);
|
|
|
|
memmove(buf + len, buf, slen);
|
|
buf[0] = c;
|
|
buf[len + slen] = '\0';
|
|
}
|
|
|
|
void prepend(const char *cstr)
|
|
{
|
|
size_t slen = length();
|
|
size_t len = strlen(cstr);
|
|
|
|
if (maxLength() < slen + len)
|
|
_reserve(slen + len, false);
|
|
|
|
memmove(buf + len, buf, slen);
|
|
memmove(buf, cstr, len);
|
|
buf[len + slen] = '\0';
|
|
}
|
|
|
|
const char *c_str() const
|
|
{
|
|
if (!buf)
|
|
return "";
|
|
return (const char *)buf;
|
|
}
|
|
|
|
char operator[](size_t index) const
|
|
{
|
|
if (index >= bufLen || !buf)
|
|
return 0;
|
|
return buf[index];
|
|
}
|
|
|
|
char &operator[](size_t index)
|
|
{
|
|
static char c;
|
|
if (index >= bufLen || !buf)
|
|
{
|
|
c = '\0';
|
|
return c;
|
|
}
|
|
return buf[index];
|
|
}
|
|
|
|
void swap(MB_String &rhs)
|
|
{
|
|
rhs.clear();
|
|
}
|
|
|
|
void shrink_to_fit()
|
|
{
|
|
size_t slen = length();
|
|
_reserve(slen, true);
|
|
}
|
|
|
|
void pop_back()
|
|
{
|
|
if (length() > 0)
|
|
{
|
|
size_t slen = length();
|
|
if (slen > 0)
|
|
buf[slen - 1] = '\0';
|
|
_reserve(slen, true);
|
|
}
|
|
}
|
|
|
|
size_t size() const
|
|
{
|
|
return length();
|
|
}
|
|
|
|
size_t bufferLength() const
|
|
{
|
|
return bufLen;
|
|
}
|
|
|
|
size_t find(const MB_String &s, size_t index = 0) const
|
|
{
|
|
if (!s.buf)
|
|
return -1;
|
|
return strpos(buf, s.buf, index);
|
|
}
|
|
|
|
size_t find(const char *s, size_t index = 0) const
|
|
{
|
|
return strpos(buf, s, index);
|
|
}
|
|
|
|
size_t find(char c, size_t index = 0) const
|
|
{
|
|
return strpos(buf, c, index);
|
|
}
|
|
|
|
size_t rfind(const char *s, size_t index = npos) const
|
|
{
|
|
return rstrpos(buf, s, index);
|
|
}
|
|
|
|
size_t rfind(char c, size_t index = npos) const
|
|
{
|
|
return rstrpos(buf, c, index);
|
|
}
|
|
|
|
void erase(size_t index = 0, size_t len = npos)
|
|
{
|
|
|
|
if (!buf || index >= length())
|
|
return;
|
|
|
|
if (index + len > length() || len == npos)
|
|
len = length() - index;
|
|
|
|
int rightLen = length() - index - len;
|
|
|
|
memmove(buf + index, buf + index + len, rightLen);
|
|
|
|
buf[index + rightLen] = '\0';
|
|
|
|
_reserve(length(), true);
|
|
}
|
|
|
|
size_t length() const
|
|
{
|
|
if (!buf)
|
|
return 0;
|
|
return strlen(buf);
|
|
}
|
|
|
|
MB_String substr(size_t offset, size_t len = npos) const
|
|
{
|
|
MB_String out;
|
|
substr(out, offset, len);
|
|
return out;
|
|
}
|
|
|
|
void substr(MB_String &out, size_t offset, size_t len = npos) const
|
|
{
|
|
if (len > 0 && length() > 0 && offset < length())
|
|
{
|
|
if (len > length() - offset)
|
|
len = length() - offset;
|
|
out.copy(buf + offset, len);
|
|
}
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
#if defined(ESP8266_USE_EXTERNAL_HEAP)
|
|
reset(1);
|
|
#else
|
|
allocate(0, false);
|
|
#endif
|
|
}
|
|
|
|
#if defined(ESP8266_USE_EXTERNAL_HEAP)
|
|
void reset(size_t len)
|
|
{
|
|
if (len == 0)
|
|
len = 4;
|
|
ESP.setExternalHeap();
|
|
if (buf)
|
|
buf = (char *)realloc(buf, len);
|
|
else
|
|
buf = (char *)malloc(len);
|
|
ESP.resetHeap();
|
|
|
|
if (buf)
|
|
{
|
|
bufLen = len;
|
|
memset(buf, 0, len);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void resize(size_t len)
|
|
{
|
|
if (_reserve(len, true))
|
|
buf[len] = '\0';
|
|
}
|
|
|
|
MB_String &replace(size_t pos, size_t len, const char *replace)
|
|
{
|
|
size_t repLen = strlen(replace);
|
|
|
|
if (length() > 0 && length() > pos && repLen > 0)
|
|
{
|
|
if (pos + len > length())
|
|
len = length() - pos;
|
|
|
|
if (repLen > len)
|
|
{
|
|
size_t rightLen = length() - pos - len;
|
|
|
|
if (maxLength() < length() + repLen - len)
|
|
_reserve(length() + repLen - len, false);
|
|
|
|
memmove(buf + pos + repLen, buf + pos + len, rightLen);
|
|
buf[pos + repLen + rightLen] = '\0';
|
|
}
|
|
|
|
memmove(buf + pos, replace, repLen);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
MB_String &replace(size_t pos, size_t len, const MB_String &replace)
|
|
{
|
|
return this->replace(pos, len, replace.c_str());
|
|
}
|
|
|
|
MB_String &insert(size_t pos, size_t n, char c)
|
|
{
|
|
size_t slen = length();
|
|
|
|
size_t rightLen = slen - pos;
|
|
|
|
if (maxLength() < slen + n)
|
|
_reserve(slen + n, false);
|
|
|
|
if (maxLength() >= slen + n)
|
|
{
|
|
memmove(buf + pos + n, buf + pos, rightLen);
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
*(buf + pos + i) = c;
|
|
|
|
buf[pos + n + rightLen] = '\0';
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
MB_String &insert(size_t pos, const char *cstr)
|
|
{
|
|
size_t insLen = strlen(cstr);
|
|
|
|
if (length() > 0 && length() > pos && insLen > 0)
|
|
{
|
|
|
|
size_t rightLen = length() - pos;
|
|
|
|
if (maxLength() < length() + insLen)
|
|
_reserve(length() + insLen, false);
|
|
|
|
memmove(buf + pos + insLen, buf + pos, rightLen);
|
|
buf[pos + insLen + rightLen] = '\0';
|
|
memmove(buf + pos, cstr, insLen);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
MB_String &insert(size_t pos, const MB_String &str)
|
|
{
|
|
return insert(pos, str.c_str());
|
|
}
|
|
|
|
MB_String &insert(size_t pos, char c)
|
|
{
|
|
char temp[2]{c, '\0'};
|
|
return insert(pos, temp);
|
|
}
|
|
|
|
size_t find_first_of(const char *cstr, size_t pos = 0) const
|
|
{
|
|
if (!cstr)
|
|
return -1;
|
|
|
|
return strpos(buf, cstr, pos);
|
|
}
|
|
|
|
size_t find_first_of(const MB_String &str, size_t pos = 0) const
|
|
{
|
|
if (length() == 0 || pos >= length())
|
|
return -1;
|
|
|
|
return find_first_of(str.buf, pos);
|
|
}
|
|
|
|
size_t find_first_not_of(const char *cstr, size_t pos = 0) const
|
|
{
|
|
if (length() == 0 || pos >= length())
|
|
return -1;
|
|
|
|
int size = strcspn(buf + pos, cstr);
|
|
if (size == 0)
|
|
{
|
|
while (size == 0 && pos < length())
|
|
{
|
|
size = strcspn(buf + pos, cstr);
|
|
pos++;
|
|
}
|
|
|
|
if (pos > 0)
|
|
pos--;
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
size_t find_first_not_of(const MB_String &str, size_t pos = 0) const
|
|
{
|
|
if (length() == 0 || pos >= length() || str.length() == 0)
|
|
return -1;
|
|
|
|
return find_first_not_of(str.buf, pos);
|
|
}
|
|
|
|
size_t find_last_of(const char *cstr, size_t pos = npos) const
|
|
{
|
|
if (!cstr)
|
|
return -1;
|
|
|
|
return rstrpos(buf, cstr, pos);
|
|
}
|
|
|
|
size_t find_last_of(const MB_String &str, size_t pos = npos) const
|
|
{
|
|
if (str.length() == 0)
|
|
return -1;
|
|
|
|
return find_last_of(str.buf, pos);
|
|
}
|
|
|
|
size_t find_last_not_of(const char *cstr, size_t pos = npos) const
|
|
{
|
|
if (length() == 0)
|
|
return -1;
|
|
|
|
if (pos >= length())
|
|
pos = length() - 1;
|
|
|
|
int p = length() - 1;
|
|
int size = strcspn(buf + p, cstr);
|
|
if (size == 0)
|
|
{
|
|
while (size == 0 && p > 0)
|
|
{
|
|
size = strcspn(buf + p, cstr);
|
|
p--;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
size_t find_last_not_of(const MB_String &str, size_t pos = npos) const
|
|
{
|
|
if (str.length() == 0)
|
|
return -1;
|
|
|
|
return find_last_not_of(str.buf, pos);
|
|
}
|
|
|
|
MB_String & replaceAll(const char *find, const char *replace)
|
|
{
|
|
if (length() == 0)
|
|
return *this;;
|
|
|
|
int i, cnt = 0;
|
|
int repLen = strlen(replace);
|
|
int findLen = strlen(find);
|
|
|
|
MB_String temp = buf;
|
|
char *s = temp.buf;
|
|
clear();
|
|
|
|
for (i = 0; s[i] != '\0'; i++)
|
|
{
|
|
if (strstr(&s[i], find) == &s[i])
|
|
{
|
|
cnt++;
|
|
i += findLen - 1;
|
|
}
|
|
}
|
|
|
|
if (_reserve(i + cnt * (repLen - findLen) + 1, false))
|
|
{
|
|
i = 0;
|
|
while (*s)
|
|
{
|
|
if (strstr(s, find) == s)
|
|
{
|
|
strcpy(&buf[i], replace);
|
|
i += repLen;
|
|
s += findLen;
|
|
}
|
|
else
|
|
buf[i++] = *s++;
|
|
}
|
|
|
|
buf[i] = '\0';
|
|
}
|
|
|
|
temp.clear();
|
|
|
|
return *this;
|
|
}
|
|
|
|
void replaceAll(const MB_String &find, const MB_String &replace)
|
|
{
|
|
replaceAll(find.c_str(), replace.c_str());
|
|
}
|
|
|
|
bool empty() const
|
|
{
|
|
return length() == 0;
|
|
}
|
|
|
|
void reserve(size_t len)
|
|
{
|
|
if (_reserve(len, false))
|
|
buf[len] = '\0';
|
|
}
|
|
|
|
static const size_t npos = -1;
|
|
|
|
private:
|
|
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT)
|
|
|
|
char *int32Str(signed long value)
|
|
{
|
|
char *t = (char *)newP(64);
|
|
sprintf(t, (const char *)MBSTRING_FLASH_MCR("%ld"), value);
|
|
return t;
|
|
}
|
|
|
|
char *uint32Str(unsigned long value)
|
|
{
|
|
char *t = (char *)newP(64);
|
|
sprintf(t, (const char *)MBSTRING_FLASH_MCR("%lu"), value);
|
|
return t;
|
|
}
|
|
|
|
#endif
|
|
|
|
char *int64Str(signed long long value)
|
|
{
|
|
char *t = (char *)newP(64);
|
|
sprintf(t, (const char *)MBSTRING_FLASH_MCR("%lld"), value);
|
|
return t;
|
|
}
|
|
|
|
char *uint64Str(unsigned long long value)
|
|
{
|
|
char *t = (char *)newP(64);
|
|
sprintf(t, (const char *)MBSTRING_FLASH_MCR("%llu"), value);
|
|
return t;
|
|
}
|
|
|
|
char *boolStr(bool value)
|
|
{
|
|
char *t = (char *)newP(8);
|
|
value ? strcpy(t, (const char *)MBSTRING_FLASH_MCR("true")) : strcpy(t, (const char *)MBSTRING_FLASH_MCR("false"));
|
|
return t;
|
|
}
|
|
|
|
char *toFloatStr(long double value, int type, int precision)
|
|
{
|
|
int width = type == 0 ? 32 : 64;
|
|
|
|
char *t = (char *)newP(width);
|
|
|
|
if (t)
|
|
{
|
|
MB_String fmt = MBSTRING_FLASH_MCR("%.");
|
|
fmt += precision;
|
|
if (type == 2)
|
|
fmt += MBSTRING_FLASH_MCR("L");
|
|
fmt += MBSTRING_FLASH_MCR("f");
|
|
sprintf(t, fmt.c_str(), value);
|
|
trim(t);
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
char *nullStr()
|
|
{
|
|
char *t = (char *)newP(6);
|
|
strcpy(t, (const char *)MBSTRING_FLASH_MCR("null"));
|
|
return t;
|
|
}
|
|
|
|
char *pgmStr(PGM_P p)
|
|
{
|
|
char *t = (char *)newP(strlen_P(p));
|
|
strcpy_P(t, p);
|
|
return t;
|
|
}
|
|
|
|
void trim(char *s)
|
|
{
|
|
if (!s)
|
|
return;
|
|
size_t i = strlen(s) - 1;
|
|
while (s[i] == '0' && i > 0)
|
|
{
|
|
if (s[i - 1] == '.')
|
|
{
|
|
i--;
|
|
break;
|
|
}
|
|
if (s[i - 1] != '0')
|
|
break;
|
|
i--;
|
|
}
|
|
if (i < strlen(s) - 1)
|
|
s[i] = '\0';
|
|
}
|
|
|
|
void *newP(size_t len)
|
|
{
|
|
void *p;
|
|
size_t newLen = getReservedLen(len);
|
|
#if defined(BOARD_HAS_PSRAM) && defined(MB_STRING_USE_PSRAM)
|
|
if (ESP.getPsramSize() > 0)
|
|
p = (void *)ps_malloc(newLen);
|
|
else
|
|
p = (void *)malloc(newLen);
|
|
if (!p)
|
|
return NULL;
|
|
|
|
#else
|
|
|
|
#if defined(ESP8266_USE_EXTERNAL_HEAP)
|
|
ESP.setExternalHeap();
|
|
#endif
|
|
|
|
p = (void *)malloc(newLen);
|
|
bool nn = p ? true : false;
|
|
|
|
#if defined(ESP8266_USE_EXTERNAL_HEAP)
|
|
ESP.resetHeap();
|
|
#endif
|
|
|
|
if (!nn)
|
|
return NULL;
|
|
|
|
#endif
|
|
memset(p, 0, newLen);
|
|
return p;
|
|
}
|
|
|
|
void delP(void *ptr)
|
|
{
|
|
void **p = (void **)ptr;
|
|
if (*p)
|
|
{
|
|
free(*p);
|
|
*p = 0;
|
|
}
|
|
}
|
|
|
|
size_t getReservedLen(size_t len)
|
|
{
|
|
int blen = len + 1;
|
|
|
|
int newlen = (blen / 4) * 4;
|
|
|
|
if (newlen < blen)
|
|
newlen += 4;
|
|
|
|
return (size_t)newlen;
|
|
}
|
|
|
|
size_t maxLength() const
|
|
{
|
|
if (bufferLength() == 0)
|
|
return 0;
|
|
return bufferLength() - 1;
|
|
}
|
|
|
|
void concat(const MB_String &s)
|
|
{
|
|
if (s.length() == 0)
|
|
return;
|
|
|
|
if (&s == this)
|
|
{
|
|
size_t slen = length();
|
|
|
|
if (2 * slen > maxLength())
|
|
{
|
|
if (!_reserve(2 * slen, false))
|
|
return;
|
|
}
|
|
|
|
memmove(buf + slen, buf, slen);
|
|
buf[2 * slen] = '\0';
|
|
}
|
|
else
|
|
{
|
|
concat(s.buf, s.length());
|
|
}
|
|
}
|
|
|
|
void concat(const char *cstr, size_t len)
|
|
{
|
|
if (!cstr)
|
|
return;
|
|
|
|
size_t slen = length();
|
|
|
|
if (slen + len > maxLength())
|
|
{
|
|
if (!_reserve(slen + len, false))
|
|
return;
|
|
}
|
|
|
|
memmove(buf + slen, cstr, len);
|
|
buf[slen + len] = '\0';
|
|
}
|
|
|
|
void concat(const char *cstr)
|
|
{
|
|
if (!cstr)
|
|
return;
|
|
|
|
concat(cstr, strlen(cstr));
|
|
}
|
|
|
|
void move(MB_String &rhs)
|
|
{
|
|
if (buf)
|
|
{
|
|
if (bufLen >= rhs.bufLen)
|
|
{
|
|
strcpy(buf, rhs.buf);
|
|
bufLen = rhs.bufLen;
|
|
rhs.bufLen = 0;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
free(buf);
|
|
}
|
|
}
|
|
buf = rhs.buf;
|
|
bufLen = rhs.bufLen;
|
|
rhs.buf = NULL;
|
|
}
|
|
|
|
void allocate(size_t len, bool shrink)
|
|
{
|
|
|
|
if (len == 0)
|
|
{
|
|
if (buf)
|
|
free(buf);
|
|
buf = NULL;
|
|
bufLen = 0;
|
|
return;
|
|
}
|
|
|
|
if (len > bufLen || shrink)
|
|
{
|
|
|
|
#if defined(ESP8266_USE_EXTERNAL_HEAP)
|
|
ESP.setExternalHeap();
|
|
#endif
|
|
|
|
if (shrink || (bufLen > 0 && buf))
|
|
{
|
|
int slen = length();
|
|
|
|
#if defined(BOARD_HAS_PSRAM) && defined(MB_STRING_USE_PSRAM)
|
|
if (ESP.getPsramSize() > 0)
|
|
buf = (char *)ps_realloc(buf, len);
|
|
else
|
|
buf = (char *)realloc(buf, len);
|
|
#else
|
|
buf = (char *)realloc(buf, len);
|
|
#endif
|
|
if (buf)
|
|
{
|
|
buf[slen] = '\0';
|
|
bufLen = len;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined(BOARD_HAS_PSRAM) && defined(MB_STRING_USE_PSRAM)
|
|
if (ESP.getPsramSize() > 0)
|
|
buf = (char *)ps_malloc(len);
|
|
else
|
|
buf = (char *)malloc(len);
|
|
#else
|
|
buf = (char *)malloc(len);
|
|
#endif
|
|
if (buf)
|
|
{
|
|
buf[0] = '\0';
|
|
bufLen = len;
|
|
}
|
|
}
|
|
|
|
#if defined(ESP8266_USE_EXTERNAL_HEAP)
|
|
ESP.resetHeap();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
MB_String ©(const char *cstr, size_t length)
|
|
{
|
|
|
|
if (!_reserve(length, false))
|
|
{
|
|
clear();
|
|
return *this;
|
|
}
|
|
|
|
memcpy_P(buf, (PGM_P)cstr, length);
|
|
buf[length] = '\0';
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool _reserve(size_t len, bool shrink)
|
|
{
|
|
|
|
size_t newlen = getReservedLen(len);
|
|
if (shrink)
|
|
allocate(newlen, true);
|
|
else if (newlen > bufLen)
|
|
allocate(newlen, false);
|
|
|
|
return newlen <= bufLen;
|
|
}
|
|
|
|
int strpos(const char *haystack, const char *needle, int offset) const
|
|
{
|
|
if (!haystack || !needle)
|
|
return -1;
|
|
|
|
int hlen = strlen(haystack);
|
|
int nlen = strlen(needle);
|
|
|
|
if (hlen == 0 || nlen == 0)
|
|
return -1;
|
|
|
|
int hidx = offset, nidx = 0;
|
|
while ((*(haystack + hidx) != '\0') && (*(needle + nidx) != '\0') && hidx < hlen)
|
|
{
|
|
if (*(needle + nidx) != *(haystack + hidx))
|
|
{
|
|
hidx++;
|
|
nidx = 0;
|
|
}
|
|
else
|
|
{
|
|
nidx++;
|
|
hidx++;
|
|
if (nidx == nlen)
|
|
return hidx - nidx;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int strpos(const char *haystack, char needle, int offset) const
|
|
{
|
|
if (!haystack || needle == 0)
|
|
return -1;
|
|
|
|
int hlen = strlen(haystack);
|
|
|
|
if (hlen == 0)
|
|
return -1;
|
|
|
|
int hidx = offset;
|
|
while ((*(haystack + hidx) != '\0') && hidx < hlen)
|
|
{
|
|
if (needle == *(haystack + hidx))
|
|
return hidx;
|
|
hidx++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int rstrpos(const char *haystack, const char *needle, int offset /* start search from this offset to the left string */) const
|
|
{
|
|
if (!haystack || !needle)
|
|
return -1;
|
|
|
|
int hlen = strlen(haystack);
|
|
int nlen = strlen(needle);
|
|
|
|
if (hlen == 0 || nlen == 0)
|
|
return -1;
|
|
|
|
int hidx = offset;
|
|
|
|
if (hidx >= hlen || (size_t)offset == npos)
|
|
hidx = hlen - 1;
|
|
|
|
int nidx = nlen - 1;
|
|
|
|
while (hidx >= 0)
|
|
{
|
|
if (*(needle + nidx) != *(haystack + hidx))
|
|
{
|
|
hidx--;
|
|
nidx = nlen - 1;
|
|
}
|
|
else
|
|
{
|
|
if (nidx == 0)
|
|
return hidx + nidx;
|
|
nidx--;
|
|
hidx--;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int rstrpos(const char *haystack, char needle, int offset /* start search from this offset to the left char */) const
|
|
{
|
|
if (!haystack || needle == 0)
|
|
return -1;
|
|
|
|
int hlen = strlen(haystack);
|
|
|
|
if (hlen == 0)
|
|
return -1;
|
|
|
|
int hidx = offset;
|
|
|
|
if (hidx >= hlen || (size_t)offset == npos)
|
|
hidx = hlen - 1;
|
|
|
|
while (hidx >= 0)
|
|
{
|
|
if (needle == *(haystack + hidx))
|
|
return hidx;
|
|
hidx--;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int compareTo(const MB_String &s) const
|
|
{
|
|
if (!buf || !s.buf)
|
|
{
|
|
if (s.buf && s.length() > 0)
|
|
return 0 - *(unsigned char *)s.buf;
|
|
if (buf && length() > 0)
|
|
return *(unsigned char *)buf;
|
|
return 0;
|
|
}
|
|
return strcmp(buf, s.buf);
|
|
}
|
|
|
|
unsigned char equals(const MB_String &s2) const
|
|
{
|
|
return (length() == s2.length() && compareTo(s2) == 0);
|
|
}
|
|
|
|
unsigned char equals(const char *cstr) const
|
|
{
|
|
if (length() == 0)
|
|
return (cstr == NULL || *cstr == 0);
|
|
if (cstr == NULL)
|
|
return buf[0] == 0;
|
|
return strcmp(buf, cstr) == 0;
|
|
}
|
|
|
|
char *ultoa(unsigned long value, char *str, int radix)
|
|
{
|
|
const char *format = NULL;
|
|
|
|
switch (radix)
|
|
{
|
|
case 8:
|
|
format = "%o";
|
|
break;
|
|
case 10:
|
|
format = "%ul";
|
|
break;
|
|
case 16:
|
|
format = "%x";
|
|
break;
|
|
}
|
|
|
|
if (format == NULL)
|
|
return str;
|
|
|
|
int size = sprintf(str, format, value);
|
|
return &str[size];
|
|
}
|
|
|
|
#if (!defined(ESP32) && !defined(ESP8266) && !defined(ARDUINO_ARCH_STM32) && !defined(ARDUINO_ARCH_SAMD)) || defined(ARDUINO_NANO_RP2040_CONNECT)
|
|
char *ltoa(long value, char *str, int radix)
|
|
{
|
|
const char *format = NULL;
|
|
|
|
switch (radix)
|
|
{
|
|
case 8:
|
|
format = "%o";
|
|
break;
|
|
case 10:
|
|
format = "%l";
|
|
break;
|
|
case 16:
|
|
format = "%x";
|
|
break;
|
|
}
|
|
|
|
if (format == NULL)
|
|
return str;
|
|
|
|
int size = sprintf(str, format, value);
|
|
return &str[size];
|
|
}
|
|
|
|
char *utoa(unsigned int value, char *str, int radix)
|
|
{
|
|
const char *format = NULL;
|
|
|
|
switch (radix)
|
|
{
|
|
case 8:
|
|
format = "%o";
|
|
break;
|
|
case 10:
|
|
format = "%u";
|
|
break;
|
|
case 16:
|
|
format = "%x";
|
|
break;
|
|
}
|
|
|
|
if (format == NULL)
|
|
return str;
|
|
|
|
int size = sprintf(str, format, value);
|
|
return &str[size];
|
|
}
|
|
|
|
char *itoa(int value, char *str, int radix)
|
|
{
|
|
const char *format = NULL;
|
|
|
|
switch (radix)
|
|
{
|
|
case 8:
|
|
format = "%o";
|
|
break;
|
|
case 10:
|
|
format = "%d";
|
|
break;
|
|
case 16:
|
|
format = "%x";
|
|
break;
|
|
}
|
|
|
|
if (format == NULL)
|
|
return str;
|
|
|
|
int size = sprintf(str, format, value);
|
|
return &str[size];
|
|
}
|
|
#endif
|
|
|
|
char *buf = NULL;
|
|
size_t bufLen = 0;
|
|
};
|
|
|
|
inline MB_String operator+(const MB_String &lhs, const MB_String &rhs)
|
|
{
|
|
MB_String res;
|
|
res.reserve(lhs.length() + rhs.length());
|
|
res += lhs;
|
|
res += rhs;
|
|
return res;
|
|
}
|
|
|
|
#if !defined(__AVR__)
|
|
|
|
inline MB_String operator+(MB_String &&lhs, const MB_String &rhs)
|
|
{
|
|
lhs += rhs;
|
|
return std::move(lhs);
|
|
}
|
|
|
|
inline MB_String operator+(MB_String &lhs, MB_String &&rhs)
|
|
{
|
|
lhs += rhs;
|
|
return std::move(lhs);
|
|
}
|
|
|
|
inline MB_String operator+(MB_String &lhs, char rhs)
|
|
{
|
|
lhs += rhs;
|
|
return std::move(lhs);
|
|
}
|
|
|
|
inline MB_String operator+(char lhs, MB_String &rhs)
|
|
{
|
|
rhs.insert(0, lhs);
|
|
return rhs;
|
|
}
|
|
|
|
inline MB_String operator+(MB_String &&lhs, char rhs)
|
|
{
|
|
return std::move(lhs.insert(0, rhs));
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif |