From f2c75658cef59a28011a983211f87db58f10549c Mon Sep 17 00:00:00 2001 From: Takayuki 'January June' Suwa Date: Wed, 9 Sep 2020 14:08:09 +0900 Subject: [PATCH 1/3] 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)` IROM size @ FSBrowser: 304916 -> 304372 (saved 544 from master) --- cores/esp8266/WString.cpp | 240 ++++++++++++++------------------------ cores/esp8266/WString.h | 206 +++++++++++++++++--------------- 2 files changed, 200 insertions(+), 246 deletions(-) diff --git a/cores/esp8266/WString.cpp b/cores/esp8266/WString.cpp index 3dda69bda4..8eaa88d91d 100644 --- a/cores/esp8266/WString.cpp +++ b/cores/esp8266/WString.cpp @@ -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 { - if (this != &rval) - move(rval); return *this; } -String & String::operator =(StringSumHelper &&rval) noexcept { +String &String::operator =(String &&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(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, const String &rhs) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(rhs.buffer(), rhs.len())) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, const char *cstr) { + StringSumHelper &a = const_cast(lhs); if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, char c) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, char c) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(c)) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, unsigned char num) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(num)) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, int num) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, int num) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(num)) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, unsigned int num) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(num)) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, long num) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, long num) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(num)) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long num) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(num)) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, float num) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, float num) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(num)) a.invalidate(); return a; } -StringSumHelper & operator +(const StringSumHelper &lhs, double num) { - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, double num) { + StringSumHelper &a = const_cast(lhs); if (!a.concat(num)) a.invalidate(); return a; } -StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs) -{ - StringSumHelper &a = const_cast(lhs); +StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs) { + StringSumHelper &a = const_cast(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; diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index 262cfda0a9..a4398c5673 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -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; - } - } - inline void clear(void) { + unsigned int length(void) const { + return buffer() ? len() : 0; + } + 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,11 +283,11 @@ 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 - struct _ptr { + struct _ptr { char * buff; uint16_t cap; uint16_t len; @@ -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,35 @@ 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(sso.buff) : ptr.buff; } // Writable version of buffer + const char *buffer() const { return wbuffer(); } + char *wbuffer() const { return isSSO() ? const_cast(sso.buff) : ptr.buff; } // Writable version of buffer protected: - void init(void); + void init(void) { + sso.buff[0] = 0; + sso.len = 0; + sso.isHeap = 0; + } 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 ©(const char *cstr, unsigned int length); + String ©(const __FlashStringHelper *pstr, unsigned int length); void move(String &rhs) noexcept; }; @@ -354,6 +369,9 @@ class StringSumHelper: public String { StringSumHelper(double num) : String(num) { } + StringSumHelper(const __FlashStringHelper *s) : + String(s) { + } }; extern const String emptyString; From 6e76a3a97a38f72426dfe8f220da0ed90b58445e Mon Sep 17 00:00:00 2001 From: Takayuki 'January June' Suwa Date: Sun, 15 Nov 2020 20:36:28 +0900 Subject: [PATCH 2/3] * optimize `init()` more IROM size @ FSBrowser: 304372 -> 304356 (saved 560 from master) --- cores/esp8266/WString.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index a4398c5673..eb1f123686 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -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); @@ -323,10 +323,16 @@ class String { char *wbuffer() const { return isSSO() ? const_cast(sso.buff) : ptr.buff; } // Writable version of buffer protected: - void init(void) { - sso.buff[0] = 0; - sso.len = 0; - sso.isHeap = 0; + void init(void) __attribute__((always_inline)) { + sso.buff[0] = 0; // In the Xtensa ISA, in fact, 32-bit store insn ("S32I.N") is one-byte shorter than 8-bit one ("S8I"). + sso.buff[1] = 0; // Thanks to store-merging optimization, these 9 lines emit only 3 insns: + sso.buff[2] = 0; // "MOVI.N aX,0", "S32I.N aX,a2,0" and "S32I.N aX,a2,8" (6 bytes in total) + sso.buff[3] = 0; + sso.buff[8] = 0; // Unfortunately, GCC seems not to re-evaluate the cost of inlining after the store-merging optimizer stage, + sso.buff[9] = 0; // `always_inline` attribute is necessary in order to assure inlining. + sso.buff[10] = 0; + sso.len = 0; + sso.isHeap = 0; } void invalidate(void); unsigned char changeBuffer(unsigned int maxStrLen); From ebe4ceb0b102545409eb220cb102a2d630280a20 Mon Sep 17 00:00:00 2001 From: Takayuki 'January June' Suwa Date: Mon, 16 Nov 2020 04:45:47 +0900 Subject: [PATCH 3/3] * revise the comment --- cores/esp8266/WString.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index eb1f123686..dcb0982387 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -324,15 +324,21 @@ class String { protected: void init(void) __attribute__((always_inline)) { - sso.buff[0] = 0; // In the Xtensa ISA, in fact, 32-bit store insn ("S32I.N") is one-byte shorter than 8-bit one ("S8I"). - sso.buff[1] = 0; // Thanks to store-merging optimization, these 9 lines emit only 3 insns: - sso.buff[2] = 0; // "MOVI.N aX,0", "S32I.N aX,a2,0" and "S32I.N aX,a2,8" (6 bytes in total) + 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; // Unfortunately, GCC seems not to re-evaluate the cost of inlining after the store-merging optimizer stage, - sso.buff[9] = 0; // `always_inline` attribute is necessary in order to assure inlining. + sso.buff[8] = 0; + sso.buff[9] = 0; sso.buff[10] = 0; - sso.len = 0; - sso.isHeap = 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);