Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
577 changes: 380 additions & 197 deletions cores/esp8266/WString.cpp

Large diffs are not rendered by default.

164 changes: 132 additions & 32 deletions cores/esp8266/WString.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ class String {
String() __attribute__((always_inline)) { // See init()
init();
}
String(const char *cstr);
String(const String &str);
String(const __FlashStringHelper *str);
String(const char *cstr);
String(const __FlashStringHelper *str) :
String(reinterpret_cast<const char *>(str))
{}
String(String &&rval) noexcept;

explicit String(char c) {
Expand Down Expand Up @@ -134,7 +136,9 @@ class String {
String &operator =(const String &rhs);
String &operator =(String &&rval) noexcept;
String &operator =(const char *cstr);
String &operator =(const __FlashStringHelper *str);
String &operator =(const __FlashStringHelper *str) {
return *this = reinterpret_cast<const char *>(str);
}
String &operator =(char c);

String &operator =(unsigned char value) {
Expand Down Expand Up @@ -189,8 +193,10 @@ class String {
// concatenation is considered unsuccessful.
bool concat(const String &str);
bool concat(const char *cstr);
bool concat(const char *cstr, unsigned int length);
bool concat(const __FlashStringHelper *str);
bool concat(const char *str, unsigned int length);
bool concat(const __FlashStringHelper *str) {
return concat(reinterpret_cast<const char *>(str));
}
bool concat(char c);

bool concat(unsigned char c);
Expand Down Expand Up @@ -218,36 +224,87 @@ class String {
}

int compareTo(const String &s) const;
int compareTo(const char *cstr) const;
int compareTo(const char *str, unsigned int length) const;
int compareTo(const __FlashStringHelper *str) const {
return compareTo(reinterpret_cast<const char *>(str));
}

bool equals(const String &s) const;
bool equals(const char *cstr) const;
bool equals(const __FlashStringHelper *s) const;
bool equals(const char *str, unsigned int length) const;
bool equals(const __FlashStringHelper *str) const {
return equals(reinterpret_cast<const char *>(str));
}

bool operator ==(const String &rhs) const {
return equals(rhs);
}
bool operator ==(const char *cstr) const {
return equals(cstr);
}
bool operator ==(const __FlashStringHelper *str) const {
return equals(str);
}
bool operator !=(const String &rhs) const {
return !equals(rhs);
}
bool operator !=(const char *cstr) const {
return !equals(cstr);
}
bool operator !=(const __FlashStringHelper *str) const {
return equals(str);
}
bool operator <(const String &rhs) const;
bool operator <(const char *rhs) const;
bool operator <(const __FlashStringHelper *rhs) const {
return *this < reinterpret_cast<const char *>(rhs);
}
bool operator >(const String &rhs) const;
bool operator >(const char *rhs) const;
bool operator >(const __FlashStringHelper *rhs) const {
return *this > reinterpret_cast<const char *>(rhs);
}
bool operator <=(const String &rhs) const;
bool operator <=(const char *rhs) const;
bool operator <=(const __FlashStringHelper *rhs) const {
return *this <= reinterpret_cast<const char *>(rhs);
}
bool operator >=(const String &rhs) const;
bool operator >=(const char *rhs) const;
bool operator >=(const __FlashStringHelper *rhs) const {
return *this >= reinterpret_cast<const char *>(rhs);
}

bool equalsIgnoreCase(const String &s) const;
bool equalsIgnoreCase(const __FlashStringHelper *s) const;
bool equalsIgnoreCase(const char *s) const;
bool equalsIgnoreCase(const char *str, unsigned int length) const;
bool equalsIgnoreCase(const __FlashStringHelper *s) const {
return equalsIgnoreCase(reinterpret_cast<const char *>(s));
}

unsigned char equalsConstantTime(const String &s) const;
unsigned char equalsConstantTime(const char *str, unsigned int length) const;

bool startsWith(const String &prefix) const;
bool startsWith(const char *prefix) const;
bool startsWith(const __FlashStringHelper *prefix) const;
bool startsWith(const __FlashStringHelper *prefix) const {
return startsWith(reinterpret_cast<const char *>(prefix));
}

bool startsWith(const String &prefix, unsigned int offset) const;
bool startsWith(const __FlashStringHelper *prefix, unsigned int offset) const;
bool startsWith(const char *prefix, unsigned int offset) const;
bool startsWith(const char *str, unsigned int length, unsigned int offset) const;
bool startsWith(const __FlashStringHelper *prefix, unsigned int offset) const {
return startsWith(reinterpret_cast<const char *>(prefix), offset);
}

bool endsWith(const String &suffix) const;
bool endsWith(const char *suffix) const;
bool endsWith(const __FlashStringHelper *suffix) const;
bool endsWith(const char *str, unsigned int length) const;
bool endsWith(const __FlashStringHelper *suffix) const {
return endsWith(reinterpret_cast<const char *>(suffix));
}

// character access
char charAt(unsigned int index) const {
Expand All @@ -256,10 +313,10 @@ class String {
void setCharAt(unsigned int index, char c);
char operator [](unsigned int index) const;
char &operator [](unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
getBytes((unsigned char *) buf, bufsize, index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const {
toCharArray((char *)buf, bufsize, index);
}
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const;
const char *c_str() const { return buffer(); }
char *begin() { return wbuffer(); }
char *end() { return wbuffer() + length(); }
Expand All @@ -270,15 +327,23 @@ class String {
int indexOf(char ch, unsigned int fromIndex = 0) const;
int indexOf(const char *str, unsigned int fromIndex = 0) const;
int indexOf(const __FlashStringHelper *str, unsigned int fromIndex = 0) const {
return indexOf((const char*)str, fromIndex);
return indexOf(reinterpret_cast<const char*>(str), fromIndex);
}
int indexOf(const String &str, unsigned int fromIndex = 0) const;
int lastIndexOf(char ch) const;
int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const;
int lastIndexOf(const String &str, unsigned int fromIndex) const;
int lastIndexOf(const __FlashStringHelper *str) const;
int lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const;
int lastIndexOf(const char *cstr) const;
int lastIndexOf(const char *cstr, unsigned int fromIndex) const;
int lastIndexOf(const char *str, unsigned int length, unsigned int fromIndex) const;

int lastIndexOf(const __FlashStringHelper *str) const {
return lastIndexOf(reinterpret_cast<const char *>(str));
}
int lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const {
return lastIndexOf(reinterpret_cast<const char *>(str), fromIndex);
}
String substring(unsigned int beginIndex) const {
return substring(beginIndex, len());
}
Expand All @@ -287,11 +352,24 @@ class String {
// modification
void replace(char find, char replace);
void replace(const String &find, const String &replace);
void replace(const String &find, const char *replace);
void replace(const String &find, const __FlashStringHelper *replace) {
this->replace(find, reinterpret_cast<const char *>(replace));
}
void replace(const char *find, const String &replace);
void replace(const __FlashStringHelper *find, const String &replace);
void replace(const __FlashStringHelper *find, const String &replace) {
this->replace(reinterpret_cast<const char *>(find), replace);
}
void replace(const char *find, const char *replace);
void replace(const __FlashStringHelper *find, const char *replace);
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace);
void replace(const char *find, const __FlashStringHelper *replace) {
this->replace(find, reinterpret_cast<const char *>(replace));
}
void replace(const __FlashStringHelper *find, const char *replace) {
this->replace(reinterpret_cast<const char *>(find), replace);
}
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
this->replace(reinterpret_cast<const char *>(find), reinterpret_cast<const char *>(replace));
}

// Pass the biggest integer if the count is not specified.
// The remove method below will take care of truncating it at the end of the string.
Expand Down Expand Up @@ -350,7 +428,6 @@ class String {
friend String operator +(const char *lhs, String &&rhs);
friend String operator +(const __FlashStringHelper *lhs, String &&rhs);

protected:
// TODO: replace init() with a union constructor, so it's called implicitly

void init(void) __attribute__((always_inline)) {
Expand All @@ -376,17 +453,43 @@ class String {
bool changeBuffer(unsigned int maxStrLen);

// copy or insert at a specific position
String &copy(const char *cstr, unsigned int length);
String &copy(const __FlashStringHelper *pstr, unsigned int length);
String &copy(const char *str, unsigned int length);
String &copy(const __FlashStringHelper *str, unsigned int length) {
return copy(reinterpret_cast<const char *>(str), length);
}

void replace(const char *find, unsigned int find_len, const char *replace, unsigned int replace_len);

String &insert(size_t position, char);
String &insert(size_t position, const char *);
String &insert(size_t position, const __FlashStringHelper *);
String &insert(size_t position, const char *, size_t length);
String &insert(size_t position, const String &);
String &insert(size_t position, const char * str);
String &insert(size_t position, const __FlashStringHelper *str) {
return insert(position, reinterpret_cast<const char *>(str));
}
String &insert(size_t position, const char * str, unsigned int length);
String &insert(size_t position, const String &str);

// rvalue helper
void move(String &rhs) noexcept;

// internal implementations rely on func wrappers
using internal_memcmp_t = int (*)(const void *, const void *, size_t);

int compareToImpl(internal_memcmp_t, const char *str, unsigned int length) const;
bool equalsImpl(internal_memcmp_t, const char *str, unsigned int length) const;
bool startsWithImpl(internal_memcmp_t, const char *str, unsigned int length, unsigned int offset) const;
bool endsWithImpl(internal_memcmp_t, const char *str, unsigned int length) const;

using internal_memcasecmp_t = int (*)(const void *, const void *, size_t);

bool equalsIgnoreCaseImpl(internal_memcasecmp_t, const char *str, unsigned int length) const;

using internal_memmem_t = void *(*)(const void *, size_t, const void *, size_t);

int indexOfImpl(internal_memmem_t, const char *str, unsigned int length, unsigned int fromIndex) const;
int lastIndexOfImpl(internal_memmem_t, const char *str, unsigned int length, unsigned int fromIndex) const;
void replaceImpl(internal_memmem_t impl, const char *find, unsigned int find_len, const char *replace, unsigned int replace_len);

friend struct __StringImpl;
};

// concatenation (note that it's done using non-method operators to handle both possible type refs)
Expand Down Expand Up @@ -432,10 +535,7 @@ inline String operator +(char lhs, String &&rhs) {
return std::move(rhs.insert(0, lhs));
}

// both `char*` and `__FlashStringHelper*` are implicitly converted into `String()`, calling the `operator+(const String& ...);`
// however, here we:
// - do an automatic `reserve(total length)` for the resulting string
// - possibly do rhs.insert(0, ...), when &&rhs capacity could fit both
// Similarly, allow c-strings and `F(...)` as both lhs and rhs

String operator +(const char *lhs, const String &rhs);

Expand All @@ -444,11 +544,11 @@ inline String operator +(const char *lhs, String &&rhs) {
}

inline String operator +(const __FlashStringHelper *lhs, const String &rhs) {
return reinterpret_cast<const char*>(lhs) + rhs;
return reinterpret_cast<const char *>(lhs) + rhs;
}

inline String operator +(const __FlashStringHelper *lhs, String &&rhs) {
return std::move(rhs.insert(0, lhs));
return reinterpret_cast<const char *>(lhs) + std::move(rhs);
}

extern const String emptyString;
Expand Down
3 changes: 2 additions & 1 deletion tests/host/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ INC_PATHS += \
../../tools/sdk/lwip2/include \
)

TEST_WRAPPER ?=
TEST_ARGS ?=

TEST_CPP_FILES := \
Expand Down Expand Up @@ -258,7 +259,7 @@ CI: # run CI
doCI: build-info $(OUTPUT_BINARY) valgrind test gcov

test: $(OUTPUT_BINARY) # run host test for CI
$(OUTPUT_BINARY) $(TEST_ARGS)
$(TEST_WRAPPER) $(OUTPUT_BINARY) $(TEST_ARGS)

.PHONY: clean
clean: clean-lcov clean-objects
Expand Down
3 changes: 3 additions & 0 deletions tests/host/common/stdio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once
#include_next <stdio.h>
#include <sys/stdio.h>
3 changes: 3 additions & 0 deletions tests/host/common/string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once
#include_next <string.h>
#include <sys/string.h>
54 changes: 42 additions & 12 deletions tests/host/core/test_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,34 @@ TEST_CASE("String::trim", "[core][String]")

TEST_CASE("String::replace", "[core][String]")
{
String str;
str = "The quick brown fox jumped over the lazy dog.";
String find = "fox";
String replace = "vulpes vulpes";
str.replace(find, replace);
const char data[] = "The quick brown fox jumped over the lazy dog.";
String str = data;
str.replace("fox", "vulpes vulpes");
REQUIRE(str == "The quick brown vulpes vulpes jumped over the lazy dog.");
str.replace("vulpes", "lis lis");
REQUIRE(str == "The quick brown lis lis lis lis jumped over the lazy dog.");
str.replace("lazy dog.", "canis piger");
REQUIRE(str == "The quick brown lis lis lis lis jumped over the canis piger");
str.replace("brown lis lis", "lis");
REQUIRE(str == "The quick lis lis lis jumped over the canis piger");
str.replace("lis", "fox");
REQUIRE(str == "The quick fox fox fox jumped over the canis piger");
str.replace("fox fox", "kot");
REQUIRE(str == "The quick kot fox jumped over the canis piger");
str.replace("fox jumped", "red felis");
REQUIRE(str == "The quick kot red felis over the canis piger");
str.replace(" over ", " jumped over ");
REQUIRE(str == "The quick kot red felis jumped over the canis piger");
str.replace("canis piger", "lazy dog");
REQUIRE(str == "The quick kot red felis jumped over the lazy dog");
str.replace("dog", "dog.");
REQUIRE(str == "The quick kot red felis jumped over the lazy dog.");
str.replace(" kot", "");
REQUIRE(str == "The quick red felis jumped over the lazy dog.");
str.replace("red", "brown");
REQUIRE(str == "The quick brown felis jumped over the lazy dog.");
str.replace("felis", "fox");
REQUIRE(str == data);
}

TEST_CASE("String(value, base)", "[core][String]")
Expand Down Expand Up @@ -186,12 +208,19 @@ TEST_CASE("String concantenation", "[core][String]")

TEST_CASE("String comparison", "[core][String]")
{
String alpha("I like fish!");
REQUIRE(String("a") < String("b")); // compareTo() reference comparisons
REQUIRE(String("1") < String("2"));
REQUIRE(String("999") > String("1000"));
String alpha("I like fish!"); // compareTo()
REQUIRE(alpha < "I like tacos!");
REQUIRE(alpha > "I like bacon!");
REQUIRE(alpha > "I like cod!");
REQUIRE(alpha >= "I like beef!");
REQUIRE(alpha <= "I like soup!");
REQUIRE(alpha.equalsIgnoreCase("i LiKe FiSh!"));
REQUIRE(!alpha.equalsIgnoreCase("i LiKe FiSh! And ChIPs!"));
REQUIRE(alpha.equalsConstantTime("I like fish!"));
REQUIRE(alpha != "I like fish?");
REQUIRE(alpha != "I like fish?"); // equals()
REQUIRE(alpha.startsWith("I like"));
REQUIRE(!alpha.startsWith("I lick"));
REQUIRE(alpha.startsWith("fish", 7));
Expand Down Expand Up @@ -581,11 +610,11 @@ TEST_CASE("String chaining", "[core][String]")

// make sure we can chain a combination of things to form a String
REQUIRE((String(chunks[0]) + String(chunks[1]) + String(chunks[2]) + String(chunks[3])) == all);
REQUIRE((chunks[0] + String(chunks[1]) + F(chunks[2]) + chunks[3]) == all);
REQUIRE((String(chunks[0]) + F(chunks[1]) + F(chunks[2]) + String(chunks[3])) == all);
REQUIRE(('~' + String(&chunks[0][0] + 1) + chunks[1] + String(chunks[2]) + F(chunks[3]))
REQUIRE((chunks[0] + String(chunks[1]) + FPSTR(chunks[2]) + chunks[3]) == all);
REQUIRE((String(chunks[0]) + FPSTR(chunks[1]) + FPSTR(chunks[2]) + String(chunks[3])) == all);
REQUIRE(('~' + String(&chunks[0][0] + 1) + chunks[1] + String(chunks[2]) + FPSTR(chunks[3]))
== all);
REQUIRE((String(chunks[0]) + '6' + (&chunks[1][0] + 1) + String(chunks[2]) + F(chunks[3]))
REQUIRE((String(chunks[0]) + '6' + (&chunks[1][0] + 1) + String(chunks[2]) + FPSTR(chunks[3]))
== all);

// these are still invalid (and also cannot compile at all):
Expand All @@ -600,7 +629,8 @@ TEST_CASE("String chaining", "[core][String]")
String tmp(chunks[3]);
tmp.reserve(2 * all.length());
auto* ptr = tmp.c_str();
String result("~1" + String(&chunks[0][0] + 2) + F(chunks[1]) + chunks[2] + std::move(tmp));
String result("~1" + String(&chunks[0][0] + 2) + FPSTR(chunks[1]) + chunks[2]
+ std::move(tmp));
REQUIRE(result == all);
REQUIRE(static_cast<const void*>(result.c_str()) == static_cast<const void*>(ptr));
}
Expand Down
Loading