diff --git a/Sming/Arch/Host/Components/libc/memrchr.c b/Sming/Arch/Host/Components/libc/memrchr.c
new file mode 100644
index 0000000000..92835b4659
--- /dev/null
+++ b/Sming/Arch/Host/Components/libc/memrchr.c
@@ -0,0 +1,197 @@
+/* memrchr -- find the last occurrence of a byte in a memory block
+ Copyright (C) 1991-2019 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Based on strlen implementation by Torbjorn Granlund (tege@sics.se),
+ with help from Dan Sahlin (dan@sics.se) and
+ commentary by Jim Blandy (jimb@ai.mit.edu);
+ adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu),
+ and implemented by Roland McGrath (roland@ai.mit.edu).
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ . */
+
+#include
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#if defined _LIBC
+# include
+# include
+#endif
+
+#if defined HAVE_LIMITS_H || defined _LIBC
+# include
+#endif
+
+#define LONG_MAX_32_BITS 2147483647
+
+#ifndef LONG_MAX
+# define LONG_MAX LONG_MAX_32_BITS
+#endif
+
+#include
+
+#undef __memrchr
+#undef memrchr
+
+#ifndef weak_alias
+# define __memrchr memrchr
+#endif
+
+/* Search no more than N bytes of S for C. */
+void *
+#ifndef MEMRCHR
+__memrchr
+#else
+MEMRCHR
+#endif
+ (const void *s, int c_in, size_t n)
+{
+ const unsigned char *char_ptr;
+ const unsigned long int *longword_ptr;
+ unsigned long int longword, magic_bits, charmask;
+ unsigned char c;
+
+ c = (unsigned char) c_in;
+
+ /* Handle the last few characters by reading one character at a time.
+ Do this until CHAR_PTR is aligned on a longword boundary. */
+ for (char_ptr = (const unsigned char *) s + n;
+ n > 0 && ((unsigned long int) char_ptr
+ & (sizeof (longword) - 1)) != 0;
+ --n)
+ if (*--char_ptr == c)
+ return (void *) char_ptr;
+
+ /* All these elucidatory comments refer to 4-byte longwords,
+ but the theory applies equally well to 8-byte longwords. */
+
+ longword_ptr = (const unsigned long int *) char_ptr;
+
+ /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits
+ the "holes." Note that there is a hole just to the left of
+ each byte, with an extra at the end:
+
+ bits: 01111110 11111110 11111110 11111111
+ bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
+
+ The 1-bits make sure that carries propagate to the next 0-bit.
+ The 0-bits provide holes for carries to fall into. */
+ magic_bits = -1;
+ magic_bits = magic_bits / 0xff * 0xfe << 1 >> 1 | 1;
+
+ /* Set up a longword, each of whose bytes is C. */
+ charmask = c | (c << 8);
+ charmask |= charmask << 16;
+#if LONG_MAX > LONG_MAX_32_BITS
+ charmask |= charmask << 32;
+#endif
+
+ /* Instead of the traditional loop which tests each character,
+ we will test a longword at a time. The tricky part is testing
+ if *any of the four* bytes in the longword in question are zero. */
+ while (n >= sizeof (longword))
+ {
+ /* We tentatively exit the loop if adding MAGIC_BITS to
+ LONGWORD fails to change any of the hole bits of LONGWORD.
+
+ 1) Is this safe? Will it catch all the zero bytes?
+ Suppose there is a byte with all zeros. Any carry bits
+ propagating from its left will fall into the hole at its
+ least significant bit and stop. Since there will be no
+ carry from its most significant bit, the LSB of the
+ byte to the left will be unchanged, and the zero will be
+ detected.
+
+ 2) Is this worthwhile? Will it ignore everything except
+ zero bytes? Suppose every byte of LONGWORD has a bit set
+ somewhere. There will be a carry into bit 8. If bit 8
+ is set, this will carry into bit 16. If bit 8 is clear,
+ one of bits 9-15 must be set, so there will be a carry
+ into bit 16. Similarly, there will be a carry into bit
+ 24. If one of bits 24-30 is set, there will be a carry
+ into bit 31, so all of the hole bits will be changed.
+
+ The one misfire occurs when bits 24-30 are clear and bit
+ 31 is set; in this case, the hole at bit 31 is not
+ changed. If we had access to the processor carry flag,
+ we could close this loophole by putting the fourth hole
+ at bit 32!
+
+ So it ignores everything except 128's, when they're aligned
+ properly.
+
+ 3) But wait! Aren't we looking for C, not zero?
+ Good point. So what we do is XOR LONGWORD with a longword,
+ each of whose bytes is C. This turns each byte that is C
+ into a zero. */
+
+ longword = *--longword_ptr ^ charmask;
+
+ /* Add MAGIC_BITS to LONGWORD. */
+ if ((((longword + magic_bits)
+
+ /* Set those bits that were unchanged by the addition. */
+ ^ ~longword)
+
+ /* Look at only the hole bits. If any of the hole bits
+ are unchanged, most likely one of the bytes was a
+ zero. */
+ & ~magic_bits) != 0)
+ {
+ /* Which of the bytes was C? If none of them were, it was
+ a misfire; continue the search. */
+
+ const unsigned char *cp = (const unsigned char *) longword_ptr;
+
+#if LONG_MAX > 2147483647
+ if (cp[7] == c)
+ return (void *) &cp[7];
+ if (cp[6] == c)
+ return (void *) &cp[6];
+ if (cp[5] == c)
+ return (void *) &cp[5];
+ if (cp[4] == c)
+ return (void *) &cp[4];
+#endif
+ if (cp[3] == c)
+ return (void *) &cp[3];
+ if (cp[2] == c)
+ return (void *) &cp[2];
+ if (cp[1] == c)
+ return (void *) &cp[1];
+ if (cp[0] == c)
+ return (void *) cp;
+ }
+
+ n -= sizeof (longword);
+ }
+
+ char_ptr = (const unsigned char *) longword_ptr;
+
+ while (n-- > 0)
+ {
+ if (*--char_ptr == c)
+ return (void *) char_ptr;
+ }
+
+ return 0;
+}
+#ifndef MEMRCHR
+# ifdef weak_alias
+weak_alias (__memrchr, memrchr)
+# endif
+#endif
diff --git a/Sming/Core/Data/CStringArray.cpp b/Sming/Core/Data/CStringArray.cpp
index a0cd04fc1d..b1c38dd404 100644
--- a/Sming/Core/Data/CStringArray.cpp
+++ b/Sming/Core/Data/CStringArray.cpp
@@ -58,9 +58,10 @@ int CStringArray::indexOf(const char* str, bool ignoreCase) const
return -1;
}
+ auto buf = begin();
unsigned index = 0;
- for(unsigned offset = 0; offset < len; ++index) {
- const char* s = buffer + offset;
+ for(unsigned offset = 0; offset < buflen; ++index) {
+ const char* s = buf + offset;
if(ignoreCase) {
if(strcasecmp(str, s) == 0) {
return index;
diff --git a/Sming/System/include/stringutil.h b/Sming/System/include/stringutil.h
index 8c27a51845..7fc378e1db 100644
--- a/Sming/System/include/stringutil.h
+++ b/Sming/System/include/stringutil.h
@@ -43,6 +43,9 @@ int strcasecmp(const char* s1, const char* s2);
* @note non-ANSI GNU C library extension
*/
void* memmem(const void* haystack, size_t haystacklen, const void* needle, size_t needlelen);
+
+void *memrchr(const void *s, int c, size_t n);
+
#endif
int memicmp(const void* buf1, const void* buf2, size_t len);
diff --git a/Sming/Wiring/WString.cpp b/Sming/Wiring/WString.cpp
index 4d2e98bde5..f7aded4542 100644
--- a/Sming/Wiring/WString.cpp
+++ b/Sming/Wiring/WString.cpp
@@ -34,31 +34,12 @@ String::String(const char *cstr)
if (cstr) copy(cstr, strlen(cstr));
}
-String::String(const char *cstr, unsigned int length)
-{
- if (cstr) copy(cstr, length);
-}
-
-String::String(const String &value)
-{
- *this = value;
-}
-
-String::String(flash_string_t pstr, int length)
-{
- setString(pstr, length);
-}
-
String::String(const FlashString& fstr)
{
setString(fstr.data(), fstr.length());
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
-String::String(String &&rval)
-{
- move(rval);
-}
String::String(StringSumHelper &&rval)
{
move(rval);
@@ -68,7 +49,7 @@ String::String(StringSumHelper &&rval)
String::String(char c)
{
if (setLength(1))
- buffer[0] = c;
+ buffer()[0] = c;
}
String::String(unsigned char value, unsigned char base)
@@ -132,11 +113,6 @@ String::String(double value, unsigned char decimalPlaces)
*this = dtostrf(value, 0, decimalPlaces, buf);
}
-String::~String()
-{
- free(buffer);
-}
-
void String::setString(const char *cstr, int length /* = -1 */)
{
if (cstr)
@@ -170,76 +146,96 @@ void String::setString(flash_string_t pstr, int length /* = -1 */)
void String::invalidate(void)
{
- if (buffer) free(buffer);
- buffer = nullptr;
- capacity = len = 0;
-}
-
-bool String::reserve(unsigned int size)
-{
- if (buffer && capacity >= size) return true;
- if (changeBuffer(size))
- {
- if (len == 0) buffer[0] = '\0';
- return true;
+ if (sso.set) {
+ sso.set = false;
+ } else {
+ free(ptr.buffer);
}
- return false;
+ ptr.buffer = nullptr;
+ ptr.capacity = ptr.len = 0;
}
-bool String::setLength(unsigned int size)
+bool String::setLength(size_t size)
{
- if(!reserve(size))
+ if(!reserve(size)) {
return false;
+ }
- len = size;
- if(buffer)
- buffer[len] = '\0';
-
+ setlen(size);
return true;
}
-bool String::changeBuffer(unsigned int maxStrLen)
+bool String::reserve(size_t size)
{
- char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
- if (newbuffer)
- {
- buffer = newbuffer;
- capacity = maxStrLen;
- return true;
- }
- return false;
+ // Can we use SSO here to avoid allocation?
+ if(size <= SSO_CAPACITY) {
+ // If already using SSO then no further action required
+ if(sso.set) {
+ return true;
+ }
+
+ // If heap hasn't been used yet then switch to SSO mode
+ if(ptr.buffer == nullptr) {
+ sso.set = true;
+ } else {
+ // Otherwise continue with existing heap allocation
+ assert(ptr.capacity >= size);
+ }
+
+ return true;
+ }
+
+ // Reallocation required?
+ if(!sso.set && ptr.buffer != nullptr && ptr.capacity >= size) {
+ return true; // Nope :-)
+ }
+
+ // Need to handle resizing an existing heap buffer and moving from SSO to heap
+ char* newbuffer = (char*)realloc(sso.set ? nullptr : ptr.buffer, size + 1);
+ if(newbuffer == nullptr) {
+ // allocation failed - leave existing buffer arrangement unchanged
+ return false;
+ }
+
+ if(sso.set) {
+ // Move content out of SSO
+ memcpy(newbuffer, sso.buffer, sso.len);
+ ptr.len = sso.len;
+ sso.set = false;
+ }
+ ptr.buffer = newbuffer;
+ ptr.capacity = size;
+ return true;
}
/*********************************************/
/* Copy and Move */
/*********************************************/
-String & String::copy(const char *cstr, unsigned int length)
+String & String::copy(const char *cstr, size_t length)
{
if (!reserve(length))
{
invalidate();
return *this;
}
- len = length;
- memmove(buffer, cstr, length);
- buffer[length] = '\0';
+ memmove(buffer(), cstr, length);
+ setlen(length);
return *this;
}
-String &String::copy(flash_string_t pstr, unsigned int length)
+String &String::copy(flash_string_t pstr, size_t length)
{
// If necessary, allocate additional space so copy can be aligned
- unsigned int length_aligned = ALIGNUP(length);
+ size_t length_aligned = ALIGNUP(length);
if(!reserve(length_aligned))
{
invalidate();
}
else
{
- memcpy_aligned(buffer, (PGM_P)pstr, length);
- buffer[length] = '\0';
- len = length;
+ memcpy_aligned(buffer(), (PGM_P)pstr, length);
+ setlen(length);
}
return *this;
}
@@ -247,14 +243,36 @@ String &String::copy(flash_string_t pstr, unsigned int length)
#ifdef __GXX_EXPERIMENTAL_CXX0X__
void String::move(String &rhs)
{
- if (buffer)
- free(buffer);
- buffer = rhs.buffer;
- capacity = rhs.capacity;
- len = rhs.len;
- rhs.buffer = nullptr;
- rhs.capacity = 0;
- rhs.len = 0;
+ if(rhs.isNull()) {
+ invalidate();
+ return;
+ }
+
+ auto rhs_len = rhs.length();
+ if(rhs.sso.set) {
+ // Switch to SSO if required
+ reserve(rhs_len);
+ }
+
+ // If we already have capacity, copy the data and free rhs buffers
+ if(capacity() >= rhs_len) {
+ memmove(buffer(), rhs.buffer(), rhs_len);
+ setlen(rhs_len);
+ rhs.invalidate();
+ return;
+ }
+
+ assert(!rhs.sso.set);
+
+ // We don't have enough space so perform a pointer swap
+ if(!sso.set) {
+ free(ptr.buffer);
+ }
+ ptr = rhs.ptr;
+ // Can't use rhs.invalidate here as it would free the buffer
+ rhs.ptr.buffer = nullptr;
+ rhs.ptr.capacity = 0;
+ rhs.ptr.len = 0;
}
#endif
@@ -262,19 +280,16 @@ String & String::operator = (const String &rhs)
{
if (this == &rhs) return *this;
- if (rhs.buffer) copy(rhs.buffer, rhs.len);
- else invalidate();
+ if (rhs.isNull()) {
+ invalidate();
+ } else {
+ copy(rhs.cbuffer(), rhs.length());
+ }
return *this;
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
-String & String::operator = (String && rval)
-{
- if (this != &rval) move(rval);
- return *this;
-}
-
String & String::operator = (StringSumHelper && rval)
{
if (this != &rval) move(rval);
@@ -294,20 +309,28 @@ String & String::operator = (const char *cstr)
/* concat */
/*********************************************/
-bool String::concat(const String &s)
-{
- return concat(s.buffer, s.len);
-}
-
-bool String::concat(const char *cstr, unsigned int length)
+bool String::concat(const char *cstr, size_t length)
{
- unsigned int newlen = len + length;
if (length == 0) return true; // Nothing to add
if (!cstr) return false; // Bad argument (length is non-zero)
+
+ auto len = this->length();
+ size_t newlen = len + length;
+
+ // Appending all or part of self requires special handling
+ auto buf = buffer();
+ if(cstr >= buf && cstr < (buf + len)) {
+ auto offset = cstr - buf;
+ if (!reserve(newlen)) return false;
+ buf = buffer();
+ memcpy(buf + len, buf + offset, length);
+ setlen(newlen);
+ return true;
+ }
+
if (!reserve(newlen)) return false;
- memmove(buffer + len, cstr, length);
- buffer[newlen] = '\0';
- len = newlen;
+ memcpy(buffer() + len, cstr, length);
+ setlen(newlen);
return true;
}
@@ -317,14 +340,6 @@ bool String::concat(const char *cstr)
return concat(cstr, strlen(cstr));
}
-bool String::concat(char c)
-{
- char buf[2];
- buf[0] = c;
- buf[1] = 0;
- return concat(buf, 1);
-}
-
bool String::concat(unsigned char num)
{
char buf[1 + 3 * sizeof(num)];
@@ -395,7 +410,7 @@ bool String::concat(double num)
StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs)
{
StringSumHelper &a = const_cast(lhs);
- if (!a.concat(rhs.buffer, rhs.len)) a.invalidate();
+ if (!a.concat(rhs)) a.invalidate();
return a;
}
@@ -466,13 +481,13 @@ StringSumHelper & operator + (const StringSumHelper &lhs, double num)
/* Comparison */
/*********************************************/
-int String::compareTo(const char* cstr, unsigned int length) const
+int String::compareTo(const char* cstr, size_t length) const
{
auto len = this->length();
if (len == 0 || length == 0) {
return len - length;
}
- auto buf = c_str();
+ auto buf = cbuffer();
assert(buf != nullptr && cstr != nullptr);
if(len == length) {
return memcmp(buf, cstr, len);
@@ -485,129 +500,111 @@ int String::compareTo(const char* cstr, unsigned int length) const
bool String::equals(const char *cstr) const
{
+ auto len = length();
if (len == 0) return (cstr == nullptr || *cstr == '\0');
if (cstr == nullptr) return false;
auto cstrlen = strlen(cstr);
if (len != cstrlen) return false;
- return memcmp(buffer, cstr, len) == 0;
+ return memcmp(cbuffer(), cstr, len) == 0;
}
-bool String::equals(const char *cstr, unsigned int length) const
+bool String::equals(const char *cstr, size_t length) const
{
auto len = this->length();
if (len != length) return false;
if (len == 0) return true;
- return memcmp(c_str(), cstr, len) == 0;
+ return memcmp(cbuffer(), cstr, len) == 0;
}
bool String::equals(const FlashString& fstr) const
{
+ auto len = length();
if (len != fstr.length()) return false;
LOAD_FSTR(buf, fstr);
- return memcmp(buf, buffer, len) == 0;
-}
-
-bool String::operator<(const String &rhs) const
-{
- return compareTo(rhs) < 0;
-}
-
-bool String::operator>(const String &rhs) const
-{
- return compareTo(rhs) > 0;
-}
-
-bool String::operator<=(const String &rhs) const
-{
- return compareTo(rhs) <= 0;
-}
-
-bool String::operator>=(const String &rhs) const
-{
- return compareTo(rhs) >= 0;
+ return memcmp(buf, cbuffer(), len) == 0;
}
bool String::equalsIgnoreCase(const char* cstr) const
{
- if(buffer == cstr) return true;
- return strcasecmp(cstr, buffer) == 0;
+ auto buf = cbuffer();
+ if(buf == cstr) return true;
+ return strcasecmp(cstr, buf) == 0;
}
-bool String::equalsIgnoreCase(const char* cstr, unsigned int length) const
+bool String::equalsIgnoreCase(const char* cstr, size_t length) const
{
auto len = this->length();
if (len != length) return false;
if (len == 0) return true;
- return memicmp(c_str(), cstr, len) == 0;
+ return memicmp(cbuffer(), cstr, len) == 0;
}
bool String::equalsIgnoreCase(const FlashString& fstr) const
{
+ auto len = length();
if (len != fstr.length()) return false;
LOAD_FSTR(buf, fstr);
- return memicmp(buf, c_str(), len) == 0;
+ return memicmp(buf, cbuffer(), len) == 0;
}
-bool String::startsWith(const String &prefix) const
+bool String::startsWith(const String &prefix, size_t offset) const
{
- if (len < prefix.len) return false;
- return startsWith(prefix, 0);
-}
-
-bool String::startsWith(const String &prefix, unsigned int offset) const
-{
- if (offset + prefix.len > len || !buffer || !prefix.buffer) return false;
- return memcmp(&buffer[offset], prefix.buffer, prefix.len) == 0;
+ auto prefix_len = prefix.length();
+ auto len = length();
+ if(prefix_len == 0 || prefix_len > len || offset > len - prefix_len) {
+ return false;
+ }
+ auto prefix_buffer = prefix.cbuffer();
+ return memcmp(&cbuffer()[offset], prefix_buffer, prefix_len) == 0;
}
bool String::endsWith(const String &suffix) const
{
- if (len < suffix.len || !buffer || !suffix.buffer) return false;
- return memcmp(&buffer[len - suffix.len], suffix.buffer, suffix.len) == 0;
+ auto len = length();
+ auto suffix_buffer = suffix.cbuffer();
+ auto suffix_len = suffix.length();
+ if (len < suffix_len || suffix_len == 0) return false;
+ return memcmp(&cbuffer()[len - suffix_len], suffix_buffer, suffix_len) == 0;
}
/*********************************************/
/* Character Access */
/*********************************************/
-char String::charAt(unsigned int index) const
+void String::setCharAt(size_t index, char c)
{
- return operator[](index);
+ if (index < length()) buffer()[index] = c;
}
-void String::setCharAt(unsigned int index, char c)
+char & String::operator[](size_t index)
{
- if (index < len) buffer[index] = c;
-}
-
-char & String::operator[](unsigned int index)
-{
- if (index >= len || !buffer)
+ if (index >= length())
{
static char dummy_writable_char;
dummy_writable_char = '\0';
return dummy_writable_char;
}
- return buffer[index];
+ return buffer()[index];
}
-char String::operator[](unsigned int index) const
+char String::operator[](size_t index) const
{
- if (index >= len || !buffer) return '\0';
- return buffer[index];
+ if (index >= length()) return '\0';
+ return cbuffer()[index];
}
-unsigned int String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const
+size_t String::getBytes(unsigned char *buf, size_t bufsize, size_t index) const
{
if (!bufsize || !buf) return 0;
+ auto len = length();
if (index >= len)
{
buf[0] = '\0';
return 0;
}
- unsigned int n = bufsize - 1;
+ size_t n = bufsize - 1;
if (n > len - index) n = len - index;
- memmove(buf, buffer + index, n);
+ memmove(buf, cbuffer() + index, n);
buf[n] = '\0';
return n;
}
@@ -616,85 +613,91 @@ unsigned int String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned
/* Search */
/*********************************************/
-int String::indexOf(char c) const
-{
- return indexOf(c, 0);
-}
-
-int String::indexOf(char ch, unsigned int fromIndex) const
+int String::indexOf(char ch, size_t fromIndex) const
{
+ auto len = length();
if (fromIndex >= len) return -1;
- auto temp = (const char*)memchr(buffer + fromIndex, ch, len - fromIndex);
+ auto buf = cbuffer();
+ auto temp = memchr(buf + fromIndex, ch, len - fromIndex);
if (temp == nullptr) return -1;
- return temp - buffer;
+ return static_cast(temp) - buf;
}
-int String::indexOf(const String &s2) const
-{
- return indexOf(s2, 0);
-}
-
-int String::indexOf(const String &s2, unsigned int fromIndex) const
+int String::indexOf(const char* s2_buf, size_t fromIndex, size_t s2_len) const
{
+ auto len = length();
if (fromIndex >= len) return -1;
- auto found = (const char*)memmem(buffer + fromIndex, len - fromIndex, s2.buffer, s2.len);
+ auto buf = cbuffer();
+ auto found = memmem(buf + fromIndex, len - fromIndex, s2_buf, s2_len);
if (found == nullptr) return -1;
- return found - buffer;
+ return static_cast(found) - buf;
}
int String::lastIndexOf(char theChar) const
{
- return lastIndexOf(theChar, len - 1);
+ auto len = length();
+ if(len == 0) {
+ return -1;
+ }
+ auto buf = cbuffer();
+ auto found = memrchr(buf, theChar, len);
+ return found ? (static_cast(found) - buf) : -1;
}
-int String::lastIndexOf(char ch, unsigned int fromIndex) const
+int String::lastIndexOf(char ch, size_t fromIndex) const
{
- if (fromIndex >= len) return -1;
- char tempchar = buffer[fromIndex + 1];
- buffer[fromIndex + 1] = '\0';
- char* temp = strrchr(buffer, ch);
- buffer[fromIndex + 1] = tempchar;
- if (temp == nullptr) return -1;
- return temp - buffer;
+ auto len = length();
+ if(len == 0) {
+ return -1;
+ }
+ if(fromIndex < len) {
+ len = fromIndex + 1;
+ }
+ auto buf = cbuffer();
+ auto found = memrchr(buf, ch, len);
+ return found ? (static_cast(found) - buf) : -1;
}
int String::lastIndexOf(const String &s2) const
{
- return lastIndexOf(s2, len - s2.len);
+ return lastIndexOf(s2.cbuffer(), length() - s2.length(), s2.length());
}
-int String::lastIndexOf(const String &s2, unsigned int fromIndex) const
+int String::lastIndexOf(const String &s2, size_t fromIndex) const
{
- if (s2.len == 0 || len == 0 || s2.len > len) return -1;
+ return lastIndexOf(s2.cbuffer(), fromIndex, s2.length());
+}
+
+int String::lastIndexOf(const char* s2_buf, size_t fromIndex, size_t s2_len) const
+{
+ auto len = length();
+ if (s2_len == 0 || len == 0 || s2_len > len) return -1;
+ auto buf = cbuffer();
if (fromIndex >= len) fromIndex = len - 1;
int found = -1;
- for (char *p = buffer; p <= buffer + fromIndex; p++)
+ for (auto p = buf; p <= buf + fromIndex; p++)
{
- p = (char*)memmem(p, buffer + len - p, s2.buffer, s2.len);
+ p = static_cast(memmem(p, buf + len - p, s2_buf, s2_len));
if (!p) break;
- if (p <= buffer + fromIndex) found = p - buffer;
+ if (p <= buf + fromIndex) found = p - buf;
}
return found;
}
-String String::substring(unsigned int left, unsigned int right) const
+String String::substring(size_t left, size_t right) const
{
- if (!buffer) return nullptr;
+ if (isNull()) return nullptr;
if (left > right)
{
- unsigned int temp = right;
+ size_t temp = right;
right = left;
left = temp;
}
- String out;
- if (left > len) return out;
+ auto len = length();
+ if (left >= len) return nullptr;
if (right > len) right = len;
- char temp = buffer[right]; // save the replaced character
- buffer[right] = '\0';
- out = buffer + left; // pointer arithmetic
- buffer[right] = temp; //restore character
- return out;
+ return String(cbuffer() + left, right - left);
}
/*********************************************/
@@ -703,86 +706,94 @@ String String::substring(unsigned int left, unsigned int right) const
void String::replace(char find, char replace)
{
- if (!buffer) return;
- for (unsigned i = 0; i < len; ++i)
- {
- if (buffer[i] == find) buffer[i] = replace;
- }
+ auto buf = buffer();
+ for(unsigned len = length(); len > 0; --len, ++buf) {
+ if(*buf == find) {
+ *buf = replace;
+ }
+ }
}
-void String::replace(const String& find, const String& replace)
+bool String::replace(const String& find, const String& replace)
{
- if (len == 0 || find.len == 0) return;
- int diff = replace.len - find.len;
- char *readFrom = buffer;
- const char* end = buffer + len;
+ return this->replace(find.cbuffer(), find.length(), replace.cbuffer(), replace.length());
+}
+
+bool String::replace(const char* find_buf, size_t find_len, const char* replace_buf, size_t replace_len)
+{
+ auto len = length();
+ auto buf = buffer();
+ if (len == 0 || find_len == 0) return true;
+ int diff = replace_len - find_len;
+ char *readFrom = buf;
+ const char* end = buf + len;
char *foundAt;
if (diff == 0)
{
- while ((foundAt = (char*)memmem(readFrom, end - readFrom, find.buffer, find.len)) != nullptr)
+ while ((foundAt = (char*)memmem(readFrom, end - readFrom, find_buf, find_len)) != nullptr)
{
- memcpy(foundAt, replace.buffer, replace.len);
- readFrom = foundAt + replace.len;
+ memcpy(foundAt, replace_buf, replace_len);
+ readFrom = foundAt + replace_len;
}
}
else if (diff < 0)
{
- char *writeTo = buffer;
- while ((foundAt = (char*)memmem(readFrom, end - readFrom, find.buffer, find.len)) != nullptr)
+ char *writeTo = buf;
+ while ((foundAt = (char*)memmem(readFrom, end - readFrom, find_buf, find_len)) != nullptr)
{
- unsigned int n = foundAt - readFrom;
+ size_t n = foundAt - readFrom;
memcpy(writeTo, readFrom, n);
writeTo += n;
- memcpy(writeTo, replace.buffer, replace.len);
- writeTo += replace.len;
- readFrom = foundAt + find.len;
+ memcpy(writeTo, replace_buf, replace_len);
+ writeTo += replace_len;
+ readFrom = foundAt + find_len;
len += diff;
}
memcpy(writeTo, readFrom, end - readFrom);
- buffer[len] = '\0';
+ setlen(len);
}
else
{
- unsigned int size = len; // compute size needed for result
- while ((foundAt = (char*)memmem(readFrom, end - readFrom, find.buffer, find.len)) != nullptr)
+ size_t size = len; // compute size needed for result
+ while ((foundAt = (char*)memmem(readFrom, end - readFrom, find_buf, find_len)) != nullptr)
{
- readFrom = foundAt + find.len;
+ readFrom = foundAt + find_len;
size += diff;
}
- if (size == len) return;
- if (size > capacity && !changeBuffer(size)) return; // XXX: tell user!
+ if (size == len) return true;
+ if(!reserve(size)) {
+ return false;
+ }
+ buf = buffer();
int index = len - 1;
- while ((index = lastIndexOf(find, index)) >= 0)
+ while ((index = lastIndexOf(find_buf, index, find_len)) >= 0)
{
- readFrom = buffer + index + find.len;
- memmove(readFrom + diff, readFrom, len - (readFrom - buffer));
+ readFrom = buf + index + find_len;
+ memmove(readFrom + diff, readFrom, len - (readFrom - buf));
len += diff;
- memcpy(buffer + index, replace.buffer, replace.len);
+ memcpy(buf + index, replace_buf, replace_len);
index--;
}
- buffer[len] = '\0';
+ setlen(len);
}
+ return true;
}
-void String::remove(unsigned int index)
-{
- if(index < len) remove(index, len - index);
-}
-
-void String::remove(unsigned int index, unsigned int count)
+void String::remove(size_t index, size_t count)
{
- if (index >= len) { return; }
if (count == 0) { return; }
- if (index + count > len) { count = len - index; }
- char *writeTo = buffer + index;
+ auto len = length();
+ if (index >= len) { return; }
+ if (count > len - index) { count = len - index; }
+ char *writeTo = buffer() + index;
len -= count;
memcpy(writeTo, writeTo + count, len - index);
- buffer[len] = '\0';
+ setlen(len);
}
void String::toLowerCase(void)
{
- auto buf = begin();
+ auto buf = buffer();
for(unsigned len = length(); len > 0; --len, ++buf) {
*buf = tolower(*buf);
}
@@ -790,7 +801,7 @@ void String::toLowerCase(void)
void String::toUpperCase(void)
{
- auto buf = begin();
+ auto buf = buffer();
for(unsigned len = length(); len > 0; --len, ++buf) {
*buf = toupper(*buf);
}
@@ -798,14 +809,16 @@ void String::toUpperCase(void)
void String::trim(void)
{
- if (!buffer || len == 0) return;
- char *begin = buffer;
+ auto len = length();
+ if (len == 0) return;
+ auto buf = buffer();
+ char *begin = buf;
while (isspace(*begin)) begin++;
- char *end = buffer + len - 1;
+ char *end = buf + len - 1;
while (isspace(*end) && end >= begin) end--;
len = end + 1 - begin;
- if (begin > buffer) memmove(buffer, begin, len);
- buffer[len] = '\0';
+ if (begin > buf) memmove(buf, begin, len);
+ setlen(len);
}
/*********************************************/
@@ -814,18 +827,10 @@ void String::trim(void)
long String::toInt(void) const
{
- if (buffer) return atoi(buffer);
- return 0;
+ return isNull() ? 0 : atoi(cbuffer());
}
float String::toFloat(void) const
{
- if (buffer) return (float)atof(buffer);
- return 0;
+ return isNull() ? 0.0 : atof(cbuffer());
}
-
-/*void String::printTo(Print &p) const
-{
- p.print(buffer);
-}*/
-
diff --git a/Sming/Wiring/WString.h b/Sming/Wiring/WString.h
index 4aaabc4c4f..dde35d9215 100644
--- a/Sming/Wiring/WString.h
+++ b/Sming/Wiring/WString.h
@@ -44,6 +44,12 @@
* These changes have a knock-on effect in that if any of the allocations in an expression fail, then the result, tmp,
* will be unpredictable.
*
+ * @author Nov 2019 mikee47
+ *
+ * Small String Optimisation (SSO). Based on the Arduino ESP8266 core implentation.
+ * An empty String object now consumes 12 bytes (from 8) but provides an SSO capacity of 11 characters.
+ * Capacity and length types changed to size_t, thus String is no longer restricted to 64K.
+ *
*/
#pragma once
@@ -51,9 +57,8 @@
#ifdef __cplusplus
#include "WConstants.h"
-
-// @deprecated Should not be using String in interrupt context
-#define STRING_IRAM_ATTR // IRAM_ATTR
+#include
+#include
#ifndef __GXX_EXPERIMENTAL_CXX0X__
#define __GXX_EXPERIMENTAL_CXX0X__
@@ -105,7 +110,7 @@ class String
// complications of an operator bool(). for more information, see:
// http://www.artima.com/cppsource/safebool.html
typedef void (String::*StringIfHelperType)() const;
- void STRING_IRAM_ATTR StringIfHelper() const {}
+ void StringIfHelper() const {}
public:
// Use these for const references, e.g. in function return values
@@ -118,15 +123,27 @@ class String
if the initial value is null or invalid, or if memory allocation
fails, the string will be marked as invalid (i.e. "if (s)" will be false).
*/
- STRING_IRAM_ATTR String(const char *cstr = nullptr);
- STRING_IRAM_ATTR String(const char *cstr, unsigned int length);
- STRING_IRAM_ATTR String(const String &str);
- explicit String(flash_string_t pstr, int length = -1);
+ String(const char *cstr = nullptr);
+ String(const char *cstr, size_t length)
+ {
+ if (cstr) copy(cstr, length);
+ }
+ String(const String &str)
+ {
+ *this = str;
+ }
+ explicit String(flash_string_t pstr, int length = -1)
+ {
+ setString(pstr, length);
+ }
String(const FlashString& fstr);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
- STRING_IRAM_ATTR String(String && rval);
- STRING_IRAM_ATTR String(StringSumHelper && rval);
+ String(String && rval)
+ {
+ move(rval);
+ }
+ String(StringSumHelper && rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base = 10);
@@ -138,7 +155,10 @@ class String
explicit String(unsigned long long, unsigned char base = 10);
explicit String(float, unsigned char decimalPlaces=2);
explicit String(double, unsigned char decimalPlaces=2);
- ~String(void);
+ ~String(void)
+ {
+ invalidate();
+ }
void setString(const char *cstr, int length = -1);
void setString(flash_string_t pstr, int length = -1);
@@ -147,27 +167,31 @@ class String
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
- bool reserve(unsigned int size);
+ bool reserve(size_t size);
/** @brief set the string length accordingly, expanding if necessary
* @param length required for string (nul terminator additional)
* @retval true on success, false on failure
* @note extra characters are undefined
*/
- bool setLength(unsigned int length);
+ bool setLength(size_t length);
- inline unsigned int length(void) const
+ inline size_t length(void) const
{
- return len;
+ return sso.set ? sso.len : ptr.len;
}
// creates a copy of the assigned value. if the value is null or
// invalid, or if the memory allocation fails, the string will be
// marked as invalid ("if (s)" will be false).
- String & STRING_IRAM_ATTR operator = (const String &rhs);
- String & STRING_IRAM_ATTR operator = (const char *cstr);
+ String & operator = (const String &rhs);
+ String & operator = (const char *cstr);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
- String & operator = (String && rval);
+ String & operator = (String && rval)
+ {
+ if (this != &rval) move(rval);
+ return *this;
+ }
String & operator = (StringSumHelper && rval);
#endif
@@ -176,11 +200,17 @@ class String
// returns true on success, false on failure (in which case, the string
// is left unchanged). if the argument is null or invalid, the
// concatenation is considered unsucessful.
- bool concat(const String &str);
+ bool concat(const String &str)
+ {
+ return concat(str.cbuffer(), str.length());
+ }
bool concat(const char *cstr);
- bool STRING_IRAM_ATTR concat(const char *cstr, unsigned int length);
- bool concat(char c);
- bool concat(unsigned char c);
+ bool concat(const char *cstr, size_t length);
+ bool concat(char c)
+ {
+ return concat(&c, 1);
+ }
+ bool concat(unsigned char num);
bool concat(int num);
bool concat(unsigned int num);
bool concat(long num);
@@ -268,61 +298,79 @@ class String
// comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const
{
- return buffer ? &String::StringIfHelper : 0;
+ return isNull() ? 0 : &String::StringIfHelper;
}
- int compareTo(const char* cstr, unsigned int length) const;
+ int compareTo(const char* cstr, size_t length) const;
int compareTo(const String &s) const
{
- return compareTo(s.c_str(), s.length());
+ return compareTo(s.cbuffer(), s.length());
}
- bool STRING_IRAM_ATTR equals(const String &s) const
+ bool equals(const String &s) const
{
- return equals(s.c_str(), s.length());
+ return equals(s.cbuffer(), s.length());
}
- bool STRING_IRAM_ATTR equals(const char *cstr) const;
- bool equals(const char *cstr, unsigned int length) const;
+ bool equals(const char *cstr) const;
+ bool equals(const char *cstr, size_t length) const;
bool equals(const FlashString& fstr) const;
- bool STRING_IRAM_ATTR operator == (const String &rhs) const
+ bool operator == (const String &rhs) const
{
return equals(rhs);
}
- bool STRING_IRAM_ATTR operator == (const char *cstr) const
+ bool operator == (const char *cstr) const
{
return equals(cstr);
}
- bool STRING_IRAM_ATTR operator==(const FlashString& fstr) const
+ bool operator==(const FlashString& fstr) const
{
return equals(fstr);
}
- bool STRING_IRAM_ATTR operator != (const String &rhs) const
+ bool operator != (const String &rhs) const
{
return !equals(rhs);
}
- bool STRING_IRAM_ATTR operator != (const char *cstr) const
+ bool operator != (const char *cstr) const
{
return !equals(cstr);
}
- 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 operator < (const String &rhs) const
+ {
+ return compareTo(rhs) < 0;
+ }
+ bool operator > (const String &rhs) const
+ {
+ return compareTo(rhs) > 0;
+ }
+ bool operator <= (const String &rhs) const
+ {
+ return compareTo(rhs) <= 0;
+ }
+ bool operator >= (const String &rhs) const
+ {
+ return compareTo(rhs) >= 0;
+ }
bool equalsIgnoreCase(const char* cstr) const;
- bool equalsIgnoreCase(const char* cstr, unsigned int length) const;
+ bool equalsIgnoreCase(const char* cstr, size_t length) const;
bool equalsIgnoreCase(const String &s2) const
{
- return equalsIgnoreCase(s2.c_str(), s2.length());
+ return equalsIgnoreCase(s2.cbuffer(), s2.length());
}
bool equalsIgnoreCase(const FlashString& fstr) const;
- bool startsWith(const String &prefix) const;
- bool startsWith(const String &prefix, unsigned int offset) const;
+ bool startsWith(const String &prefix) const
+ {
+ return startsWith(prefix, 0);
+ }
+ bool startsWith(const String &prefix, size_t offset) const;
bool endsWith(const String &suffix) const;
// character acccess
- char STRING_IRAM_ATTR charAt(unsigned int index) const;
- void STRING_IRAM_ATTR setCharAt(unsigned int index, char c);
- char STRING_IRAM_ATTR operator [](unsigned int index) const;
- char& STRING_IRAM_ATTR operator [](unsigned int index);
+ char charAt(size_t index) const
+ {
+ return operator[](index);
+ }
+ void setCharAt(size_t index, char c);
+ char operator [](size_t index) const;
+ char& operator [](size_t index);
/** @brief read contents of string into a buffer
* @param buf buffer to write data
@@ -332,35 +380,42 @@ class String
* @note Returned data always nul terminated so buffer size needs to take this
* into account
*/
- unsigned int getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
+ size_t getBytes(unsigned char *buf, size_t bufsize, size_t index = 0) const;
- void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const
+ void toCharArray(char *buf, size_t bufsize, size_t index = 0) const
{
getBytes((unsigned char *)buf, bufsize, index);
}
- const char* c_str() const { return buffer ?: empty.buffer; }
- char* begin() { return buffer; }
- char* end() { return buffer + length(); }
+ const char* c_str() const { return cbuffer() ?: empty.cbuffer(); }
+ char* begin() { return buffer(); }
+ char* end() { return buffer() + length(); }
const char* begin() const { return c_str(); }
const char* end() const { return c_str() + length(); }
// search
- int STRING_IRAM_ATTR indexOf(char ch) const;
- int indexOf(char ch, unsigned int fromIndex) const;
- int STRING_IRAM_ATTR indexOf(const String &str) const;
- int indexOf(const String &s2, unsigned int fromIndex) const;
+ int indexOf(char ch, size_t fromIndex = 0) const;
+ int indexOf(const char* s2_buf, size_t fromIndex = 0, size_t s2_len = 0) const;
+ int indexOf(const String &s2, size_t fromIndex = 0) const
+ {
+ return indexOf(s2.cbuffer(), fromIndex, s2.length());
+ }
int lastIndexOf(char ch) const;
- int lastIndexOf(char ch, unsigned int fromIndex) const;
+ int lastIndexOf(char ch, size_t fromIndex) const;
int lastIndexOf(const String &s2) const;
- int lastIndexOf(const String &s2, unsigned int fromIndex) const;
- String substring(unsigned int beginIndex) const { return substring(beginIndex, len); }
- String substring(unsigned int beginIndex, unsigned int endIndex) const;
+ int lastIndexOf(const String &s2, size_t fromIndex) const;
+ int lastIndexOf(const char* s2_buf, size_t fromIndex, size_t s2_len) const;
+ String substring(size_t beginIndex) const { return substring(beginIndex, length()); }
+ String substring(size_t beginIndex, size_t endIndex) const;
// modification
void replace(char find, char replace);
- void replace(const String& find, const String& replace);
- void remove(unsigned int index);
- void remove(unsigned int index, unsigned int count);
+ bool replace(const String& find, const String& replace);
+ bool replace(const char* find_buf, size_t find_len, const char* replace_buf, size_t replace_len);
+ void remove(size_t index)
+ {
+ remove(index, SIZE_MAX);
+ }
+ void remove(size_t index, size_t count);
void toLowerCase(void);
void toUpperCase(void);
void trim(void);
@@ -368,24 +423,75 @@ class String
// parsing/conversion
long toInt(void) const;
float toFloat(void) const;
-
- //void printTo(Print &p) const;
+protected:
+ /// Used when contents allocated on heap
+ struct PtrBuf {
+ char* buffer; // the actual char array
+ size_t len; // the String length (not counting the '\0')
+ size_t capacity : 31; // the array length minus one (for the '\0')
+ size_t isSSO : 1;
+ };
+ // For small strings we can store data directly without requiring the heap
+ static constexpr size_t SSO_CAPACITY = sizeof(PtrBuf) - 2; ///< Less one char for '\0'
+ struct SsoBuf {
+ char buffer[SSO_CAPACITY + 1];
+ unsigned char len : 7;
+ unsigned char set : 1; ///< true for SSO mode
+ };
+ union {
+ struct {
+ size_t u32[3] = {0};
+ };
+ PtrBuf ptr;
+ SsoBuf sso;
+ };
+
+ static_assert(sizeof(PtrBuf) == sizeof(SsoBuf), "String size incorrect - check alignment");
+
+protected:
+ // Free any heap memory and set to non-SSO mode; isNull() will return true
+ void invalidate(void);
+
+ // String is Null (invalid) by default, i.e. non-SSO and null buffer
+ __forceinline bool isNull() const
+ {
+ return !sso.set && (ptr.buffer == nullptr);
+ }
+ // Get writeable buffer pointer
+ __forceinline char* buffer()
+ {
+ return sso.set ? sso.buffer : ptr.buffer;
+ }
- protected:
- char *buffer = nullptr; // the actual char array
- uint16_t capacity = 0; // the array length minus one (for the '\0')
- uint16_t len = 0; // the String length (not counting the '\0')
- //unsigned char flags; // unused, for future features
+ // Get read-only buffer pointer
+ __forceinline const char* cbuffer() const
+ {
+ return sso.set ? sso.buffer : ptr.buffer;
+ }
- protected:
- void STRING_IRAM_ATTR invalidate(void);
- bool STRING_IRAM_ATTR changeBuffer(unsigned int maxStrLen);
+ // Get currently assigned capacity for current mode
+ __forceinline size_t capacity() const
+ {
+ return sso.set ? SSO_CAPACITY : ptr.capacity;
+ }
+
+ // Called whenever string length changes to ensure NUL terminator is set
+ __forceinline void setlen(size_t len)
+ {
+ if(sso.set) {
+ sso.len = len;
+ sso.buffer[len] = '\0';
+ } else {
+ ptr.len = len;
+ ptr.buffer[len] = '\0';
+ }
+ }
// copy and move
- String & copy(const char *cstr, unsigned int length);
- String& copy(flash_string_t pstr, unsigned int length);
+ String & copy(const char *cstr, size_t length);
+ String& copy(flash_string_t pstr, size_t length);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
void move(String &rhs);
#endif
diff --git a/tests/HostTests/app/arduino-test-string.cpp b/tests/HostTests/app/arduino-test-string.cpp
index 094995240a..a2fb54777b 100644
--- a/tests/HostTests/app/arduino-test-string.cpp
+++ b/tests/HostTests/app/arduino-test-string.cpp
@@ -283,7 +283,6 @@ class ArduinoStringTest : public TestGroup
REQUIRE(!strcmp(s17.c_str(), "1234567890123456_"));
}
- /*
TEST_CASE("String SSO works", "[core][String]")
{
// This test assumes that SSO_SIZE==8, if that changes the test must as well
@@ -384,7 +383,6 @@ class ArduinoStringTest : public TestGroup
REQUIRE(s == "abcde");
REQUIRE(s.length() == 5);
}
- */
auto repl = [](const String& key, const String& val, String& s, boolean useURLencode) { s.replace(key, val); };