#ifndef STRINGBUFFER_H #define STRINGBUFFER_H #include #include #include #include "utils/Check.h" #include "utils/Types.h" // ignore buffer overflow warnings with snprintf #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-truncation" template class StringBuffer final { int length; Hash hash; static_assert(N > 0, "StringBuffer must have a positive size"); char data[static_cast(N)]; void addToHash(char c) { hash = static_cast(2120251889) * hash + static_cast(c); } void addToHash(int from, int to) { for(int i = from; i < to; i++) { addToHash(data[i]); } } public: StringBuffer() : length(0), hash(0) { data[0] = '\0'; } template StringBuffer(const T& t) : StringBuffer() { append(t); } bool operator==(const char* str) const { return strcmp(data, str) == 0; } bool operator==(const StringBuffer& other) const { return length == other.length && other == data; } bool operator!=(const char* str) const { return !((*this) == str); } bool operator!=(const StringBuffer& other) const { return !((*this) == other); } operator const char*() const { return data; } char operator[](int index) const { return data[index]; } int getLength() const { return length; } StringBuffer& append(char c) { if(length < N - 1) { data[length++] = c; addToHash(c); data[length] = '\0'; } return *this; } StringBuffer& append(signed char c) { return append(static_cast(c)); } StringBuffer& append(unsigned char c) { return append(static_cast(c)); } StringBuffer& append(const char* str) { return appendString(str); } StringBuffer& append(const signed char* str) { return appendString(str); } StringBuffer& append(const unsigned char* str) { return appendString(str); } check_format(2, 3) StringBuffer& appendFormat(const char* format, ...) { int left = N - length; va_list args; va_start(args, format); int written = vsnprintf(data + length, static_cast(left), format, args); va_end(args); int oldLength = length; if(written < left) { length += written; } else { length = N - 1; } addToHash(oldLength, length); return *this; } StringBuffer& append(signed short s) { return appendFormat("%hd", s); } StringBuffer& append(unsigned short s) { return appendFormat("%hu", s); } StringBuffer& append(signed int i) { return appendFormat("%d", i); } StringBuffer& append(unsigned int i) { return appendFormat("%u", i); } StringBuffer& append(float f) { return appendFormat("%.2f", static_cast(f)); } StringBuffer& append(bool b) { return b ? append("true") : append("false"); } template StringBuffer& append(const T& t) { t.toString(*this); return *this; } template void toString(StringBuffer& s) const { if(reinterpret_cast(this) == reinterpret_cast(&s)) { return; } s.append(data); } StringBuffer& appendUnicode(unsigned int c) { if(c < (1 << 7)) { append(static_cast(c & 0x7F)); } else if(c < (1 << 11)) { append(static_cast(((c >> 6) & 0x1F) | 0xC0)); append(static_cast(((c >> 0) & 0x3F) | 0x80)); } else if(c < (1 << 16)) { append(static_cast(((c >> 12) & 0x0F) | 0xE0)); append(static_cast(((c >> 6) & 0x3F) | 0x80)); append(static_cast(((c >> 0) & 0x3F) | 0x80)); } else if(c < (1 << 21)) { append(static_cast(((c >> 18) & 0x07) | 0xF0)); append(static_cast(((c >> 12) & 0x3F) | 0x80)); append(static_cast(((c >> 6) & 0x3F) | 0x80)); append(static_cast(((c >> 0) & 0x3F) | 0x80)); } return *this; } StringBuffer& clear() { length = 0; hash = 0; data[0] = '\0'; return *this; } Hash hashCode() const { return hash; } void print() const { std::cout << data; } void printLine() const { std::cout << data << '\n'; } private: template StringBuffer& appendString(T str) { for(int i = 0; length < N - 1 && str[i] != '\0'; length++, i++) { data[length] = static_cast(str[i]); addToHash(static_cast(str[i])); } data[length] = '\0'; return *this; } }; template bool operator==(const char* str, const StringBuffer buffer) { return buffer == str; } template bool operator!=(const char* str, const StringBuffer buffer) { return buffer != str; } #pragma GCC diagnostic pop #endif