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); };