Skip to content

Commit

Permalink
[WString] Reduce build size by implementing flash string calls in .cpp (
Browse files Browse the repository at this point in the history
#8106)

A function called with a flash string, which only has an implementation with `const String&` as argument will be compiled as if it is called with a `String` constructor wrapped around it.

For example this implementation in the .h file:
```c++
bool startsWith(const __FlashStringHelper *prefix) const {
            return this->startsWith(String(prefix));
}
```

This is completely useless as the compiler will generate exactly the same code with or without this function implementation in the .h file.
However if we move the implementation to the .cpp file, this conversion to `String` is only added once in the compiled binary.
In my own project I already managed to shrink the largest (ESP32) build by more than 70k in size (!!) by just adding extra function calls with the conversion in the .cpp file.
This PR is just a simple optimisation which already shrinks a very small build of my project by almost 3k in build size.  (custom_beta_ESP8266_4M1M PIO env of ESPEasy)

```
Flash: [========  ]  82.5% (used 862137 bytes from 1044464 bytes)
Flash: [========  ]  82.3% (used 859545 bytes from 1044464 bytes)
```

Larger builds may benefit even more.
  • Loading branch information
TD-er committed Sep 4, 2021
1 parent 140d0ff commit 211606f
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 32 deletions.
61 changes: 61 additions & 0 deletions cores/esp8266/WString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ String &String::operator =(const __FlashStringHelper *pstr) {
return *this;
}

String &String::operator =(char c) {
char buffer[2] { c, '\0' };
*this = buffer;
return *this;
}


/*********************************************/
/* concat */
/*********************************************/
Expand Down Expand Up @@ -500,6 +507,10 @@ bool String::equals(const char *cstr) const {
return strcmp(buffer(), cstr) == 0;
}

bool String::equals(const __FlashStringHelper *s) const {
return equals(String(s));
}

bool String::operator<(const String &rhs) const {
return compareTo(rhs) < 0;
}
Expand Down Expand Up @@ -532,6 +543,10 @@ bool String::equalsIgnoreCase(const String &s2) const {
return true;
}

bool String::equalsIgnoreCase(const __FlashStringHelper *s) const {
return equalsIgnoreCase(String(s));
}

unsigned char String::equalsConstantTime(const String &s2) const {
// To avoid possible time-based attacks present function
// compares given strings in a constant time.
Expand Down Expand Up @@ -565,18 +580,37 @@ bool String::startsWith(const String &s2) const {
return startsWith(s2, 0);
}

bool String::startsWith(const char *prefix) const {
return this->startsWith(String(prefix));
}
bool String::startsWith(const __FlashStringHelper *prefix) const {
return this->startsWith(String(prefix));
}

bool String::startsWith(const String &s2, unsigned int offset) const {
if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer())
return false;
return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0;
}

bool String::startsWith(const __FlashStringHelper *prefix, unsigned int offset) const {
return startsWith(String(prefix), offset);
}

bool String::endsWith(const String &s2) const {
if (len() < s2.len() || !buffer() || !s2.buffer())
return false;
return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0;
}

bool String::endsWith(const char *suffix) const {
return this->endsWith(String(suffix));
}
bool String::endsWith(const __FlashStringHelper *suffix) const {
return this->endsWith(String(suffix));
}


/*********************************************/
/* Character Access */
/*********************************************/
Expand Down Expand Up @@ -678,6 +712,15 @@ int String::lastIndexOf(const String &s2, unsigned int fromIndex) const {
return found;
}

int String::lastIndexOf(const __FlashStringHelper *str) const {
return lastIndexOf(String(str));
}

int String::lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const {
return lastIndexOf(String(str), fromIndex);
}


String String::substring(unsigned int left, unsigned int right) const {
if (left > right) {
unsigned int temp = right;
Expand Down Expand Up @@ -756,6 +799,24 @@ void String::replace(const String &find, const String &replace) {
}
}


void String::replace(const char *find, const String &replace) {
this->replace(String(find), replace);
}
void String::replace(const __FlashStringHelper *find, const String &replace) {
this->replace(String(find), replace);
}
void String::replace(const char *find, const char *replace) {
this->replace(String(find), String(replace));
}
void String::replace(const __FlashStringHelper *find, const char *replace) {
this->replace(String(find), String(replace));
}
void String::replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
this->replace(String(find), String(replace));
}


void String::remove(unsigned int index, unsigned int count) {
if (index >= len()) {
return;
Expand Down
54 changes: 22 additions & 32 deletions cores/esp8266/WString.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,7 @@ class String {
String &operator =(const char *cstr);
String &operator =(const __FlashStringHelper *str);
String &operator =(String &&rval) noexcept;
String &operator =(char c) {
char buffer[2] { c, '\0' };
*this = buffer;
return *this;
}
String &operator =(char c);

// concatenate (works w/ built-in types)

Expand Down Expand Up @@ -142,39 +138,40 @@ class String {
int compareTo(const String &s) const;
bool equals(const String &s) const;
bool equals(const char *cstr) const;
bool equals(const __FlashStringHelper *s) const;
bool operator ==(const String &rhs) const {
return equals(rhs);
}
bool operator ==(const char *cstr) const {
return equals(cstr);
}
bool operator ==(const __FlashStringHelper *rhs) const {
return equals(rhs);
}
bool operator !=(const String &rhs) const {
return !equals(rhs);
}
bool operator !=(const char *cstr) const {
return !equals(cstr);
}
bool operator !=(const __FlashStringHelper *rhs) const {
return !equals(rhs);
}
bool operator <(const String &rhs) const;
bool operator >(const String &rhs) const;
bool operator <=(const String &rhs) const;
bool operator >=(const String &rhs) const;
bool equalsIgnoreCase(const String &s) const;
bool equalsIgnoreCase(const __FlashStringHelper *s) const;
unsigned char equalsConstantTime(const String &s) const;
bool startsWith(const String &prefix) const;
bool startsWith(const char *prefix) const {
return this->startsWith(String(prefix));
}
bool startsWith(const __FlashStringHelper *prefix) const {
return this->startsWith(String(prefix));
}
bool startsWith(const char *prefix) const;
bool startsWith(const __FlashStringHelper *prefix) const;
bool startsWith(const String &prefix, unsigned int offset) const;
bool startsWith(const __FlashStringHelper *prefix, unsigned int offset) const;
bool endsWith(const String &suffix) const;
bool endsWith(const char *suffix) const {
return this->endsWith(String(suffix));
}
bool endsWith(const __FlashStringHelper *suffix) const {
return this->endsWith(String(suffix));
}
bool endsWith(const char *suffix) const;
bool endsWith(const __FlashStringHelper *suffix) const;

// character access
char charAt(unsigned int index) const {
Expand Down Expand Up @@ -204,6 +201,8 @@ class String {
int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const;
int lastIndexOf(const String &str, unsigned int fromIndex) const;
int lastIndexOf(const __FlashStringHelper *str) const;
int lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const;
String substring(unsigned int beginIndex) const {
return substring(beginIndex, len());
}
Expand All @@ -212,21 +211,12 @@ class String {
// modification
void replace(char find, char 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) {
this->replace(String(find), replace);
}
void replace(const char *find, const char *replace) {
this->replace(String(find), String(replace));
}
void replace(const __FlashStringHelper *find, const char *replace) {
this->replace(String(find), String(replace));
}
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
this->replace(String(find), String(replace));
}
void replace(const char *find, const String &replace);
void replace(const __FlashStringHelper *find, const String &replace);
void replace(const char *find, const char *replace);
void replace(const __FlashStringHelper *find, const char *replace);
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace);

// 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);
Expand Down

0 comments on commit 211606f

Please sign in to comment.