1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-25 20:02:37 +03:00

WString: Optimize a bit (#7553)

* WString: Optimize a bit

* move bodies of dtor, `init()` and `charAt()` to .h (implicitly inlined)

* unify descriptions of the initialization into one: `init()` (literally), that is called from each ctors, `invalidate()` and `move()`

* invert the SSO state logic in order to make init state zeroed (as a result, each inlined `init()` saves 1 insn)

* detab and trim

* remove `inline` from .h

* cosmetics

* optimize the non-SSO -> SSO transition part of `changeBuffer()`

* remove duped body of `operator =(StringSumHelper &&rval)`

* remove common subexpressions from `lastIndexOf()` and `substring()`

* eliminate `strlen(buf)` after calling `sprintf(buf, ...)` that returns # of chars written

* eliminate `len()` after calling `setLen(newlen)`

* make ctor`(char c)` inlineable

* optimize `setLen()`

* replace constant-forwarding overload functions with default argument ones

* optimize `concat(char c)`

* * optimize `init()` more
This commit is contained in:
Takayuki 'January June' Suwa 2020-11-16 18:40:48 +09:00 committed by GitHub
parent 5ae3efb823
commit 8fe80f1630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 212 additions and 246 deletions

View File

@ -55,14 +55,6 @@ String::String(StringSumHelper &&rval) noexcept {
move(rval);
}
String::String(char c) {
init();
char buf[2];
buf[0] = c;
buf[1] = 0;
*this = buf;
}
String::String(unsigned char value, unsigned char base) {
init();
char buf[1 + 8 * sizeof(unsigned char)];
@ -91,7 +83,7 @@ String::String(unsigned int value, unsigned char base) {
String::String(long value, unsigned char base) {
init();
char buf[2 + 8 * sizeof(long)];
if (base==10) {
if (base == 10) {
sprintf(buf, "%ld", value);
} else {
ltoa(value, buf, base);
@ -118,31 +110,21 @@ String::String(double value, unsigned char decimalPlaces) {
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
String::~String() {
invalidate();
}
/*********************************************/
/* Memory Management */
/*********************************************/
inline void String::init(void) {
setSSO(true);
setLen(0);
wbuffer()[0] = 0;
}
void String::invalidate(void) {
if(!isSSO() && wbuffer())
if (!isSSO() && wbuffer())
free(wbuffer());
init();
}
unsigned char String::reserve(unsigned int size) {
if(buffer() && capacity() >= size)
if (buffer() && capacity() >= size)
return 1;
if(changeBuffer(size)) {
if(len() == 0)
if (changeBuffer(size)) {
if (len() == 0)
wbuffer()[0] = 0;
return 1;
}
@ -157,35 +139,32 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
uint16_t oldLen = len();
setSSO(true);
setLen(oldLen);
return 1;
} else { // if bufptr && !isSSO()
// Using bufptr, need to shrink into sso.buff
char temp[sizeof(sso.buff)];
memcpy(temp, buffer(), maxStrLen);
free(wbuffer());
const char *temp = buffer();
uint16_t oldLen = len();
setSSO(true);
setLen(oldLen);
memcpy(wbuffer(), temp, maxStrLen);
return 1;
free((void *)temp);
}
return 1;
}
// Fallthrough to normal allocator
size_t newSize = (maxStrLen + 16) & (~0xf);
// Make sure we can fit newsize in the buffer
if (newSize > CAPACITY_MAX) {
return false;
return 0;
}
uint16_t oldLen = len();
char *newbuffer = (char *) realloc(isSSO() ? nullptr : wbuffer(), newSize);
char *newbuffer = (char *)realloc(isSSO() ? nullptr : wbuffer(), newSize);
if (newbuffer) {
size_t oldSize = capacity() + 1; // include NULL.
if (isSSO()) {
// Copy the SSO buffer into allocated space
memmove_P(newbuffer, sso.buff, sizeof(sso.buff));
}
if (newSize > oldSize)
{
if (newSize > oldSize) {
memset(newbuffer + oldSize, 0, newSize - oldSize);
}
setSSO(false);
@ -201,7 +180,7 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
/* Copy and Move */
/*********************************************/
String & String::copy(const char *cstr, unsigned int length) {
String &String::copy(const char *cstr, unsigned int length) {
if (!reserve(length)) {
invalidate();
return *this;
@ -211,7 +190,7 @@ String & String::copy(const char *cstr, unsigned int length) {
return *this;
}
String & String::copy(const __FlashStringHelper *pstr, unsigned int length) {
String &String::copy(const __FlashStringHelper *pstr, unsigned int length) {
if (!reserve(length)) {
invalidate();
return *this;
@ -227,44 +206,35 @@ void String::move(String &rhs) noexcept {
rhs.init();
}
String & String::operator =(const String &rhs) {
String &String::operator =(const String &rhs) {
if (this == &rhs)
return *this;
if (rhs.buffer())
copy(rhs.buffer(), rhs.len());
else
invalidate();
return *this;
}
String & String::operator =(String &&rval) noexcept {
String &String::operator =(String &&rval) noexcept {
if (this != &rval)
move(rval);
return *this;
}
String & String::operator =(StringSumHelper &&rval) noexcept {
if (this != &rval)
move(rval);
return *this;
}
String & String::operator =(const char *cstr) {
String &String::operator =(const char *cstr) {
if (cstr)
copy(cstr, strlen(cstr));
else
invalidate();
return *this;
}
String & String::operator = (const __FlashStringHelper *pstr)
{
if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
else invalidate();
String &String::operator =(const __FlashStringHelper *pstr) {
if (pstr)
copy(pstr, strlen_P((PGM_P)pstr));
else
invalidate();
return *this;
}
@ -285,7 +255,7 @@ unsigned char String::concat(const String &s) {
return 0;
memmove_P(wbuffer() + len(), buffer(), len());
setLen(newlen);
wbuffer()[len()] = 0;
wbuffer()[newlen] = 0;
return 1;
} else {
return concat(s.buffer(), s.len());
@ -313,22 +283,17 @@ unsigned char String::concat(const char *cstr) {
}
unsigned char String::concat(char c) {
char buf[2];
buf[0] = c;
buf[1] = 0;
return concat(buf, 1);
return concat(&c, 1);
}
unsigned char String::concat(unsigned char num) {
char buf[1 + 3 * sizeof(unsigned char)];
sprintf(buf, "%d", num);
return concat(buf, strlen(buf));
return concat(buf, sprintf(buf, "%d", num));
}
unsigned char String::concat(int num) {
char buf[2 + 3 * sizeof(int)];
sprintf(buf, "%d", num);
return concat(buf, strlen(buf));
return concat(buf, sprintf(buf, "%d", num));
}
unsigned char String::concat(unsigned int num) {
@ -339,8 +304,7 @@ unsigned char String::concat(unsigned int num) {
unsigned char String::concat(long num) {
char buf[2 + 3 * sizeof(long)];
sprintf(buf, "%ld", num);
return concat(buf, strlen(buf));
return concat(buf, sprintf(buf, "%ld", num));
}
unsigned char String::concat(unsigned long num) {
@ -351,22 +315,25 @@ unsigned char String::concat(unsigned long num) {
unsigned char String::concat(float num) {
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
char *string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
unsigned char String::concat(double num) {
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
char *string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
unsigned char String::concat(const __FlashStringHelper * str) {
if (!str) return 0;
unsigned char String::concat(const __FlashStringHelper *str) {
if (!str)
return 0;
int length = strlen_P((PGM_P)str);
if (length == 0) return 1;
if (length == 0)
return 1;
unsigned int newlen = len() + length;
if (!reserve(newlen)) return 0;
if (!reserve(newlen))
return 0;
memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1);
setLen(newlen);
return 1;
@ -376,79 +343,78 @@ unsigned char String::concat(const __FlashStringHelper * str) {
/* Concatenate */
/*********************************************/
StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, const String &rhs) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(rhs.buffer(), rhs.len()))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, const char *cstr) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!cstr || !a.concat(cstr, strlen(cstr)))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, char c) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, char c) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(c))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, unsigned char num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, int num) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, int num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, unsigned int num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, long num) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, long num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, float num) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, float num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num))
a.invalidate();
return a;
}
StringSumHelper & operator +(const StringSumHelper &lhs, double num) {
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, double num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num))
a.invalidate();
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs)
{
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(rhs))
a.invalidate();
return a;
@ -459,11 +425,11 @@ StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHel
/*********************************************/
int String::compareTo(const String &s) const {
if(!buffer() || !s.buffer()) {
if(s.buffer() && s.len() > 0)
return 0 - *(unsigned char *) s.buffer();
if(buffer() && len() > 0)
return *(unsigned char *) buffer();
if (!buffer() || !s.buffer()) {
if (s.buffer() && s.len() > 0)
return 0 - *(unsigned char *)s.buffer();
if (buffer() && len() > 0)
return *(unsigned char *)buffer();
return 0;
}
return strcmp(buffer(), s.buffer());
@ -521,7 +487,7 @@ unsigned char String::equalsConstantTime(const String &s2) const {
//at this point lengths are the same
if (len() == 0)
return 1;
//at this point lenghts are the same and non-zero
//at this point lengths are the same and non-zero
const char *p1 = buffer();
const char *p2 = s2.buffer();
unsigned int equalchars = 0;
@ -541,19 +507,19 @@ unsigned char String::equalsConstantTime(const String &s2) const {
}
unsigned char String::startsWith(const String &s2) const {
if(len() < s2.len())
if (len() < s2.len())
return 0;
return startsWith(s2, 0);
}
unsigned char String::startsWith(const String &s2, unsigned int offset) const {
if(offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer())
if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer())
return 0;
return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0;
}
unsigned char String::endsWith(const String &s2) const {
if(len() < s2.len() || !buffer() || !s2.buffer())
if (len() < s2.len() || !buffer() || !s2.buffer())
return 0;
return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0;
}
@ -562,16 +528,12 @@ unsigned char String::endsWith(const String &s2) const {
/* Character Access */
/*********************************************/
char String::charAt(unsigned int loc) const {
return operator[](loc);
}
void String::setCharAt(unsigned int loc, char c) {
if (loc < len())
wbuffer()[loc] = c;
}
char & String::operator[](unsigned int index) {
char &String::operator[](unsigned int index) {
static char dummy_writable_char;
if (index >= len() || !buffer()) {
dummy_writable_char = 0;
@ -596,7 +558,7 @@ void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int ind
unsigned int n = bufsize - 1;
if (n > len() - index)
n = len() - index;
strncpy((char *) buf, buffer() + index, n);
strncpy((char *)buf, buffer() + index, n);
buf[n] = 0;
}
@ -604,31 +566,15 @@ void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int ind
/* Search */
/*********************************************/
int String::indexOf(char c) const {
return indexOf(c, 0);
}
int String::indexOf(char ch, unsigned int fromIndex) const {
if (fromIndex >= len())
return -1;
const char* temp = strchr(buffer() + fromIndex, ch);
const char *temp = strchr(buffer() + fromIndex, ch);
if (temp == NULL)
return -1;
return temp - buffer();
}
int String::indexOf(const __FlashStringHelper *s2) const {
return indexOf(s2, 0);
}
int String::indexOf(const __FlashStringHelper *s2, unsigned int fromIndex) const {
return indexOf((const char*) s2, fromIndex);
}
int String::indexOf(const char *s2) const {
return indexOf(s2, 0);
}
int String::indexOf(const char *s2, unsigned int fromIndex) const {
if (fromIndex >= len())
return -1;
@ -638,28 +584,25 @@ int String::indexOf(const char *s2, unsigned int fromIndex) const {
return found - buffer();
}
int String::indexOf(const String &s2) const {
return indexOf(s2, 0);
}
int String::indexOf(const String &s2, unsigned int fromIndex) const {
return indexOf(s2.c_str(), fromIndex);
}
int String::lastIndexOf(char theChar) const {
return lastIndexOf(theChar, len() - 1);
int String::lastIndexOf(char ch) const {
return lastIndexOf(ch, len() - 1);
}
int String::lastIndexOf(char ch, unsigned int fromIndex) const {
if (fromIndex >= len())
return -1;
char tempchar = buffer()[fromIndex + 1];
wbuffer()[fromIndex + 1] = '\0';
char* temp = strrchr(wbuffer(), ch);
wbuffer()[fromIndex + 1] = tempchar;
char *writeTo = wbuffer();
char tempchar = writeTo[fromIndex + 1]; // save the replaced character
writeTo[fromIndex + 1] = '\0';
char *temp = strrchr(writeTo, ch);
writeTo[fromIndex + 1] = tempchar; // restore character
if (temp == NULL)
return -1;
return temp - buffer();
return temp - writeTo;
}
int String::lastIndexOf(const String &s2) const {
@ -672,11 +615,11 @@ int String::lastIndexOf(const String &s2, unsigned int fromIndex) const {
if (fromIndex >= len())
fromIndex = len() - 1;
int found = -1;
for (char *p = wbuffer(); p <= wbuffer() + fromIndex; p++) {
for (const char *p = buffer(); p <= buffer() + fromIndex; p++) {
p = strstr(p, s2.buffer());
if (!p)
break;
if ((unsigned int) (p - wbuffer()) <= fromIndex)
if ((unsigned int)(p - buffer()) <= fromIndex)
found = p - buffer();
}
return found;
@ -693,10 +636,11 @@ String String::substring(unsigned int left, unsigned int right) const {
return out;
if (right > len())
right = len();
char temp = buffer()[right]; // save the replaced character
wbuffer()[right] = '\0';
out = wbuffer() + left; // pointer arithmetic
wbuffer()[right] = temp; //restore character
char *writeTo = wbuffer();
char tempchar = writeTo[right]; // save the replaced character
writeTo[right] = '\0';
out = writeTo + left; // pointer arithmetic
writeTo[right] = tempchar; // restore character
return out;
}
@ -713,7 +657,7 @@ void String::replace(char find, char replace) {
}
}
void String::replace(const String& find, const String& replace) {
void String::replace(const String &find, const String &replace) {
if (len() == 0 || find.len() == 0)
return;
int diff = replace.len() - find.len();
@ -735,7 +679,7 @@ void String::replace(const String& find, const String& replace) {
readFrom = foundAt + find.len();
setLen(len() + diff);
}
memmove_P(writeTo, readFrom, strlen(readFrom)+1);
memmove_P(writeTo, readFrom, strlen(readFrom) + 1);
} else {
unsigned int size = len(); // compute size needed for result
while ((foundAt = strstr(readFrom, find.buffer())) != NULL) {
@ -759,13 +703,6 @@ void String::replace(const String& find, const String& replace) {
}
}
void String::remove(unsigned int index) {
// Pass the biggest integer as the count. The remove method
// below will take care of truncating it at the end of the
// string.
remove(index, (unsigned int) -1);
}
void String::remove(unsigned int index, unsigned int count) {
if (index >= len()) {
return;
@ -828,11 +765,10 @@ long String::toInt(void) const {
float String::toFloat(void) const {
if (buffer())
return atof(buffer());
return 0;
return 0.0F;
}
double String::toDouble(void) const
{
double String::toDouble(void) const {
if (buffer())
return atof(buffer());
return 0.0;

View File

@ -53,7 +53,7 @@ class String {
// 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() {
String() __attribute__((always_inline)) { // See init()
init();
}
String(const char *cstr);
@ -61,7 +61,12 @@ class String {
String(const __FlashStringHelper *str);
String(String &&rval) noexcept;
String(StringSumHelper &&rval) noexcept;
explicit String(char c);
explicit String(char c) {
sso.buff[0] = c;
sso.buff[1] = 0;
sso.len = 1;
sso.isHeap = 0;
}
explicit String(unsigned char, unsigned char base = 10);
explicit String(int, unsigned char base = 10);
explicit String(unsigned int, unsigned char base = 10);
@ -69,35 +74,35 @@ class String {
explicit String(unsigned long, unsigned char base = 10);
explicit String(float, unsigned char decimalPlaces = 2);
explicit String(double, unsigned char decimalPlaces = 2);
~String(void);
~String() {
invalidate();
}
// 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;
}
unsigned int length(void) const {
return buffer() ? len() : 0;
}
inline void clear(void) {
void clear(void) {
setLen(0);
}
inline bool isEmpty(void) const {
bool isEmpty(void) const {
return length() == 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);
String & operator =(String &&rval) noexcept;
String & operator =(StringSumHelper &&rval) noexcept;
String &operator =(const String &rhs);
String &operator =(const char *cstr);
String &operator =(const __FlashStringHelper *str);
String &operator =(String &&rval) noexcept;
String &operator =(StringSumHelper &&rval) noexcept {
return operator =((String &&)rval);
}
// concatenate (works w/ built-in types)
@ -114,67 +119,67 @@ class String {
unsigned char concat(unsigned long num);
unsigned char concat(float num);
unsigned char concat(double num);
unsigned char concat(const __FlashStringHelper * str);
unsigned char concat(const __FlashStringHelper *str);
unsigned char concat(const char *cstr, unsigned int length);
// if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signalled in any way)
String & operator +=(const String &rhs) {
String &operator +=(const String &rhs) {
concat(rhs);
return (*this);
return *this;
}
String & operator +=(const char *cstr) {
String &operator +=(const char *cstr) {
concat(cstr);
return (*this);
return *this;
}
String & operator +=(char c) {
String &operator +=(char c) {
concat(c);
return (*this);
return *this;
}
String & operator +=(unsigned char num) {
String &operator +=(unsigned char num) {
concat(num);
return (*this);
return *this;
}
String & operator +=(int num) {
String &operator +=(int num) {
concat(num);
return (*this);
return *this;
}
String & operator +=(unsigned int num) {
String &operator +=(unsigned int num) {
concat(num);
return (*this);
return *this;
}
String & operator +=(long num) {
String &operator +=(long num) {
concat(num);
return (*this);
return *this;
}
String & operator +=(unsigned long num) {
String &operator +=(unsigned long num) {
concat(num);
return (*this);
return *this;
}
String & operator +=(float num) {
String &operator +=(float num) {
concat(num);
return (*this);
return *this;
}
String & operator +=(double num) {
String &operator +=(double num) {
concat(num);
return (*this);
return *this;
}
String & operator += (const __FlashStringHelper *str){
String &operator +=(const __FlashStringHelper *str) {
concat(str);
return (*this);
return *this;
}
friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper & operator +(const StringSumHelper &lhs, char c);
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, 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, unsigned long num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, float 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 String &rhs);
friend StringSumHelper &operator +(const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper &operator +(const StringSumHelper &lhs, char c);
friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned char num);
friend StringSumHelper &operator +(const StringSumHelper &lhs, 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, unsigned long num);
friend StringSumHelper &operator +(const StringSumHelper &lhs, float num);
friend StringSumHelper &operator +(const StringSumHelper &lhs, double num);
friend StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs);
// comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const {
@ -202,45 +207,45 @@ class String {
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 char * prefix) const {
unsigned char startsWith(const char *prefix) const {
return this->startsWith(String(prefix));
}
unsigned char startsWith(const __FlashStringHelper * prefix) const {
unsigned char startsWith(const __FlashStringHelper *prefix) const {
return this->startsWith(String(prefix));
}
unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char endsWith(const String &suffix) const;
unsigned char endsWith(const char * suffix) const {
unsigned char endsWith(const char *suffix) const {
return this->endsWith(String(suffix));
}
unsigned char endsWith(const __FlashStringHelper * suffix) const {
unsigned char endsWith(const __FlashStringHelper *suffix) const {
return this->endsWith(String(suffix));
}
// character access
char charAt(unsigned int index) const;
char charAt(unsigned int index) const {
return operator [](index);
}
void setCharAt(unsigned int index, char c);
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 toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
getBytes((unsigned char *) buf, bufsize, index);
}
const char* c_str() const { return buffer(); }
char* begin() { return wbuffer(); }
char* end() { return wbuffer() + length(); }
const char* begin() const { return c_str(); }
const char* end() const { return c_str() + length(); }
const char *c_str() const { return buffer(); }
char *begin() { return wbuffer(); }
char *end() { return wbuffer() + length(); }
const char *begin() const { return c_str(); }
const char *end() const { return c_str() + length(); }
// search
int indexOf(char ch) const;
int indexOf(char ch, unsigned int fromIndex) const;
int indexOf(const char *str) const;
int indexOf(const char *str, unsigned int fromIndex) const;
int indexOf(const __FlashStringHelper *str) const;
int indexOf(const __FlashStringHelper *str, unsigned int fromIndex) const;
int indexOf(const String &str) const;
int indexOf(const String &str, unsigned int fromIndex) const;
int indexOf(char ch, unsigned int fromIndex = 0) const;
int indexOf(const char *str, unsigned int fromIndex = 0) const;
int indexOf(const __FlashStringHelper *str, unsigned int fromIndex = 0) const {
return indexOf((const char*)str, fromIndex);
}
int indexOf(const String &str, unsigned int fromIndex = 0) const;
int lastIndexOf(char ch) const;
int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const;
@ -248,29 +253,29 @@ class String {
String substring(unsigned int beginIndex) const {
return substring(beginIndex, len());
}
;
String substring(unsigned int beginIndex, unsigned int endIndex) const;
// modification
void replace(char find, char replace);
void replace(const String& find, const String& replace);
void replace(const char * find, const String& replace) {
void replace(const String &find, const String &replace);
void replace(const char *find, const String &replace) {
this->replace(String(find), replace);
}
void replace(const __FlashStringHelper * find, const String& replace) {
void replace(const __FlashStringHelper *find, const String &replace) {
this->replace(String(find), replace);
}
void replace(const char * find, const char * replace) {
void replace(const char *find, const char *replace) {
this->replace(String(find), String(replace));
}
void replace(const __FlashStringHelper * find, const char * replace) {
void replace(const __FlashStringHelper *find, const char *replace) {
this->replace(String(find), String(replace));
}
void replace(const __FlashStringHelper * find, const __FlashStringHelper * replace) {
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
this->replace(String(find), String(replace));
}
void remove(unsigned int index);
void remove(unsigned int index, unsigned int count);
// Pass the biggest integer if the count is not specified.
// The remove method below will take care of truncating it at the end of the string.
void remove(unsigned int index, unsigned int count = (unsigned int)-1);
void toLowerCase(void);
void toUpperCase(void);
void trim(void);
@ -278,7 +283,7 @@ class String {
// parsing/conversion
long toInt(void) const;
float toFloat(void) const;
double toDouble(void) const;
double toDouble(void) const;
protected:
// Contains the string info when we're not in SSO mode
@ -291,8 +296,8 @@ class String {
enum { SSOSIZE = sizeof(struct _ptr) + 4 - 1 }; // Characters to allocate space for SSO, must be 12 or more
struct _sso {
char buff[SSOSIZE];
unsigned char len : 7; // Ensure only one byte is allocated by GCC for the bitfields
unsigned char isSSO : 1;
unsigned char len : 7; // Ensure only one byte is allocated by GCC for the bitfields
unsigned char isHeap : 1;
} __attribute__((packed)); // Ensure that GCC doesn't expand the flag byte to a 32-bit word for alignment issues
enum { CAPACITY_MAX = 65535 }; // If typeof(cap) changed from uint16_t, be sure to update this enum to the max value storable in the type
union {
@ -300,25 +305,47 @@ class String {
struct _sso sso;
};
// Accessor functions
inline bool isSSO() const { return sso.isSSO; }
inline unsigned int len() const { return isSSO() ? sso.len : ptr.len; }
inline unsigned int capacity() const { return isSSO() ? (unsigned int)SSOSIZE - 1 : ptr.cap; } // Size of max string not including terminal NUL
inline void setSSO(bool set) { sso.isSSO = set; }
inline void setLen(int len) { if (isSSO()) sso.len = len; else ptr.len = len; }
inline void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; }
inline void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
bool isSSO() const { return !sso.isHeap; }
unsigned int len() const { return isSSO() ? sso.len : ptr.len; }
unsigned int capacity() const { return isSSO() ? (unsigned int)SSOSIZE - 1 : ptr.cap; } // Size of max string not including terminal NUL
void setSSO(bool set) { sso.isHeap = !set; }
void setLen(int len) {
if (isSSO()) {
setSSO(true); // Avoid emitting of bitwise EXTRACT-AND-OR ops (store-merging optimization)
sso.len = len;
} else
ptr.len = len;
}
void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; }
void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
// Buffer accessor functions
inline const char *buffer() const { return (const char *)(isSSO() ? sso.buff : ptr.buff); }
inline char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
const char *buffer() const { return wbuffer(); }
char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
protected:
void init(void);
void init(void) __attribute__((always_inline)) {
sso.buff[0] = 0;
sso.len = 0;
sso.isHeap = 0;
// Without the 6 statements shown below, GCC simply emits such as: "MOVI.N aX,0", "S8I aX,a2,0" and "S8I aX,a2,11" (8 bytes in total)
sso.buff[1] = 0;
sso.buff[2] = 0;
sso.buff[3] = 0;
sso.buff[8] = 0;
sso.buff[9] = 0;
sso.buff[10] = 0;
// With the above, thanks to store-merging, GCC can use the narrow form of 32-bit store insn ("S32I.N") and emits:
// "MOVI.N aX,0", "S32I.N aX,a2,0" and "S32I.N aX,a2,8" (6 bytes in total)
// (Literature: Xtensa(R) Instruction Set Reference Manual, "S8I - Store 8-bit" [p.504] and "S32I.N - Narrow Store 32-bit" [p.512])
// Unfortunately, GCC seems not to re-evaluate the cost of inlining after the store-merging optimizer stage,
// `always_inline` attribute is necessary in order to keep inlining.
}
void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen);
// copy and move
String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *pstr, unsigned int length);
String &copy(const char *cstr, unsigned int length);
String &copy(const __FlashStringHelper *pstr, unsigned int length);
void move(String &rhs) noexcept;
};
@ -354,6 +381,9 @@ class StringSumHelper: public String {
StringSumHelper(double num) :
String(num) {
}
StringSumHelper(const __FlashStringHelper *s) :
String(s) {
}
};
extern const String emptyString;