Skip to content

Commit fd089d8

Browse files
earlephilhowerme-no-dev
authored andcommitted
Pull in ESP8266 String::replace() fixes, others (#3143)
Pull in bugfixes from the ESP8266 repo for problems in the SSO implementation of replace(). See the following patches for full details: esp8266/Arduino@54240d2#diff-8d9e71e16d437343017df828f0528f63 esp8266/Arduino@78a1a66#diff-8d9e71e16d437343017df828f0528f63 esp8266/Arduino@4e93584#diff-8d9e71e16d437343017df828f0528f63 Fixes #3140
1 parent f356ccd commit fd089d8

File tree

2 files changed

+55
-38
lines changed

2 files changed

+55
-38
lines changed

cores/esp32/WString.cpp

+29-20
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
String::String(const char *cstr) {
3333
init();
34-
if(cstr)
34+
if (cstr)
3535
copy(cstr, strlen(cstr));
3636
}
3737

@@ -136,7 +136,7 @@ inline void String::init(void) {
136136
}
137137

138138
void String::invalidate(void) {
139-
if(!sso() && wbuffer())
139+
if(!isSSO() && wbuffer())
140140
free(wbuffer());
141141
init();
142142
}
@@ -154,17 +154,21 @@ unsigned char String::reserve(unsigned int size) {
154154

155155
unsigned char String::changeBuffer(unsigned int maxStrLen) {
156156
// Can we use SSO here to avoid allocation?
157-
if (maxStrLen < sizeof(sso_buf)) {
158-
if (sso() || !buffer()) {
157+
if (maxStrLen < sizeof(sso.buff) - 1) {
158+
if (isSSO() || !buffer()) {
159159
// Already using SSO, nothing to do
160+
uint16_t oldLen = len();
160161
setSSO(true);
162+
setLen(oldLen);
161163
return 1;
162-
} else { // if bufptr && !sso()
163-
// Using bufptr, need to shrink into sso_buff
164-
char temp[sizeof(sso_buf)];
164+
} else { // if bufptr && !isSSO()
165+
// Using bufptr, need to shrink into sso.buff
166+
char temp[sizeof(sso.buff)];
165167
memcpy(temp, buffer(), maxStrLen);
166168
free(wbuffer());
169+
uint16_t oldLen = len();
167170
setSSO(true);
171+
setLen(oldLen);
168172
memcpy(wbuffer(), temp, maxStrLen);
169173
return 1;
170174
}
@@ -176,12 +180,12 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
176180
return false;
177181
}
178182
uint16_t oldLen = len();
179-
char *newbuffer = (char *) realloc(sso() ? nullptr : wbuffer(), newSize);
180-
if(newbuffer) {
183+
char *newbuffer = (char *) realloc(isSSO() ? nullptr : wbuffer(), newSize);
184+
if (newbuffer) {
181185
size_t oldSize = capacity() + 1; // include NULL.
182-
if (sso()) {
186+
if (isSSO()) {
183187
// Copy the SSO buffer into allocated space
184-
memcpy(newbuffer, sso_buf, sizeof(sso_buf));
188+
memmove(newbuffer, sso.buff, sizeof(sso.buff));
185189
}
186190
if (newSize > oldSize)
187191
{
@@ -206,7 +210,7 @@ String & String::copy(const char *cstr, unsigned int length) {
206210
return *this;
207211
}
208212
setLen(length);
209-
strcpy(wbuffer(), cstr);
213+
memmove(wbuffer(), cstr, length + 1);
210214
return *this;
211215
}
212216

@@ -216,28 +220,28 @@ String & String::copy(const __FlashStringHelper *pstr, unsigned int length) {
216220
return *this;
217221
}
218222
setLen(length);
219-
strcpy_P(wbuffer(), (PGM_P)pstr);
223+
memcpy_P(wbuffer(), (PGM_P)pstr, length + 1); // We know wbuffer() cannot ever be in PROGMEM, so memcpy safe here
220224
return *this;
221225
}
222226

223227
#ifdef __GXX_EXPERIMENTAL_CXX0X__
224228
void String::move(String &rhs) {
225229
if(buffer()) {
226230
if(capacity() >= rhs.len()) {
227-
strcpy(wbuffer(), rhs.buffer());
231+
memmove(wbuffer(), rhs.buffer(), rhs.length() + 1);
228232
setLen(rhs.len());
229233
rhs.invalidate();
230234
return;
231235
} else {
232-
if (!sso()) {
236+
if (!isSSO()) {
233237
free(wbuffer());
234238
setBuffer(nullptr);
235239
}
236240
}
237241
}
238-
if (rhs.sso()) {
242+
if (rhs.isSSO()) {
239243
setSSO(true);
240-
memmove(sso_buf, rhs.sso_buf, sizeof(sso_buf));
244+
memmove(sso.buff, rhs.sso.buff, sizeof(sso.buff));
241245
} else {
242246
setSSO(false);
243247
setBuffer(rhs.wbuffer());
@@ -309,7 +313,7 @@ unsigned char String::concat(const String &s) {
309313
return 1;
310314
if (!reserve(newlen))
311315
return 0;
312-
memcpy(wbuffer() + len(), buffer(), len());
316+
memmove(wbuffer() + len(), buffer(), len());
313317
setLen(newlen);
314318
wbuffer()[len()] = 0;
315319
return 1;
@@ -326,7 +330,12 @@ unsigned char String::concat(const char *cstr, unsigned int length) {
326330
return 1;
327331
if(!reserve(newlen))
328332
return 0;
329-
strcpy(wbuffer() + len(), cstr);
333+
if (cstr >= wbuffer() && cstr < wbuffer() + len())
334+
// compatible with SSO in ram #6155 (case "x += x.c_str()")
335+
memmove(wbuffer() + len(), cstr, length + 1);
336+
else
337+
// compatible with source in flash #6367
338+
memcpy_P(wbuffer() + len(), cstr, length + 1);
330339
setLen(newlen);
331340
return 1;
332341
}
@@ -392,7 +401,7 @@ unsigned char String::concat(const __FlashStringHelper * str) {
392401
if (length == 0) return 1;
393402
unsigned int newlen = len() + length;
394403
if (!reserve(newlen)) return 0;
395-
strcpy_P(wbuffer() + len(), (PGM_P)str);
404+
memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1);
396405
setLen(newlen);
397406
return 1;
398407
}

cores/esp32/WString.h

+26-18
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ class String {
8383
return 0;
8484
}
8585
}
86+
inline void clear(void) {
87+
setLen(0);
88+
}
89+
inline bool isEmpty(void) const {
90+
return length() == 0;
91+
}
8692

8793
// creates a copy of the assigned value. if the value is null or
8894
// invalid, or if the memory allocation fails, the string will be
@@ -99,7 +105,7 @@ class String {
99105

100106
// returns true on success, false on failure (in which case, the string
101107
// is left unchanged). if the argument is null or invalid, the
102-
// concatenation is considered unsucessful.
108+
// concatenation is considered unsuccessful.
103109
unsigned char concat(const String &str);
104110
unsigned char concat(const char *cstr);
105111
unsigned char concat(char c);
@@ -200,7 +206,7 @@ class String {
200206
unsigned char startsWith(const String &prefix, unsigned int offset) const;
201207
unsigned char endsWith(const String &suffix) const;
202208

203-
// character acccess
209+
// character access
204210
char charAt(unsigned int index) const;
205211
void setCharAt(unsigned int index, char c);
206212
char operator [](unsigned int index) const;
@@ -251,27 +257,29 @@ class String {
251257
uint16_t cap;
252258
uint16_t len;
253259
};
254-
255-
// SSO is handled by checking the last byte of sso_buff.
256-
// When not in SSO mode, that byte is set to 0xff, while when in SSO mode it is always 0x00 (so it can serve as the string terminator as well as a flag)
257-
// This allows strings up up to 12 (11 + \0 termination) without any extra space.
258-
enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more
259-
enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum
260+
// This allows strings up up to 11 (10 + \0 termination) without any extra space.
261+
enum { SSOSIZE = sizeof(struct _ptr) + 4 - 1 }; // Characters to allocate space for SSO, must be 12 or more
262+
struct _sso {
263+
char buff[SSOSIZE];
264+
unsigned char len : 7; // Ensure only one byte is allocated by GCC for the bitfields
265+
unsigned char isSSO : 1;
266+
} __attribute__((packed)); // Ensure that GCC doesn't expand the flag byte to a 32-bit word for alignment issues
267+
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
260268
union {
261269
struct _ptr ptr;
262-
char sso_buf[SSOSIZE];
270+
struct _sso sso;
263271
};
264272
// Accessor functions
265-
inline bool sso() const { return sso_buf[SSOSIZE - 1] == 0; }
266-
inline unsigned int len() const { return sso() ? strlen(sso_buf) : ptr.len; }
267-
inline unsigned int capacity() const { return sso() ? SSOSIZE - 1 : ptr.cap; }
268-
inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; }
269-
inline void setLen(int len) { if (!sso()) ptr.len = len; }
270-
inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; }
271-
inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; }
273+
inline bool isSSO() const { return sso.isSSO; }
274+
inline unsigned int len() const { return isSSO() ? sso.len : ptr.len; }
275+
inline unsigned int capacity() const { return isSSO() ? (unsigned int)SSOSIZE - 1 : ptr.cap; } // Size of max string not including terminal NUL
276+
inline void setSSO(bool set) { sso.isSSO = set; }
277+
inline void setLen(int len) { if (isSSO()) sso.len = len; else ptr.len = len; }
278+
inline void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; }
279+
inline void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
272280
// Buffer accessor functions
273-
inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buff); }
274-
inline char *wbuffer() const { return sso() ? const_cast<char *>(sso_buf) : ptr.buff; } // Writable version of buffer
281+
inline const char *buffer() const { return (const char *)(isSSO() ? sso.buff : ptr.buff); }
282+
inline char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
275283

276284
protected:
277285
void init(void);

0 commit comments

Comments
 (0)