1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +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)];
@ -118,20 +110,10 @@ 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())
free(wbuffer());
@ -157,24 +139,22 @@ 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);
@ -184,8 +164,7 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
// 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);
@ -230,12 +209,10 @@ void String::move(String &rhs) noexcept {
String &String::operator =(const String &rhs) {
if (this == &rhs)
return *this;
if (rhs.buffer())
copy(rhs.buffer(), rhs.len());
else
invalidate();
return *this;
}
@ -245,26 +222,19 @@ String & String::operator =(String &&rval) noexcept {
return *this;
}
String & String::operator =(StringSumHelper &&rval) noexcept {
if (this != &rval)
move(rval);
return *this;
}
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) {
@ -362,11 +326,14 @@ unsigned char String::concat(double num) {
}
unsigned char String::concat(const __FlashStringHelper *str) {
if (!str) return 0;
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;
@ -446,8 +413,7 @@ StringSumHelper & operator +(const StringSumHelper &lhs, double num) {
return a;
}
StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs)
{
StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(rhs))
a.invalidate();
@ -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;
@ -562,10 +528,6 @@ 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;
@ -604,10 +566,6 @@ 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;
@ -617,18 +575,6 @@ int String::indexOf(char ch, unsigned int fromIndex) const {
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;
}
@ -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,24 +74,22 @@ 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;
}
@ -97,7 +100,9 @@ class String {
String &operator =(const char *cstr);
String &operator =(const __FlashStringHelper *str);
String &operator =(String &&rval) noexcept;
String & operator =(StringSumHelper &&rval) noexcept;
String &operator =(StringSumHelper &&rval) noexcept {
return operator =((String &&)rval);
}
// concatenate (works w/ built-in types)
@ -121,47 +126,47 @@ class String {
// will be left unchanged (but this isn't signalled in any way)
String &operator +=(const String &rhs) {
concat(rhs);
return (*this);
return *this;
}
String &operator +=(const char *cstr) {
concat(cstr);
return (*this);
return *this;
}
String &operator +=(char c) {
concat(c);
return (*this);
return *this;
}
String &operator +=(unsigned char num) {
concat(num);
return (*this);
return *this;
}
String &operator +=(int num) {
concat(num);
return (*this);
return *this;
}
String &operator +=(unsigned int num) {
concat(num);
return (*this);
return *this;
}
String &operator +=(long num) {
concat(num);
return (*this);
return *this;
}
String &operator +=(unsigned long num) {
concat(num);
return (*this);
return *this;
}
String &operator +=(float num) {
concat(num);
return (*this);
return *this;
}
String &operator +=(double num) {
concat(num);
return (*this);
return *this;
}
String &operator +=(const __FlashStringHelper *str) {
concat(str);
return (*this);
return *this;
}
friend StringSumHelper &operator +(const StringSumHelper &lhs, const String &rhs);
@ -218,7 +223,9 @@ class String {
}
// 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);
@ -233,14 +240,12 @@ class String {
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,7 +253,6 @@ class String {
String substring(unsigned int beginIndex) const {
return substring(beginIndex, len());
}
;
String substring(unsigned int beginIndex, unsigned int endIndex) const;
// modification
@ -269,8 +273,9 @@ class String {
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);
@ -292,7 +297,7 @@ class String {
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 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,19 +305,41 @@ 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);
@ -354,6 +381,9 @@ class StringSumHelper: public String {
StringSumHelper(double num) :
String(num) {
}
StringSumHelper(const __FlashStringHelper *s) :
String(s) {
}
};
extern const String emptyString;