Skip to content

Commit 2e09d34

Browse files
Add host test, make SSO 8 bytes, remove magic num
Memory fragmentation is worse than not saving any RAM per String instance, so extend the SSO size to 8 bytes (7 chars + '\0'), and making the total String size the same size as it was before. Remove any hardcoded SSO size and allow configuration via an enum, and replace the magic max-capacity with an enum tor clarity. Add a host test that verifies that no memory is allocated until a full 8 characters are assigned to a string, as well as checking all intermediate values.
1 parent c0a6b27 commit 2e09d34

File tree

3 files changed

+52
-13
lines changed

3 files changed

+52
-13
lines changed

cores/esp8266/WString.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,20 +153,16 @@ unsigned char String::reserve(unsigned int size) {
153153
}
154154

155155
unsigned char String::changeBuffer(unsigned int maxStrLen) {
156-
if (maxStrLen > 0x7fff) {
157-
// Sanity check len can fit in 15-bits
158-
return 0;
159-
}
160156
// Can we use SSO here to avoid allocation?
161-
if (maxStrLen < 4) {
157+
if (maxStrLen < sizeof(sso_buff)) {
162158
if (sso || !bufptr) {
163159
// Already using SSO, nothing to do but set xap properly
164160
sso = true;
165161
capacity = sizeof(sso_buff) - 1; // We subtract off trailing 0 space from available
166162
return 1;
167163
} else { // if bufptr
168164
// Using bufptr, need to shrink into sso_buff
169-
char temp[4];
165+
char temp[sizeof(sso_buff)];
170166
memcpy(temp, buffer(), maxStrLen);
171167
free(bufptr);
172168
sso = true;
@@ -177,18 +173,22 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
177173
}
178174
// Fallthrough to normal allocator
179175
size_t newSize = (maxStrLen + 16) & (~0xf);
180-
char temp[4];
176+
// Make sure we can fit newsize in the buffer
177+
if (newSize > CAPACITY_MAX) {
178+
return false;
179+
}
180+
char temp[sizeof(sso_buff)];
181181
if (buffer()) {
182-
memcpy(temp, buffer(), 4); // Just in case SSO was on
182+
memcpy(temp, buffer(), sizeof(sso_buff)); // Just in case SSO was on
183183
} else {
184-
memset(temp, 0, 0);
184+
memset(temp, 0, sizeof(sso_buff));
185185
}
186186
char *newbuffer = (char *) realloc(sso ? nullptr : bufptr, newSize);
187187
if(newbuffer) {
188188
size_t oldSize = capacity + 1; // include NULL.
189189
if (sso) {
190190
// Copy the SSO buffer into allocated space
191-
memcpy(newbuffer, temp, 4);
191+
memcpy(newbuffer, temp, sizeof(sso_buff));
192192
sso = false;
193193
}
194194
if (newSize > oldSize)

cores/esp8266/WString.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,14 +242,17 @@ class String {
242242
long toInt(void) const;
243243
float toFloat(void) const;
244244

245+
enum { SSOSIZE = 8 }; // Characters to allocate space for SSO
246+
245247
protected:
246248
union {
247-
char *bufptr; // the actual char array
248-
char sso_buff[4]; // Overwrite the ptr with actual string for < 4 chars
249+
char *bufptr; // the actual char array
250+
char sso_buff[SSOSIZE]; // Overwrite the ptr with actual string for < SSOSIZE chars
249251
};
250252
unsigned sso : 1;
253+
enum { CAPACITY_MAX = 32767 }; // If size of capacity changed, be sure to update this enum
251254
unsigned capacity : 15; // the array length minus one (for the '\0')
252-
unsigned unused : 1;
255+
unsigned unused : 1;
253256
unsigned len : 15; // the String length (not counting the '\0')
254257
// Buffer accessor functions
255258
inline const char *buffer() const { return (const char *)(sso ? sso_buff : bufptr); }

tests/host/core/test_string.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,39 @@ TEST_CASE("String sizes near 8b", "[core][String]")
268268
REQUIRE(!strcmp(s16.c_str(),"123456789012345_"));
269269
REQUIRE(!strcmp(s17.c_str(),"1234567890123456_"));
270270
}
271+
272+
TEST_CASE("String SSO works", "[core][String]")
273+
{
274+
// This test assumes that SSO_SIZE==8, if that changes the test must as well
275+
String s;
276+
s += "0";
277+
REQUIRE(s == "0");
278+
const char *savesso = s.c_str();
279+
s += 1;
280+
REQUIRE(s.c_str() == savesso);
281+
REQUIRE(s == "01");
282+
s += "2";
283+
REQUIRE(s.c_str() == savesso);
284+
REQUIRE(s == "012");
285+
s += 3;
286+
REQUIRE(s.c_str() == savesso);
287+
REQUIRE(s == "0123");
288+
s += "4";
289+
REQUIRE(s.c_str() == savesso);
290+
REQUIRE(s == "01234");
291+
s += "5";
292+
REQUIRE(s.c_str() == savesso);
293+
REQUIRE(s == "012345");
294+
s += "6";
295+
REQUIRE(s.c_str() == savesso);
296+
REQUIRE(s == "0123456");
297+
s += "7";
298+
REQUIRE(s.c_str() != savesso);
299+
REQUIRE(s == "01234567");
300+
s += "8";
301+
REQUIRE(s.c_str() != savesso);
302+
REQUIRE(s == "012345678");
303+
s += "9";
304+
REQUIRE(s.c_str() != savesso);
305+
REQUIRE(s == "0123456789");
306+
}

0 commit comments

Comments
 (0)