Skip to content

Instantly share code, notes, and snippets.

@mariodivece
Last active December 27, 2023 12:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mariodivece/4491f8a7462cc66414dc0620309e7941 to your computer and use it in GitHub Desktop.
Save mariodivece/4491f8a7462cc66414dc0620309e7941 to your computer and use it in GitHub Desktop.
A C++ string class designed for embedded systems
// 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