Skip to content

Commit 3c0d387

Browse files
Make SSO support embedded \0s like native String
Supercedes esp8266#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).
1 parent d83eabe commit 3c0d387

File tree

3 files changed

+42
-34
lines changed

3 files changed

+42
-34
lines changed

cores/esp8266/WString.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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,19 @@ 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
160160
setSSO(true);
161161
return 1;
162-
} else { // if bufptr && !sso()
163-
// Using bufptr, need to shrink into sso_buff
164-
char temp[sizeof(sso_buf)];
162+
} else { // if bufptr && !isSSO()
163+
// Using bufptr, need to shrink into sso.buff
164+
char temp[sizeof(sso.buff)];
165165
memcpy(temp, buffer(), maxStrLen);
166166
free(wbuffer());
167+
uint16_t oldLen = len();
167168
setSSO(true);
169+
setLen(oldLen);
168170
memcpy(wbuffer(), temp, maxStrLen);
169171
return 1;
170172
}
@@ -176,12 +178,12 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
176178
return false;
177179
}
178180
uint16_t oldLen = len();
179-
char *newbuffer = (char *) realloc(sso() ? nullptr : wbuffer(), newSize);
180-
if(newbuffer) {
181+
char *newbuffer = (char *) realloc(isSSO() ? nullptr : wbuffer(), newSize);
182+
if (newbuffer) {
181183
size_t oldSize = capacity() + 1; // include NULL.
182-
if (sso()) {
184+
if (isSSO()) {
183185
// Copy the SSO buffer into allocated space
184-
memcpy(newbuffer, sso_buf, sizeof(sso_buf));
186+
memcpy(newbuffer, sso.buff, sizeof(sso.buff));
185187
}
186188
if (newSize > oldSize)
187189
{
@@ -229,15 +231,15 @@ void String::move(String &rhs) {
229231
rhs.invalidate();
230232
return;
231233
} else {
232-
if (!sso()) {
234+
if (!isSSO()) {
233235
free(wbuffer());
234236
setBuffer(nullptr);
235237
}
236238
}
237239
}
238-
if (rhs.sso()) {
240+
if (rhs.isSSO()) {
239241
setSSO(true);
240-
memmove(sso_buf, rhs.sso_buf, sizeof(sso_buf));
242+
memmove(sso.buff, rhs.sso.buff, sizeof(sso.buff));
241243
} else {
242244
setSSO(false);
243245
setBuffer(rhs.wbuffer());

cores/esp8266/WString.h

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -250,27 +250,29 @@ class String {
250250
uint16_t cap;
251251
uint16_t len;
252252
};
253-
254-
// SSO is handled by checking the last byte of sso_buff.
255-
// 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)
256-
// This allows strings up up to 12 (11 + \0 termination) without any extra space.
257-
enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more
253+
// This allows strings up up to 11 (10 + \0 termination) without any extra space.
254+
enum { SSOSIZE = sizeof(struct _ptr) + 4 - 1 }; // Characters to allocate space for SSO, must be 12 or more
255+
struct _sso {
256+
char buff[SSOSIZE];
257+
unsigned len : 7;
258+
unsigned flag : 1;
259+
};
258260
enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum
259261
union {
260262
struct _ptr ptr;
261-
char sso_buf[SSOSIZE];
263+
struct _sso sso;
262264
};
263265
// Accessor functions
264-
inline bool sso() const { return sso_buf[SSOSIZE - 1] == 0; }
265-
inline unsigned int len() const { return sso() ? strlen(sso_buf) : ptr.len; }
266-
inline unsigned int capacity() const { return sso() ? SSOSIZE - 1 : ptr.cap; }
267-
inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; }
268-
inline void setLen(int len) { if (!sso()) ptr.len = len; }
269-
inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; }
270-
inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; }
266+
inline bool isSSO() const { return sso.flag; }
267+
inline unsigned int len() const { return isSSO() ? sso.len : ptr.len; }
268+
inline unsigned int capacity() const { return isSSO() ? (unsigned int)SSOSIZE - 1 : ptr.cap; }
269+
inline void setSSO(bool set) { sso.flag = set; }
270+
inline void setLen(int len) { if (!isSSO()) ptr.len = len; else sso.len = len; }
271+
inline void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; }
272+
inline void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
271273
// Buffer accessor functions
272-
inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buff); }
273-
inline char *wbuffer() const { return sso() ? const_cast<char *>(sso_buf) : ptr.buff; } // Writable version of buffer
274+
inline const char *buffer() const { return (const char *)(isSSO() ? sso.buff : ptr.buff); }
275+
inline char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
274276

275277
protected:
276278
void init(void);

tests/host/core/test_string.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,11 @@ TEST_CASE("String SSO works", "[core][String]")
320320
REQUIRE(s.c_str() == savesso);
321321
REQUIRE(s == "0123456789");
322322
REQUIRE(s.length() == 10);
323-
s += "a";
324-
REQUIRE(s.c_str() == savesso);
325-
REQUIRE(s == "0123456789a");
326-
REQUIRE(s.length() == 11);
327323
if (sizeof(savesso) == 4) {
324+
s += "a";
325+
REQUIRE(s.c_str() != savesso);
326+
REQUIRE(s == "0123456789a");
327+
REQUIRE(s.length() == 11);
328328
s += "b";
329329
REQUIRE(s.c_str() != savesso);
330330
REQUIRE(s == "0123456789ab");
@@ -334,12 +334,16 @@ TEST_CASE("String SSO works", "[core][String]")
334334
REQUIRE(s == "0123456789abc");
335335
REQUIRE(s.length() == 13);
336336
} else {
337+
s += "a";
338+
REQUIRE(s.c_str() == savesso);
339+
REQUIRE(s == "0123456789a");
340+
REQUIRE(s.length() == 11);
337341
s += "bcde";
338342
REQUIRE(s.c_str() == savesso);
339343
REQUIRE(s == "0123456789abcde");
340344
REQUIRE(s.length() == 15);
341345
s += "fghi";
342-
REQUIRE(s.c_str() == savesso);
346+
REQUIRE(s.c_str() != savesso);
343347
REQUIRE(s == "0123456789abcdefghi");
344348
REQUIRE(s.length() == 19);
345349
s += "j";
@@ -360,7 +364,7 @@ TEST_CASE("String SSO works", "[core][String]")
360364
REQUIRE(s.length() == 23);
361365
s += "nopq";
362366
REQUIRE(s.c_str() != savesso);
363-
REQUIRE(s == "0123456789abcdefghijklmnopq");
367+
REQUIRE(s == "0123456789abcdefghijklmnopq");
364368
REQUIRE(s.length() == 27);
365369
s += "rstu";
366370
REQUIRE(s.c_str() != savesso);

0 commit comments

Comments
 (0)