Last active
December 27, 2023 12:56
-
-
Save mariodivece/4491f8a7462cc66414dc0620309e7941 to your computer and use it in GitHub Desktop.
A C++ string class designed for embedded systems
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// online dev: https://www.onlinegdb.com/online_c++_compiler | |
#include <cstring> | |
#include <iostream> | |
#include <cstdarg> | |
#include <algorithm> | |
using namespace std; | |
class StringEx { | |
private: | |
static const size_t GrowSize = 16; // reduces realloc calls by grown 16 bytes at a time | |
size_t size; | |
int length; | |
char* buffer; | |
public: | |
inline StringEx() { | |
size = computeGrowth(sizeof(char)); | |
length = 0; | |
buffer = (char*)malloc(size); | |
buffer[0] = 0; | |
} | |
inline explicit StringEx(size_t reserved) { | |
size = reserved; | |
length = 0; | |
buffer = (char*)malloc(reserved); | |
buffer[0] = 0; | |
} | |
inline explicit StringEx(const char* str) : | |
StringEx((strlen(str) + 1) * sizeof(char)) { | |
append(str); | |
} | |
inline explicit StringEx(const StringEx* other) { | |
size = other->size; | |
length = other->length; | |
buffer = (char*)malloc(size); | |
buffer[0] = 0; | |
append(other->buffer); | |
} | |
inline ~StringEx() { | |
if (buffer != nullptr) | |
free(buffer); | |
length = 0; | |
size = 0; | |
} | |
inline void clear() { | |
buffer[0] = 0; | |
length = 0; | |
} | |
inline void appendFormat(const char* format, ...) { | |
// capture variable arguments. | |
va_list tokens; | |
// guess the needed size of the format buffer. | |
auto formattedLength = strlen(format); | |
size_t formattedSize = (formattedLength + (GrowSize * 2) + 1) * sizeof(char); | |
auto formattedBuffer = (char*)malloc(formattedSize); | |
// format the string and reformat if not enough room. | |
va_start(tokens, format); | |
auto wantedFormattedLength = vsnprintf(formattedBuffer, formattedSize, format, tokens); | |
va_end(tokens); | |
size_t wantedFormattedSize = (wantedFormattedLength + 1) * sizeof(char); | |
if (wantedFormattedSize > formattedSize) { | |
formattedSize = wantedFormattedSize; | |
formattedBuffer = (char*)realloc(formattedBuffer, formattedSize); | |
// reformat via variable arguments | |
va_start(tokens, format); | |
formattedLength = vsnprintf(formattedBuffer, formattedSize, format, tokens); | |
va_end(tokens); | |
} | |
else { | |
formattedLength = wantedFormattedLength; | |
} | |
// concatenate and regrow the buffer as needed. | |
append(formattedBuffer); | |
free(formattedBuffer); | |
} | |
inline void append(const char* other) { | |
auto otherLength = strlen(other); | |
size_t neededSize = (length + otherLength + 1) * sizeof(char); | |
if (size < neededSize) { | |
size_t newSize = computeGrowth(neededSize); | |
buffer = (char*)realloc(buffer, newSize); | |
size = newSize; | |
} | |
auto copyAddress = &buffer[length]; | |
strcpy(copyAddress, other); | |
length += otherLength; | |
} | |
inline void append(const StringEx& other) { | |
append(other.buffer); | |
} | |
inline void append(char c) { | |
auto otherLength = 1; | |
size_t neededSize = (length + otherLength + 1) * sizeof(char); | |
if (size < neededSize) { | |
size_t newSize = computeGrowth(neededSize); | |
buffer = (char*)realloc(buffer, newSize); | |
size = newSize; | |
} | |
buffer[length] = c; | |
length += otherLength; | |
} | |
inline bool startsWith(const char* other, bool sensitive = true) { | |
auto otherLength = strlen(other); | |
if (otherLength <= 0 || otherLength > length) { | |
return false; | |
} | |
auto result = true; | |
if (sensitive) { | |
// case sensitve check | |
for (auto i = 0; i < otherLength; i++) { | |
if (buffer[i] != other[i]) | |
return false; | |
} | |
} | |
else { | |
// case-insensitive check | |
for (auto i = 0; i < otherLength; i++) { | |
if (toupper(buffer[i]) != toupper(other[i])) | |
return false; | |
} | |
} | |
return true; | |
} | |
inline bool startsWith(const StringEx& other, bool sensitive = true) { | |
return startsWith(other.buffer, sensitive); | |
} | |
inline bool endsWith(const char* other, bool sensitive = true) { | |
auto otherLength = strlen(other); | |
if (otherLength <= 0 || otherLength > length) { | |
return false; | |
} | |
auto result = true; | |
auto bufferOffset = &buffer[length - otherLength]; | |
if (sensitive) { | |
for (auto i = 0; i < otherLength; i++) { | |
if (bufferOffset[i] != other[i]) | |
return false; | |
} | |
} | |
else { | |
for (auto i = 0; i < otherLength; i++) { | |
if (toupper(bufferOffset[i]) != toupper(other[i])) | |
return false; | |
} | |
} | |
return true; | |
} | |
inline bool endsWith(const StringEx& other, bool sensitive = true) { | |
return endsWith(other.buffer, sensitive); | |
} | |
inline int indexOf(const char* match, bool sensitive = true, int startIndex = 0) { | |
auto matchLen = strlen(match); | |
if (length < matchLen || matchLen <= 0) return -1; | |
if (startIndex >= length) return -1; | |
if (startIndex < 0) startIndex = 0; | |
auto matchIndex = 0; | |
auto matchCount = 0; | |
auto result = -1; | |
if (sensitive) { | |
for (auto i = startIndex; i < length; i++) { | |
if (buffer[i] == match[matchIndex]) { | |
if (matchIndex == 0) | |
result = i; | |
matchCount = matchIndex + 1; | |
matchIndex++; | |
} | |
else { | |
result = -1; | |
matchIndex = 0; | |
} | |
if (matchCount >= matchLen) { | |
return result; | |
} | |
} | |
} | |
else { | |
for (auto i = startIndex; i < length; i++) { | |
if (toupper(buffer[i]) == toupper(match[matchIndex])) { | |
if (matchIndex == 0) | |
result = i; | |
matchCount = matchIndex + 1; | |
matchIndex++; | |
} | |
else { | |
result = -1; | |
matchIndex = 0; | |
} | |
if (matchCount >= matchLen) { | |
return result; | |
} | |
} | |
} | |
return -1; | |
} | |
inline int indexOf(const StringEx& match, bool sensitive = true, int startIndex = 0) { | |
return indexOf(match.buffer, sensitive, startIndex); | |
} | |
inline bool contains(const char* match, bool sensitive = true, int startIndex = 0) { | |
return indexOf(match, sensitive, startIndex) >= 0; | |
} | |
inline bool contains(const StringEx& match, bool sensitive = true, int startIndex = 0) { | |
return contains(match.buffer, sensitive, startIndex); | |
} | |
inline int compareTo(const char* other) { | |
auto result = strcmp(buffer, other); | |
return result < 0 | |
? -1 | |
: result > 0 | |
? 1 | |
: 0; | |
} | |
inline int compareTo(const StringEx& other) { | |
return compareTo(other.buffer); | |
} | |
inline bool isEmpty() { return length == 0; } | |
inline bool equals(const char* other) { return compareTo(other) == 0; } | |
inline bool equals(const StringEx& other) { return compareTo(other) == 0; } | |
inline StringEx* substring(int startIndex, int maxLength = -1) { | |
if (maxLength == 0 || startIndex >= length) | |
return new StringEx(); | |
auto outputLength = length - startIndex; | |
if (maxLength > 0) outputLength = min(outputLength, maxLength); | |
auto copyAddress = &buffer[startIndex]; | |
auto result = new StringEx((outputLength + 1) * sizeof(char)); | |
memcpy(result->buffer, copyAddress, outputLength * sizeof(char)); | |
result->buffer[outputLength] = 0; | |
result->length = outputLength; | |
return result; | |
} | |
/// @brief Returns a copy of this string converted to Uppercase. | |
inline StringEx* toUpper() { | |
auto result = (char*)malloc(size); | |
for (auto i = 0; i < length; i++) { | |
result[i] = toupper(buffer[i]); | |
} | |
result[length] = 0; | |
return new StringEx(result); | |
} | |
/// @brief Returns a copy of this string converted to Lowercase. | |
inline StringEx* toLower() { | |
auto result = (char*)malloc(size); | |
for (auto i = 0; i < length; i++) { | |
result[i] = tolower(buffer[i]); | |
} | |
result[length] = 0; | |
return new StringEx(result); | |
} | |
inline const char* c_str() const { return buffer; } | |
inline int len() { return length; } | |
inline int bufferSize() { return size; } | |
inline void trimBuffer() { | |
auto wantedSize = (length + 1) * sizeof(char); | |
if (size <= wantedSize) return; | |
buffer = (char*)realloc(buffer, wantedSize); | |
size = wantedSize; | |
} | |
inline void reset() { | |
clear(); | |
trimBuffer(); | |
} | |
inline void dump() { | |
cout << "'" << buffer << "'\r\n" << " >> length: " << len() << ", size: " << bufferSize() << "\r\n"; | |
} | |
StringEx& operator += (const char* cstr) {append(cstr); return *this;} | |
private: | |
inline static size_t computeGrowth(size_t neededSize) { | |
if (neededSize <= 0) return GrowSize; | |
auto modResult = neededSize % GrowSize; | |
if (modResult == 0) return neededSize + GrowSize; | |
return neededSize + (GrowSize - modResult); | |
} | |
}; | |
int main() | |
{ | |
auto s2 = new StringEx(); | |
s2->dump(); | |
s2->append("Extended String > 16 | "); | |
s2->dump(); | |
s2->append("NOGROW"); | |
s2->dump(); | |
s2->append(" | Another string"); | |
s2->dump(); | |
s2->appendFormat(" | One is %d and 2 is %d and <<%s>> is formatted!", 1, 2, "hello world!"); | |
s2->dump(); | |
s2->clear(); | |
s2->trimBuffer(); | |
s2->dump(); | |
delete s2; | |
// starts with test | |
auto s3 = new StringEx("Hello World!"); | |
bool startsTest[4]; | |
startsTest[0] = s3->startsWith("Hello"); | |
startsTest[1] = s3->startsWith("hell"); | |
startsTest[2] = s3->startsWith("Hello World?"); | |
startsTest[3] = s3->startsWith("Hello World!?"); | |
s3->appendFormat("\r\n StartsWith 'Hello' ? %d ", startsTest[0]); | |
s3->appendFormat("\r\n StartsWith 'hell' ? %d ", startsTest[1]); | |
s3->appendFormat("\r\n StartsWith 'Hello World?' ? %d ", startsTest[2]); | |
s3->appendFormat("\r\n StartsWith 'Hello World!?' ? %d ", startsTest[3]); | |
s3->dump(); | |
delete s3; | |
auto s4 = new StringEx("Hello World!"); | |
bool endsTests[4]; | |
endsTests[0] = s4->endsWith("World!"); | |
endsTests[1] = s4->endsWith("world!", false); | |
endsTests[2] = s4->endsWith(""); | |
endsTests[3] = s4->endsWith("Hello World!?"); | |
s4->appendFormat("\r\n EndsWith 'World!' ? %d ", endsTests[0]); | |
s4->appendFormat("\r\n EndsWith 'world!' ? %d ", endsTests[1]); | |
s4->appendFormat("\r\n EndsWith '' ? %d ", endsTests[2]); | |
s4->appendFormat("\r\n EndsWith 'Hello World!?' ? %d ", endsTests[3]); | |
s4->dump(); | |
delete s4; | |
auto s5 = new StringEx("We think in generalities, but we live in details."); | |
s5->append('X'); | |
s5->dump(); | |
auto ix = s5->indexOf("generalities"); | |
cout << "Index of generalities: " << ix << endl; | |
auto s6 = s5->substring(ix, strlen("generalities")); | |
s6->dump(); | |
s6->appendFormat(": ils %d", 6); | |
s6->dump(); | |
auto s7 = s6->toUpper(); | |
s7->dump(); | |
delete s5; | |
delete s6; | |
delete s7; | |
auto s8 = new StringEx("fire pit"); | |
cout << endl << s8->compareTo("Fire Pit") << endl; | |
const char* opers = " Operators!"; | |
s8->operator+=(opers); | |
//s8 += opers; // this is not working for some reason ... | |
s8->dump(); | |
delete s8; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment