#pragma once #include #include #include #include #include #include namespace BAN { class String { public: using size_type = size_t; using iterator = IteratorSimple; using const_iterator = ConstIteratorSimple; static constexpr size_type sso_capacity = 15; public: String() {} String(const String& other) { *this = other; } String(String&& other) { *this = move(other); } String(StringView other) { *this = other; } ~String() { clear(); } template static BAN::ErrorOr formatted(const char* format, Args&&... args) { size_type length = 0; BAN::Formatter::print([&](char) { length++; }, format, BAN::forward(args)...); String result; TRY(result.reserve(length)); BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward(args)...); return result; } String& operator=(const String& other) { clear(); MUST(ensure_capacity(other.size())); memcpy(data(), other.data(), other.size() + 1); m_size = other.size(); return *this; } String& operator=(String&& other) { clear(); if (other.has_sso()) memcpy(data(), other.data(), other.size() + 1); else { m_storage.general_storage = other.m_storage.general_storage; m_has_sso = false; } m_size = other.m_size; other.m_size = 0; other.m_storage.sso_storage = SSOStorage(); other.m_has_sso = true; return *this; } String& operator=(StringView other) { clear(); MUST(ensure_capacity(other.size())); memcpy(data(), other.data(), other.size()); m_size = other.size(); data()[m_size] = '\0'; return *this; } ErrorOr push_back(char c) { TRY(ensure_capacity(m_size + 1)); data()[m_size] = c; m_size++; data()[m_size] = '\0'; return {}; } ErrorOr insert(char c, size_type index) { ASSERT(index <= m_size); TRY(ensure_capacity(m_size + 1)); memmove(data() + index + 1, data() + index, m_size - index); data()[index] = c; m_size++; data()[m_size] = '\0'; return {}; } ErrorOr insert(StringView str, size_type index) { ASSERT(index <= m_size); TRY(ensure_capacity(m_size + str.size())); memmove(data() + index + str.size(), data() + index, m_size - index); memcpy(data() + index, str.data(), str.size()); m_size += str.size(); data()[m_size] = '\0'; return {}; } ErrorOr append(StringView str) { TRY(ensure_capacity(m_size + str.size())); memcpy(data() + m_size, str.data(), str.size()); m_size += str.size(); data()[m_size] = '\0'; return {}; } void pop_back() { ASSERT(m_size > 0); m_size--; data()[m_size] = '\0'; } void remove(size_type index) { ASSERT(index < m_size); memmove(data() + index, data() + index + 1, m_size - index); m_size--; data()[m_size] = '\0'; } void clear() { if (!has_sso()) { deallocator(m_storage.general_storage.data); m_storage.sso_storage = SSOStorage(); m_has_sso = true; } m_size = 0; data()[m_size] = '\0'; } const_iterator begin() const { return const_iterator(data()); } iterator begin() { return iterator(data()); } const_iterator end() const { return const_iterator(data() + size()); } iterator end() { return iterator(data() + size()); } char front() const { ASSERT(m_size > 0); return data()[0]; } char& front() { ASSERT(m_size > 0); return data()[0]; } char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; } char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; } char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; } char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; } bool operator==(const String& str) const { if (size() != str.size()) return false; for (size_type i = 0; i < m_size; i++) if (data()[i] != str.data()[i]) return false; return true; } bool operator==(StringView str) const { if (size() != str.size()) return false; for (size_type i = 0; i < m_size; i++) if (data()[i] != str.data()[i]) return false; return true; } bool operator==(const char* cstr) const { for (size_type i = 0; i < m_size; i++) if (data()[i] != cstr[i]) return false; if (cstr[size()] != '\0') return false; return true; } ErrorOr resize(size_type new_size, char init_c = '\0') { if (m_size == new_size) return {}; // expanding if (m_size < new_size) { TRY(ensure_capacity(new_size)); memset(data() + m_size, init_c, new_size - m_size); m_size = new_size; data()[m_size] = '\0'; return {}; } m_size = new_size; data()[m_size] = '\0'; return {}; } ErrorOr reserve(size_type new_size) { TRY(ensure_capacity(new_size)); return {}; } ErrorOr shrink_to_fit() { if (has_sso()) return {}; if (fits_in_sso()) { char* data = m_storage.general_storage.data; m_storage.sso_storage = SSOStorage(); m_has_sso = true; memcpy(this->data(), data, m_size + 1); deallocator(data); return {}; } GeneralStorage& storage = m_storage.general_storage; if (storage.capacity == m_size) return {}; char* new_data = (char*)allocator(m_size + 1); if (new_data == nullptr) return Error::from_errno(ENOMEM); memcpy(new_data, storage.data, m_size); deallocator(storage.data); storage.capacity = m_size; storage.data = new_data; return {}; } StringView sv() const { return StringView(data(), size()); } bool empty() const { return m_size == 0; } size_type size() const { return m_size; } size_type capacity() const { if (has_sso()) return sso_capacity; return m_storage.general_storage.capacity; } char* data() { if (has_sso()) return m_storage.sso_storage.data; return m_storage.general_storage.data; } const char* data() const { if (has_sso()) return m_storage.sso_storage.data; return m_storage.general_storage.data; } private: ErrorOr ensure_capacity(size_type new_size) { if (m_size >= new_size) return {}; if (has_sso() && fits_in_sso(new_size)) return {}; char* new_data = (char*)allocator(new_size + 1); if (new_data == nullptr) return Error::from_errno(ENOMEM); if (m_size) memcpy(new_data, data(), m_size + 1); if (has_sso()) { m_storage.general_storage = GeneralStorage(); m_has_sso = false; } else deallocator(m_storage.general_storage.data); auto& storage = m_storage.general_storage; storage.capacity = new_size; storage.data = new_data; return {}; } bool has_sso() const { return m_has_sso; } bool fits_in_sso() const { return fits_in_sso(m_size); } static bool fits_in_sso(size_type size) { return size < sso_capacity; } private: struct SSOStorage { char data[sso_capacity + 1] {}; }; struct GeneralStorage { size_type capacity { 0 }; char* data { nullptr }; }; private: union { SSOStorage sso_storage; GeneralStorage general_storage; } m_storage { .sso_storage = SSOStorage() }; size_type m_size : sizeof(size_type) * 8 - 1 { 0 }; size_type m_has_sso : 1 { true }; }; template<> struct hash { hash_t operator()(const String& string) const { constexpr hash_t FNV_offset_basis = 0x811c9dc5; constexpr hash_t FNV_prime = 0x01000193; hash_t hash = FNV_offset_basis; for (String::size_type i = 0; i < string.size(); i++) { hash *= FNV_prime; hash ^= (uint8_t)string[i]; } return hash; } }; } namespace BAN::Formatter { template void print_argument(F putc, const String& string, const ValueFormat&) { for (String::size_type i = 0; i < string.size(); i++) putc(string[i]); } }