Copy ESP8266 String w/SSO to ESP32 repo (#2715)

I redid the ESP8266 WString library to enable small string optimization
(SSO) a while back, and think it would be helpful even on the ESP32 with
its higher memory complement.

SSO avoids lots of tiny mallocs() on the heap which cause fragmentation
by using the memory in the class object itself to store the actual
string and only mallocing() for buffers that are larger than what can
fit in thie class object.  Modern C++ std::string implementations have
this optimization as well, but since we're using Arduino strings we had
to roll our own.
This commit is contained in:
Earle F. Philhower, III 2019-04-26 09:41:42 -07:00 committed by Me No Dev
parent 932666a03f
commit ab309e40d5
3 changed files with 797 additions and 870 deletions

View File

@ -2,6 +2,7 @@
StreamString.cpp StreamString.cpp
Copyright (c) 2015 Markus Sattler. All rights reserved. Copyright (c) 2015 Markus Sattler. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -22,31 +23,27 @@
#include <Arduino.h> #include <Arduino.h>
#include "StreamString.h" #include "StreamString.h"
size_t StreamString::write(const uint8_t *data, size_t size) size_t StreamString::write(const uint8_t *data, size_t size) {
{
if(size && data) { if(size && data) {
if(reserve(length() + size + 1)) { if(reserve(length() + size + 1)) {
memcpy((void *) (buffer + len), (const void *) data, size); memcpy((void *) (wbuffer() + len()), (const void *) data, size);
len += size; setLen(len() + size);
*(buffer + len) = 0x00; // add null for string end *(wbuffer() + len()) = 0x00; // add null for string end
return size; return size;
} }
} }
return 0; return 0;
} }
size_t StreamString::write(uint8_t data) size_t StreamString::write(uint8_t data) {
{
return concat((char) data); return concat((char) data);
} }
int StreamString::available() int StreamString::available() {
{
return length(); return length();
} }
int StreamString::read() int StreamString::read() {
{
if(length()) { if(length()) {
char c = charAt(0); char c = charAt(0);
remove(0, 1); remove(0, 1);
@ -56,8 +53,7 @@ int StreamString::read()
return -1; return -1;
} }
int StreamString::peek() int StreamString::peek() {
{
if(length()) { if(length()) {
char c = charAt(0); char c = charAt(0);
return c; return c;
@ -65,7 +61,6 @@ int StreamString::peek()
return -1; return -1;
} }
void StreamString::flush() void StreamString::flush() {
{
} }

File diff suppressed because it is too large Load Diff

View File

@ -35,295 +35,292 @@ class StringSumHelper;
// an abstract class used as a means to proide a unique pointer type // an abstract class used as a means to proide a unique pointer type
// but really has no body // but really has no body
class __FlashStringHelper; class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal))) #define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
// The string class // The string class
class String class String {
{ // use a function pointer to allow for "if (s)" without the
// use a function pointer to allow for "if (s)" without the // complications of an operator bool(). for more information, see:
// complications of an operator bool(). for more information, see: // http://www.artima.com/cppsource/safebool.html
// http://www.artima.com/cppsource/safebool.html typedef void (String::*StringIfHelperType)() const;
typedef void (String::*StringIfHelperType)() const; void StringIfHelper() const {
void StringIfHelper() const
{
}
public:
// constructors
// creates a copy of the initial value.
// if the initial value is null or invalid, or if memory allocation
// fails, the string will be marked as invalid (i.e. "if (s)" will
// be false).
String(const char *cstr = "");
String(const String &str);
String(const __FlashStringHelper *str) : String(reinterpret_cast<const char *>(str)) {};
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String(String &&rval);
String(StringSumHelper &&rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base = 10);
explicit String(int, unsigned char base = 10);
explicit String(unsigned int, unsigned char base = 10);
explicit String(long, unsigned char base = 10);
explicit String(unsigned long, unsigned char base = 10);
explicit String(float, unsigned char decimalPlaces = 2);
explicit String(double, unsigned char decimalPlaces = 2);
~String(void);
// memory management
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const
{
if(buffer) {
return len;
} else {
return 0;
} }
}
// creates a copy of the assigned value. if the value is null or public:
// invalid, or if the memory allocation fails, the string will be // constructors
// marked as invalid ("if (s)" will be false). // creates a copy of the initial value.
String & operator =(const String &rhs); // if the initial value is null or invalid, or if memory allocation
String & operator =(const char *cstr); // fails, the string will be marked as invalid (i.e. "if (s)" will
String & operator = (const __FlashStringHelper *str); // be false).
String(const char *cstr = "");
String(const String &str);
String(const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
String & operator =(String &&rval); String(String &&rval);
String & operator =(StringSumHelper &&rval); String(StringSumHelper &&rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base = 10);
explicit String(int, unsigned char base = 10);
explicit String(unsigned int, unsigned char base = 10);
explicit String(long, unsigned char base = 10);
explicit String(unsigned long, unsigned char base = 10);
explicit String(float, unsigned char decimalPlaces = 2);
explicit String(double, unsigned char decimalPlaces = 2);
~String(void);
// memory management
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const {
if(buffer()) {
return len();
} else {
return 0;
}
}
// creates a copy of the assigned value. if the value is null or
// invalid, or if the memory allocation fails, the string will be
// marked as invalid ("if (s)" will be false).
String & operator =(const String &rhs);
String & operator =(const char *cstr);
String & operator = (const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String & operator =(String &&rval);
String & operator =(StringSumHelper &&rval);
#endif #endif
// concatenate (works w/ built-in types) // concatenate (works w/ built-in types)
// returns true on success, false on failure (in which case, the string // returns true on success, false on failure (in which case, the string
// is left unchanged). if the argument is null or invalid, the // is left unchanged). if the argument is null or invalid, the
// concatenation is considered unsucessful. // concatenation is considered unsucessful.
unsigned char concat(const String &str); unsigned char concat(const String &str);
unsigned char concat(const char *cstr); unsigned char concat(const char *cstr);
unsigned char concat(char c); unsigned char concat(char c);
unsigned char concat(unsigned char c); unsigned char concat(unsigned char c);
unsigned char concat(int num); unsigned char concat(int num);
unsigned char concat(unsigned int num); unsigned char concat(unsigned int num);
unsigned char concat(long num); unsigned char concat(long num);
unsigned char concat(unsigned long num); unsigned char concat(unsigned long num);
unsigned char concat(float num); unsigned char concat(float num);
unsigned char concat(double num); unsigned char concat(double num);
unsigned char concat(const __FlashStringHelper * str); unsigned char concat(const __FlashStringHelper * str);
// if there's not enough memory for the concatenated value, the string // if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signalled in any way) // will be left unchanged (but this isn't signalled in any way)
String & operator +=(const String &rhs) String & operator +=(const String &rhs) {
{ concat(rhs);
concat(rhs); return (*this);
return (*this); }
} String & operator +=(const char *cstr) {
String & operator +=(const char *cstr) concat(cstr);
{ return (*this);
concat(cstr); }
return (*this); String & operator +=(char c) {
} concat(c);
String & operator +=(char c) return (*this);
{ }
concat(c); String & operator +=(unsigned char num) {
return (*this); concat(num);
} return (*this);
String & operator +=(unsigned char num) }
{ String & operator +=(int num) {
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(int num) String & operator +=(unsigned int num) {
{ concat(num);
concat(num); return (*this);
return (*this); }
} String & operator +=(long num) {
String & operator +=(unsigned int num) concat(num);
{ return (*this);
concat(num); }
return (*this); String & operator +=(unsigned long num) {
} concat(num);
String & operator +=(long num) return (*this);
{ }
concat(num); String & operator +=(float num) {
return (*this); concat(num);
} return (*this);
String & operator +=(unsigned long num) }
{ String & operator +=(double num) {
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(float num) String & operator += (const __FlashStringHelper *str){
{ concat(str);
concat(num); return (*this);
return (*this); }
}
String & operator +=(double num)
{
concat(num);
return (*this);
}
String & operator += (const __FlashStringHelper *str)
{
concat(str);
return (*this);
}
friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs); friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr); friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper & operator +(const StringSumHelper &lhs, char c); friend StringSumHelper & operator +(const StringSumHelper &lhs, char c);
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num); friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, int num); friend StringSumHelper & operator +(const StringSumHelper &lhs, int num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num); friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, long num); friend StringSumHelper & operator +(const StringSumHelper &lhs, long num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num); friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, float num); friend StringSumHelper & operator +(const StringSumHelper &lhs, float num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, double num); friend StringSumHelper & operator +(const StringSumHelper &lhs, double num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs); friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs);
// comparison (only works w/ Strings and "strings") // comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const operator StringIfHelperType() const {
{ return buffer() ? &String::StringIfHelper : 0;
return buffer ? &String::StringIfHelper : 0; }
} int compareTo(const String &s) const;
int compareTo(const String &s) const; unsigned char equals(const String &s) const;
unsigned char equals(const String &s) const; unsigned char equals(const char *cstr) const;
unsigned char equals(const char *cstr) const; unsigned char operator ==(const String &rhs) const {
unsigned char operator ==(const String &rhs) const return equals(rhs);
{ }
return equals(rhs); unsigned char operator ==(const char *cstr) const {
} return equals(cstr);
unsigned char operator ==(const char *cstr) const }
{ unsigned char operator !=(const String &rhs) const {
return equals(cstr); return !equals(rhs);
} }
unsigned char operator !=(const String &rhs) const unsigned char operator !=(const char *cstr) const {
{ return !equals(cstr);
return !equals(rhs); }
} unsigned char operator <(const String &rhs) const;
unsigned char operator !=(const char *cstr) const unsigned char operator >(const String &rhs) const;
{ unsigned char operator <=(const String &rhs) const;
return !equals(cstr); unsigned char operator >=(const String &rhs) const;
} unsigned char equalsIgnoreCase(const String &s) const;
unsigned char operator <(const String &rhs) const; unsigned char equalsConstantTime(const String &s) const;
unsigned char operator >(const String &rhs) const; unsigned char startsWith(const String &prefix) const;
unsigned char operator <=(const String &rhs) const; unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char operator >=(const String &rhs) const; unsigned char endsWith(const String &suffix) const;
unsigned char equalsIgnoreCase(const String &s) const;
unsigned char equalsConstantTime(const String &s) const;
unsigned char startsWith(const String &prefix) const;
unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char endsWith(const String &suffix) const;
// character acccess // character acccess
char charAt(unsigned int index) const; char charAt(unsigned int index) const;
void setCharAt(unsigned int index, char c); void setCharAt(unsigned int index, char c);
char operator [](unsigned int index) const; char operator [](unsigned int index) const;
char& operator [](unsigned int index); char& operator [](unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const; void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
{ getBytes((unsigned char *) buf, bufsize, index);
getBytes((unsigned char *) buf, bufsize, index); }
} const char* c_str() const { return buffer(); }
const char * c_str() const char* begin() { return wbuffer(); }
{ char* end() { return wbuffer() + length(); }
return buffer; const char* begin() const { return c_str(); }
} const char* end() const { return c_str() + length(); }
// search // search
int indexOf(char ch) const; int indexOf(char ch) const;
int indexOf(char ch, unsigned int fromIndex) const; int indexOf(char ch, unsigned int fromIndex) const;
int indexOf(const String &str) const; int indexOf(const String &str) const;
int indexOf(const String &str, unsigned int fromIndex) const; int indexOf(const String &str, unsigned int fromIndex) const;
int lastIndexOf(char ch) const; int lastIndexOf(char ch) const;
int lastIndexOf(char ch, unsigned int fromIndex) const; int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const; int lastIndexOf(const String &str) const;
int lastIndexOf(const String &str, unsigned int fromIndex) const; int lastIndexOf(const String &str, unsigned int fromIndex) const;
String substring(unsigned int beginIndex) const String substring(unsigned int beginIndex) const {
{ return substring(beginIndex, len());
return substring(beginIndex, len); }
} ;
; String substring(unsigned int beginIndex, unsigned int endIndex) const;
String substring(unsigned int beginIndex, unsigned int endIndex) const;
// modification // modification
void replace(char find, char replace); void replace(char find, char replace);
void replace(const String& find, const String& replace); void replace(const String& find, const String& replace);
void remove(unsigned int index); void remove(unsigned int index);
void remove(unsigned int index, unsigned int count); void remove(unsigned int index, unsigned int count);
void toLowerCase(void); void toLowerCase(void);
void toUpperCase(void); void toUpperCase(void);
void trim(void); void trim(void);
// parsing/conversion // parsing/conversion
long toInt(void) const; long toInt(void) const;
float toFloat(void) const; float toFloat(void) const;
double toDouble(void) const; double toDouble(void) const;
protected: protected:
char *buffer; // the actual char array // Contains the string info when we're not in SSO mode
unsigned int capacity; // the array length minus one (for the '\0') struct _ptr {
unsigned int len; // the String length (not counting the '\0') char * buff;
protected: uint16_t cap;
void init(void); uint16_t len;
void invalidate(void); };
unsigned char changeBuffer(unsigned int maxStrLen);
unsigned char concat(const char *cstr, unsigned int length);
// copy and move // SSO is handled by checking the last byte of sso_buff.
String & copy(const char *cstr, unsigned int length); // When not in SSO mode, that byte is set to 0xff, while when in SSO mode it is always 0x00 (so it can serve as the string terminator as well as a flag)
String & copy(const __FlashStringHelper *pstr, unsigned int length); // This allows strings up up to 12 (11 + \0 termination) without any extra space.
enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more
enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum
union {
struct _ptr ptr;
char sso_buf[SSOSIZE];
};
// Accessor functions
inline bool sso() const { return sso_buf[SSOSIZE - 1] == 0; }
inline unsigned int len() const { return sso() ? strlen(sso_buf) : ptr.len; }
inline unsigned int capacity() const { return sso() ? SSOSIZE - 1 : ptr.cap; }
inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; }
inline void setLen(int len) { if (!sso()) ptr.len = len; }
inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; }
inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; }
// Buffer accessor functions
inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buff); }
inline char *wbuffer() const { return sso() ? const_cast<char *>(sso_buf) : ptr.buff; } // Writable version of buffer
protected:
void init(void);
void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen);
unsigned char concat(const char *cstr, unsigned int length);
// copy and move
String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *pstr, unsigned int length);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
void move(String &rhs); void move(String &rhs);
#endif #endif
}; };
class StringSumHelper: public String class StringSumHelper: public String {
{ public:
public: StringSumHelper(const String &s) :
StringSumHelper(const String &s) : String(s) {
String(s) }
{ StringSumHelper(const char *p) :
} String(p) {
StringSumHelper(const char *p) : }
String(p) StringSumHelper(char c) :
{ String(c) {
} }
StringSumHelper(char c) : StringSumHelper(unsigned char num) :
String(c) String(num) {
{ }
} StringSumHelper(int num) :
StringSumHelper(unsigned char num) : String(num) {
String(num) }
{ StringSumHelper(unsigned int num) :
} String(num) {
StringSumHelper(int num) : }
String(num) StringSumHelper(long num) :
{ String(num) {
} }
StringSumHelper(unsigned int num) : StringSumHelper(unsigned long num) :
String(num) String(num) {
{ }
} StringSumHelper(float num) :
StringSumHelper(long num) : String(num) {
String(num) }
{ StringSumHelper(double num) :
} String(num) {
StringSumHelper(unsigned long num) : }
String(num)
{
}
StringSumHelper(float num) :
String(num)
{
}
StringSumHelper(double num) :
String(num)
{
}
}; };
extern const String emptyString;
#endif // __cplusplus #endif // __cplusplus
#endif // String_class_h #endif // String_class_h