-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Commit
Supercedes #6027 Make SSO more generic by keeping track of its length explicitly, allowing for embedded \0s to exist in the String (just like the non-SSO ones). Use memmove/memcpy_P when we know the length of a string to save CPU time. Add tests to inject \0s in a String to ensure it is still working as designed.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ | |
|
||
String::String(const char *cstr) { | ||
init(); | ||
if(cstr) | ||
if (cstr) | ||
copy(cstr, strlen(cstr)); | ||
} | ||
|
||
|
@@ -136,7 +136,7 @@ inline void String::init(void) { | |
} | ||
|
||
void String::invalidate(void) { | ||
if(!sso() && wbuffer()) | ||
if(!isSSO() && wbuffer()) | ||
free(wbuffer()); | ||
init(); | ||
} | ||
|
@@ -154,17 +154,21 @@ unsigned char String::reserve(unsigned int size) { | |
|
||
unsigned char String::changeBuffer(unsigned int maxStrLen) { | ||
// Can we use SSO here to avoid allocation? | ||
if (maxStrLen < sizeof(sso_buf)) { | ||
if (sso() || !buffer()) { | ||
if (maxStrLen < sizeof(sso.buff) - 1) { | ||
if (isSSO() || !buffer()) { | ||
// Already using SSO, nothing to do | ||
uint16_t oldLen = len(); | ||
setSSO(true); | ||
setLen(oldLen); | ||
return 1; | ||
} else { // if bufptr && !sso() | ||
// Using bufptr, need to shrink into sso_buff | ||
char temp[sizeof(sso_buf)]; | ||
} else { // if bufptr && !isSSO() | ||
// Using bufptr, need to shrink into sso.buff | ||
char temp[sizeof(sso.buff)]; | ||
memcpy(temp, buffer(), maxStrLen); | ||
free(wbuffer()); | ||
uint16_t oldLen = len(); | ||
setSSO(true); | ||
setLen(oldLen); | ||
memcpy(wbuffer(), temp, maxStrLen); | ||
return 1; | ||
} | ||
|
@@ -176,12 +180,12 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) { | |
return false; | ||
} | ||
uint16_t oldLen = len(); | ||
char *newbuffer = (char *) realloc(sso() ? nullptr : wbuffer(), newSize); | ||
if(newbuffer) { | ||
char *newbuffer = (char *) realloc(isSSO() ? nullptr : wbuffer(), newSize); | ||
if (newbuffer) { | ||
size_t oldSize = capacity() + 1; // include NULL. | ||
if (sso()) { | ||
if (isSSO()) { | ||
// Copy the SSO buffer into allocated space | ||
memcpy(newbuffer, sso_buf, sizeof(sso_buf)); | ||
memmove(newbuffer, sso.buff, sizeof(sso.buff)); | ||
} | ||
if (newSize > oldSize) | ||
{ | ||
|
@@ -206,7 +210,7 @@ String & String::copy(const char *cstr, unsigned int length) { | |
return *this; | ||
} | ||
setLen(length); | ||
strcpy(wbuffer(), cstr); | ||
memmove(wbuffer(), cstr, length + 1); | ||
return *this; | ||
} | ||
|
||
|
@@ -216,28 +220,28 @@ String & String::copy(const __FlashStringHelper *pstr, unsigned int length) { | |
return *this; | ||
} | ||
setLen(length); | ||
strcpy_P(wbuffer(), (PGM_P)pstr); | ||
memcpy_P(wbuffer(), (PGM_P)pstr, length + 1); // We know wbuffer() cannot ever be in PROGMEM, so memcpy safe here | ||
return *this; | ||
} | ||
|
||
#ifdef __GXX_EXPERIMENTAL_CXX0X__ | ||
void String::move(String &rhs) { | ||
if(buffer()) { | ||
if(capacity() >= rhs.len()) { | ||
strcpy(wbuffer(), rhs.buffer()); | ||
memmove(wbuffer(), rhs.buffer(), rhs.length() + 1); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
earlephilhower
Author
Collaborator
|
||
setLen(rhs.len()); | ||
rhs.invalidate(); | ||
return; | ||
} else { | ||
if (!sso()) { | ||
if (!isSSO()) { | ||
free(wbuffer()); | ||
setBuffer(nullptr); | ||
} | ||
} | ||
} | ||
if (rhs.sso()) { | ||
if (rhs.isSSO()) { | ||
setSSO(true); | ||
memmove(sso_buf, rhs.sso_buf, sizeof(sso_buf)); | ||
memmove(sso.buff, rhs.sso.buff, sizeof(sso.buff)); | ||
} else { | ||
setSSO(false); | ||
setBuffer(rhs.wbuffer()); | ||
|
@@ -309,7 +313,7 @@ unsigned char String::concat(const String &s) { | |
return 1; | ||
if (!reserve(newlen)) | ||
return 0; | ||
memcpy(wbuffer() + len(), buffer(), len()); | ||
memmove(wbuffer() + len(), buffer(), len()); | ||
setLen(newlen); | ||
wbuffer()[len()] = 0; | ||
return 1; | ||
|
@@ -326,7 +330,7 @@ unsigned char String::concat(const char *cstr, unsigned int length) { | |
return 1; | ||
if(!reserve(newlen)) | ||
return 0; | ||
strcpy(wbuffer() + len(), cstr); | ||
memmove(wbuffer() + len(), cstr, length + 1); | ||
setLen(newlen); | ||
return 1; | ||
} | ||
|
@@ -392,7 +396,7 @@ unsigned char String::concat(const __FlashStringHelper * str) { | |
if (length == 0) return 1; | ||
unsigned int newlen = len() + length; | ||
if (!reserve(newlen)) return 0; | ||
strcpy_P(wbuffer() + len(), (PGM_P)str); | ||
memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1); | ||
setLen(newlen); | ||
return 1; | ||
} | ||
|
Just browsing through this code and this +1 looks a bit dangerous with the
>=
compare.I know it is past the time to be sharp and clear headed here, and the empty beer bottle(s) next to me will support that idea. But are you sure it is correct behavior?