#ifndef CORE_STRING_H #define CORE_STRING_H #include "math/Math.h" #include "utils/Check.h" #include "utils/Utility.h" namespace Core { template class ArrayString final { int length; u32 hash; static constexpr int DATA_LENGTH = (N - Math::max(CORE_SIZE(length), CORE_SIZE(hash))) / CORE_SIZE(u32); static_assert(DATA_LENGTH > 0, "Size of array string too small"); u32 data[static_cast(DATA_LENGTH)]; public: ArrayString() : length(0), hash(0) { data[0] = '\0'; } bool operator==(const char* s) const { for(int i = 0; i < length; i++) { u32 u = 0; if(readUnicode(u, s) || data[i] != u) { return false; } } return read(s) == 0; } template bool operator==(const ArrayString& other) const { if(length != other.getLength()) { return false; } for(int i = 0; i < length; i++) { if(data[i] != other[i]) { return false; } } return true; } bool operator!=(const char* s) const { return !((*this) == s); } template bool operator!=(const ArrayString& other) const { return !((*this) == other); } u32 operator[](int index) const { return data[index]; } int getLength() const { return length; } constexpr int getCapacity() const { return DATA_LENGTH; } // returns true on error and calls the error callback check_return bool append(char c) { if(c < 0) { return CORE_ERROR(Error::NEGATIVE_ARGUMENT); } return appendUnicode(static_cast(c)); } // returns true on error and calls the error callback check_return bool append(signed char c) { if(c < 0) { return CORE_ERROR(Error::NEGATIVE_ARGUMENT); } return appendUnicode(static_cast(c)); } // returns true on error and calls the error callback check_return bool append(unsigned char c) { return appendUnicode(c); } // returns true on error and calls the error callback check_return bool append(const char* s) { while(true) { u32 u = 0; if(readUnicode(u, s)) { return true; } else if(u == 0) { return false; } else if(appendUnicode(u)) { return true; } } return false; } // returns true on error and calls the error callback check_return bool append(const signed char* s) { return append(reinterpret_cast(s)); } // returns true on error and calls the error callback check_return bool append(const unsigned char* s) { return append(reinterpret_cast(s)); } // returns true on error and calls the error callback check_return bool append(signed short s) { return convertAppend(s); } // returns true on error and calls the error callback check_return bool append(unsigned short s) { return convertAppend(s); } // returns true on error and calls the error callback check_return bool append(signed int i) { return convertAppend(i); } // returns true on error and calls the error callback check_return bool append(unsigned int i) { return convertAppend(i); } // returns true on error and calls the error callback check_return bool append(signed long l) { return convertAppend(l); } // returns true on error and calls the error callback check_return bool append(unsigned long l) { return convertAppend(l); } // returns true on error and calls the error callback check_return bool append(signed long long ll) { return convertAppend(ll); } // returns true on error and calls the error callback check_return bool append(unsigned long long ll) { return convertAppend(ll); } // returns true on error and calls the error callback check_return bool append(float f) { return convertAppend(static_cast(f)); } // returns true on error and calls the error callback check_return bool append(double d) { return convertAppend(d); } // returns true on error and calls the error callback check_return bool append(long double ld) { return convertAppend(ld); } // returns true on error and calls the error callback check_return bool append(bool b) { return b ? append("true") : append("false"); } // returns true on error and calls the error callback check_return bool appendUnicode(u32 c) { if(length >= DATA_LENGTH) { return CORE_ERROR(Error::CAPACITY_REACHED); } data[length++] = c; addToHash(c); return false; } // returns true on error and calls the error callback template check_return bool append(const T& t) { return t.toString(*this); } // returns true on error and calls the error callback template check_return bool toString(ArrayString& s) const { int l = length; // length changes if &s == this for(int i = 0; i < l; i++) { if(s.appendUnicode(data[i])) { return true; } } return false; } void clear() { length = 0; hash = 0; data[0] = '\0'; } u32 hashCode() const { return hash; } // returns true on error and calls the error callback check_return bool print() const { for(int i = 0; i < length; i++) { u32 c = data[i]; if(c < (1 << 7)) { if(Core::putChar(static_cast(c & 0x7F))) { return true; } } else if(c < (1 << 11)) { if(printChar(c, 6, 0x1F, 0xC0) || printChar(c, 0, 0x3F, 0x80)) { return true; } } else if(c < (1 << 16)) { if(printChar(c, 12, 0x0F, 0xE0) || printChar(c, 6, 0x3F, 0x80) || printChar(c, 0, 0x3F, 0x80)) { return true; } } else if(c < (1 << 21)) { if(printChar(c, 18, 0x07, 0xF0) || printChar(c, 12, 0x3F, 0x80) || printChar(c, 6, 0x3F, 0x80) || printChar(c, 0, 0x3F, 0x80)) { return true; } } } return false; } // returns true on error and calls the error callback check_return bool printLine() const { return print() || Core::putChar('\n'); } // returns true on error and calls the error callback template check_return bool format(Args&&... args) { ArrayString s; if(formatBuffer(s, 0, Core::forward(args)...)) { return true; } *this = s; return false; } template bool startsWidth(const ArrayString& other, int from = 0) const { if(from + other.getLength() > length) { return false; } for(int i = 0; i < other.getLength(); i++) { if(data[from + i] != other[i]) { return false; } } return true; } template int search(const ArrayString& other, int from = 0) const { for(int i = from; i < length; i++) { if(startsWidth(other, i)) { return i; } } return -1; } template bool contains(const ArrayString& other, int from = 0) const { return search(other, from) >= 0; } int search(u32 u, int from = 0) const { for(int i = from; i < length; i++) { if(data[i] == u) { return i; } } return -1; } bool contains(u32 u, int from = 0) const { return search(u, from) >= 0; } ArrayString substring(int from, int to) const { from = Math::max(from, 0); to = Math::min(to, length - 1); ArrayString s; for(int i = from; i <= to; i++) { (void)s.appendUnicode(data[i]); } return s; } ArrayString substring(int from = 0) const { return substring(from, length - 1); } template bool replace(const ArrayString& search, const ArrayString& replace) { ArrayString s; int i = 0; while(i < length) { if(startsWidth(search, i)) { if(s.append(replace)) { return true; } i += search.getLength(); } else { if(s.appendUnicode(data[i])) { return true; } i++; } } *this = s; return false; } void replace(u32 search, u32 replace) { hash = 0; for(int i = 0; i < length; i++) { if(data[i] == search) { data[i] = replace; } addToHash(data[i]); } } private: // returns true on error and calls the error callback check_return static bool printChar(u32 u, u32 shift, u32 a, u32 o) { return Core::putChar(static_cast(((u >> shift) & a) | o)); } static u32 read(const char*& s) { if(*s == '\0') { return 0; } return static_cast(*(s++)); } // returns true on error and calls the error callback static bool readUnicode(u32& u, const char*& s) { u = read(s); if((u & 0x80) == 0) { return false; } if((u & 0xE0) == 0xC0) { u32 u2 = read(s); if(u2 == 0) { return CORE_ERROR(Error::INVALID_CHAR); } u = ((u & 0x1F) << 6) | (u2 & 0x3F); return false; } else if((u & 0xF0) == 0xE0) { u32 u2 = read(s); u32 u3 = read(s); if(u2 == 0 || u3 == 0) { return CORE_ERROR(Error::INVALID_CHAR); } u = ((u & 0xF) << 12) | ((u2 & 0x3F) << 6) | (u3 & 0x3F); return false; } else if((u & 0xF8) == 0xF0) { u32 u2 = read(s); u32 u3 = read(s); u32 u4 = read(s); if(u2 == 0 || u3 == 0 || u4 == 0) { return CORE_ERROR(Error::INVALID_CHAR); } u = ((u & 0x07) << 18) | ((u2 & 0x3F) << 12) | ((u3 & 0x3F) << 6) | (u4 & 0x3F); return false; } return CORE_ERROR(Error::INVALID_CHAR); } void addToHash(u32 u) { hash = static_cast(2120251889) * hash + static_cast(u); } // returns true on error and calls the error callback template check_return bool formatBuffer(ArrayString& s, int index, const T& t, Args&&... args) { while(index < length) { u32 u = data[index++]; if(u == '#') { if(index >= length || (index < length && data[index] != '#')) { break; } index++; } if(s.appendUnicode(u)) { return true; } } if(s.append(t)) { return true; } return formatBuffer(s, index, Core::forward(args)...); } // returns true on error and calls the error callback check_return bool formatBuffer(ArrayString& s, int index) { while(index < length) { if(s.appendUnicode(data[index++])) { return true; } } return false; } // returns true on error and calls the error callback template check_return bool convertAppend(T t) { constexpr int BUFFER_SIZE = 64; char buffer[BUFFER_SIZE]; if(Core::toString(t, buffer, BUFFER_SIZE)) { return true; } return append(static_cast(buffer)); } }; } template bool operator==(const char* cs, const Core::ArrayString& s) { return s == cs; } template bool operator!=(const char* cs, const Core::ArrayString& s) { return s != cs; } #endif