#pragma once #include "tl/detail/prologue.h" #include #include #include #include #include #include "murmur_hash.h" #include "tl/api.h" #include "tl/hash_map.h" #include "tl/vector.h" #include "tl/plain_assert.h" #include "tl/detail/string_db.h" namespace tl { TL_API inline bool memeq(const char* a, const char* b, size_t size) noexcept; namespace ascii { TL_API constexpr bool isdigit(char c) noexcept; TL_API constexpr bool isspace(char c) noexcept; TL_API constexpr char toupper(char c) noexcept; TL_API constexpr char tolower(char c) noexcept; TL_API constexpr int stricmp(const char* s1, const char* s2) noexcept; TL_API constexpr int strnicmp(const char* s1, const char* s2, size_t len) noexcept; TL_API constexpr int memicmp(const char* s1, const char* s2, size_t n) noexcept; TL_API constexpr const char* memichr(const char* s, char ch, size_t n) noexcept; TL_API constexpr const char* stristr(const char* in, const char* str) noexcept; TL_API constexpr const char* strnstr(const char* in, const char* str, size_t n) noexcept; } namespace detail { class string_db_map; class string_db; struct string_cell; } #define cell_data(c) (reinterpret_cast(c) + sizeof(tl::detail::string_cell)) class TL_API string { friend class detail::string_db_map; friend class detail::string_db; public: using const_iterator = const char*; using value_type = char; using size_type = size_t; string() noexcept = default; string(string const& str) noexcept; string(string&& str) noexcept; string(string const& str, size_type offset, size_type size) noexcept; string(char const* from, char const* to) noexcept; string(char const* str) noexcept; string(char const* str, size_type size) noexcept; explicit string(std::string_view const& str) noexcept; explicit string(string_view const& str) noexcept; ~string() noexcept; bool operator==(string const& str) const noexcept; bool operator!=(string const& str) const noexcept; bool operator==(std::string_view const& str) const noexcept; bool operator!=(std::string_view const& str) const noexcept; bool operator==(string_view const& str) const noexcept; bool operator!=(string_view const& str) const noexcept; bool operator==(char const* str) const noexcept; bool operator!=(char const* str) const noexcept; operator std::string_view() const noexcept; operator string_view() const noexcept; constexpr std::strong_ordering operator<=>(const string& str) const noexcept; std::strong_ordering operator<=>(const std::string_view& str) const noexcept; std::strong_ordering operator<=>(const string_view& str) const noexcept; std::strong_ordering operator<=>(const char* c_str) const noexcept; int compare(const string& str) const noexcept; int compare_ci(const string& str) const noexcept; int compare(const std::string_view& str) const noexcept; int compare_ci(const std::string_view& str) const noexcept; int compare(const string_view& str) const noexcept; int compare_ci(const string_view& str) const noexcept; int compare(const char* c_str) const noexcept; int compare_ci(const char* c_str) const noexcept; string& operator=(string const& str) noexcept; string& operator=(string&& str) noexcept; string& operator=(char const* str) noexcept; string& operator=(std::string_view const& str) noexcept; string& operator=(string_view const& str) noexcept; string operator+(string const& str) const noexcept; string& operator+=(string const& str) noexcept; string operator+(std::string_view const& str) const noexcept; string& operator+=(std::string_view const& str) noexcept; string operator+(string_view const& str) const noexcept; string& operator+=(string_view const& str) noexcept; string operator+(char const* cstr) const noexcept; string& operator+=(char const* cstr) noexcept; string operator+(char ch) const noexcept; string& operator+=(char ch) noexcept; char operator[](size_type index) const noexcept; char back() const noexcept; char front() const noexcept; ////////////////////////////////////////////////////////////////////////// //equivalents of std::string char const* data() const noexcept; char const* c_str() const noexcept; std::string std_str() const noexcept; eastl::string eastl_str() const noexcept; size_type size() const noexcept; size_type length() const noexcept; bool empty() const noexcept; void clear() noexcept; void swap(string& other) noexcept; void reserve(size_type size) noexcept; void resize(size_type size) noexcept; string substr(size_type off = 0, size_type count = npos) const noexcept; void push_back(char ch) noexcept; string& append(char ch) noexcept; string& append(char const* cstr) noexcept; string& append(char const* from, char const* to) noexcept; string& append(string const& str) noexcept; string& append(std::string_view const& str) noexcept; string& append(string_view const& str) noexcept; string& append(string const& str, size_t pos, size_t count = npos) noexcept; string& append(std::string_view const& str, size_t pos, size_t count = npos) noexcept; string& append(string_view const& str, size_t pos, size_t count = npos) noexcept; size_type find(char ch, size_type off = 0) const noexcept; size_type find_ci(char ch, size_type off = 0) const noexcept; size_type find(char const* cstr, size_type off = 0) const noexcept; size_type find_ci(char const* cstr, size_type off = 0) const noexcept; size_type find(char const* cstr, size_t cstr_size, size_type off = 0) const noexcept; size_type find_ci(char const* cstr, size_t cstr_size, size_type off = 0) const noexcept; size_type find(string const& str, size_type off = 0) const noexcept; size_type find_ci(string const& str, size_type off = 0) const noexcept; size_type find(std::string_view const& str, size_type off = 0) const noexcept; size_type find_ci(std::string_view const& str, size_type off = 0) const noexcept; size_type find(string_view const& str, size_type off = 0) const noexcept; size_type find_ci(string_view const& str, size_type off = 0) const noexcept; size_type find_first_of(char ch, size_type off = 0) const noexcept; size_type find_first_of_ci(char ch, size_type off = 0) const noexcept; size_type find_first_of(char const* cstr, size_type off = 0) const noexcept; size_type find_first_of_ci(char const* cstr, size_type off = 0) const noexcept; size_type find_first_of(string const& str, size_type off = 0) const noexcept; size_type find_first_of_ci(string const& str, size_type off = 0) const noexcept; size_type find_first_of(std::string_view const& str, size_type off = 0) const noexcept; size_type find_first_of_ci(std::string_view const& str, size_type off = 0) const noexcept; size_type find_first_of(string_view const& str, size_type off = 0) const noexcept; size_type find_first_of_ci(string_view const& str, size_type off = 0) const noexcept; size_type find_first_not_of(char ch, size_type off = 0) const noexcept; size_type find_first_not_of_ci(char ch, size_type off = 0) const noexcept; size_type find_first_not_of(char const* cstr, size_type off = 0) const noexcept; size_type find_first_not_of_ci(char const* cstr, size_type off = 0) const noexcept; size_type find_first_not_of(string const& str, size_type off = 0) const noexcept; size_type find_first_not_of_ci(string const& str, size_type off = 0) const noexcept; size_type find_first_not_of(std::string_view const& str, size_type off = 0) const noexcept; size_type find_first_not_of_ci(std::string_view const& str, size_type off = 0) const noexcept; size_type find_first_not_of(string_view const& str, size_type off = 0) const noexcept; size_type find_first_not_of_ci(string_view const& str, size_type off = 0) const noexcept; size_type find_last_of(char ch, size_type off = npos) const noexcept; size_type find_last_of_ci(char ch, size_type off = npos) const noexcept; size_type find_last_of(char const* cstr, size_type off = npos) const noexcept; size_type find_last_of_ci(char const* cstr, size_type off = npos) const noexcept; size_type find_last_of(string const& str, size_type off = npos) const noexcept; size_type find_last_of_ci(string const& str, size_type off = npos) const noexcept; size_type find_last_of(std::string_view const& str, size_type off = npos) const noexcept; size_type find_last_of_ci(std::string_view const& str, size_type off = npos) const noexcept; size_type find_last_of(string_view const& str, size_type off = npos) const noexcept; size_type find_last_of_ci(string_view const& str, size_type off = npos) const noexcept; size_type find_last_not_of(char ch, size_type off = npos) const noexcept; size_type find_last_not_of_ci(char ch, size_type off = npos) const noexcept; size_type find_last_not_of(char const* cstr, size_type off = npos) const noexcept; size_type find_last_not_of_ci(char const* cstr, size_type off = npos) const noexcept; size_type find_last_not_of(string const& str, size_type off = npos) const noexcept; size_type find_last_not_of_ci(string const& str, size_type off = npos) const noexcept; size_type find_last_not_of(std::string_view const& str, size_type off = npos) const noexcept; size_type find_last_not_of_ci(std::string_view const& str, size_type off = npos) const noexcept; size_type find_last_not_of(string_view const& str, size_type off = npos) const noexcept; size_type find_last_not_of_ci(string_view const& str, size_type off = npos) const noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; ////////////////////////////////////////////////////////////////////////// //specific methods //returns a murmur hash of this string uint32_t hash() const noexcept; //this is used to be able to calculate a compatible hash for a const char* //useful for doing stuff like find_as(const char*) in unordered_maps static uint32_t raw_hash(const char* c_str) noexcept; static uint32_t raw_hash(const char* str, size_t length) noexcept; //returns a key that is unique for this string, and all strings that compare equal to this string. size_t unique_key() const noexcept; static constexpr size_type npos = static_cast(-1); //Call this to free the internal string database. //By default the database doesn't deallocate itself when the application dies to avoid the static variable destruction hell. //In unit tests it's useful to free the database to avoid leak reports. //NOTE::: if when calling free_database there are still strings alive, the method will cause an intended crash !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! static void free_database() noexcept; private: void assign(const char* str, size_t size) noexcept; template string& _append(Str const& str, size_t pos, size_t count) noexcept; size_type find_first_of(char const* cstr, size_type cstr_size, size_type off) const noexcept; size_type find_first_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept; size_type find_first_not_of(char const* cstr, size_type cstr_size, size_type off) const noexcept; size_type find_first_not_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept; size_type find_last_of(char const* cstr, size_type cstr_size, size_type off) const noexcept; size_type find_last_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept; size_type find_last_not_of(char const* cstr, size_type cstr_size, size_type off) const noexcept; size_type find_last_not_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept; using cell_t = detail::string_cell; cell_t* m_cell = nullptr; }; ////////////////////////////////////////////////////////////////////////// // This operator needs to be inside TL namespace to avoid conflicts // with another global operators bool operator==(char const* cstr, string const& str) noexcept; bool operator==(std::string_view const& stdstr, string const& str) noexcept; bool operator==(string_view const& stdstr, string const& str) noexcept; string operator+(char const* cstr, string const& str) noexcept; string operator+(std::string_view const& stdstr, string const& str) noexcept; string operator+(string_view const& stdstr, string const& str) noexcept; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// namespace tl { inline bool memeq(const char* a, const char* b, size_t size) noexcept { const size_t alignment = ((size_t)a) | ((size_t)b); if ((alignment & (sizeof(size_t) - 1)) == 0) { while (size >= sizeof(size_t)) { const size_t sa = *(const size_t*)a; const size_t sb = *(const size_t*)b; if (sa != sb) return false; a += sizeof(size_t); b += sizeof(size_t); size -= sizeof(size_t); } } if ((alignment & (sizeof(uint32_t) - 1)) == 0) { while (size >= sizeof(uint32_t)) { const uint32_t sa = *(const uint32_t*)a; const uint32_t sb = *(const uint32_t*)b; if (sa != sb) return false; a += sizeof(uint32_t); b += sizeof(uint32_t); size -= sizeof(uint32_t); } } if ((alignment & (sizeof(uint16_t) - 1)) == 0) { while (size >= sizeof(uint16_t)) { const uint16_t sa = *(const uint16_t*)a; const uint16_t sb = *(const uint16_t*)b; if (sa != sb) return false; a += sizeof(uint16_t); b += sizeof(uint16_t); size -= sizeof(uint16_t); } } while (size >= sizeof(uint8_t)) { const uint8_t sa = *(const uint8_t*)a; const uint8_t sb = *(const uint8_t*)b; if (sa != sb) return false; a += sizeof(uint8_t); b += sizeof(uint8_t); size -= sizeof(uint8_t); } return true; } namespace ascii { constexpr bool isdigit(char c) noexcept { return (unsigned char)c - '0' < 10u; } constexpr bool isspace(char c) noexcept { return c == ' '; } constexpr char toupper(char c) noexcept { return c - (((unsigned char)(c - 'a') < 26) << 5); } constexpr char tolower(char c) noexcept { return c + (((unsigned char)(c - 'A') < 26) << 5); } constexpr int stricmp(const char* s1, const char* s2) noexcept { TL_PLAIN_ASSERT(s1 && s2); for (;;) { const char c1 = tolower(*s1); const char c2 = tolower(*s2); if (const int d = c1 - c2) return d; if (c1 == '\0') return 0; ++s1; ++s2; } } constexpr int strnicmp(const char* s1, const char* s2, size_t len) noexcept { TL_PLAIN_ASSERT(s1 && s2); for (; len > 0; len--) { const char c1 = tolower(*s1); const char c2 = tolower(*s2); if (const int d = c1 - c2) return d; if (c1 == '\0') return 0; ++s1; ++s2; } return 0; } constexpr int memicmp(const char* s1, const char* s2, size_t n) noexcept { TL_PLAIN_ASSERT(s1 && s2); if (n != 0) { do { const char c1 = tolower(*s1); const char c2 = tolower(*s2); if (const int d = c1 - c2) return d; s1++; s2++; } while (--n != 0); } return 0; } constexpr const char* memichr(const char* s, char ch, size_t n) noexcept { TL_PLAIN_ASSERT(s); if (n != 0) { ch = toupper(ch); do { if (tolower(*s) == ch) return s; s++; } while (--n != 0); } return nullptr; } constexpr const char* stristr(const char* in, const char* str) noexcept { TL_PLAIN_ASSERT(in && str); const char c = toupper(*str++); if (!c) return in; const size_t len = strlen(str); do { char sc = 0; do { sc = toupper(*in++); if (!sc) return nullptr; } while (sc != c); } while (strnicmp(in, str, len) != 0); return in - 1; } // adapted from here: http://src.gnu-darwin.org/src/lib/libc/string/strnstr.c.html constexpr const char* strnstr(const char* in, const char* str, size_t n) noexcept { char c, sc; if ((c = *str++) != '\0') { const size_t len = strlen(str); do { do { if (n-- < 1 || (sc = *in++) == '\0') return nullptr; } while (sc != c); if (len > n) return nullptr; } while (strncmp(in, str, len) != 0); in--; } return in; } } inline string::string(string const& str) noexcept { if (str.m_cell) { str.m_cell->inc_ref_counter(); m_cell = str.m_cell; } } inline string::string(string&& str) noexcept : m_cell(str.m_cell) { str.m_cell = nullptr; } inline string::string(const char* from, const char* to) noexcept { TL_PLAIN_ASSERT(from && to && to >= from); detail::string_db& tldb = detail::string_db::get_instance(); tldb.internalize(from, to, m_cell); } inline string::string(string const& str, size_type offset, size_type size) noexcept { TL_PLAIN_ASSERT(offset <= str.size()); if (offset + size > str.size()) [[unlikely]] { size = str.size() - offset; if (offset + size > str.size()) [[unlikely]] { TL_PLAIN_FAIL("Trying to read beyond the string end"); return; } } const char* c_str = str.c_str(); detail::string_db& tldb = detail::string_db::get_instance(); tldb.internalize(c_str + offset, c_str + offset + size, m_cell); } inline string::string(char const* str) noexcept { if (!str || *str == '\0') [[unlikely]] return; detail::string_db& tldb = detail::string_db::get_instance(); tldb.internalize(str, m_cell); } inline string::string(char const* str, size_type size) noexcept { if (!str) [[unlikely]] { TL_PLAIN_FAIL(); return; } if (size == 0) [[unlikely]] return; detail::string_db& tldb = detail::string_db::get_instance(); tldb.internalize(str, str + size, m_cell); } inline string::string(std::string_view const& str) noexcept { if (!str.empty()) [[likely]] { const char* data = str.data(); detail::string_db& tldb = detail::string_db::get_instance(); tldb.internalize(data, data + str.length(), m_cell); } } inline string::string(string_view const& str) noexcept { if (!str.empty()) [[likely]] { const char* data = str.data(); detail::string_db& tldb = detail::string_db::get_instance(); tldb.internalize(data, data + str.length(), m_cell); } } inline string::~string() noexcept { if (m_cell) [[likely]] { if (const auto db = detail::string_db::get_instance_ptr(); db && m_cell->dec_ref_counter()) db->free_cell(*m_cell); } } inline string& string::operator=(const char* str) noexcept { if (!str || *str == '\0') [[unlikely]] { assign(nullptr, 0); return *this; } assign(str, ::strlen(str)); return *this; } inline string& string::operator=(const std::string_view& str) noexcept { assign(str.data(), str.size()); return *this; } inline string& string::operator=(const string_view& str) noexcept { assign(str.data(), str.size()); return *this; } inline string& string::operator=(const string& str) noexcept { if (this == &str) return *this; if (str.m_cell) str.m_cell->inc_ref_counter(); cell_t* old_cell = m_cell; m_cell = str.m_cell; if (old_cell && old_cell->dec_ref_counter()) detail::string_db::get_instance_ptr()->free_cell(*old_cell); return *this; } inline void string::assign(const char* str, size_t size) noexcept { if (size == 0) [[unlikely]] { cell_t* old_cell = m_cell; m_cell = nullptr; if (old_cell && old_cell->dec_ref_counter()) detail::string_db::get_instance_ptr()->free_cell(*old_cell); } else { detail::string_db& tldb = detail::string_db::get_instance(); cell_t* old_cell = m_cell; tldb.internalize(str, str + size, m_cell); if (old_cell) tldb.free_cell(*old_cell); } } inline string& string::operator=(string&& str) noexcept { std::swap(m_cell, str.m_cell); return *this; } constexpr std::strong_ordering string::operator<=>(const string& str) const noexcept { if (m_cell == str.m_cell) return std::strong_ordering::equal; return ::strcmp(c_str(), str.c_str()) < 0 ? std::strong_ordering::less : std::strong_ordering::greater; } inline std::strong_ordering string::operator<=>(const std::string_view& str) const noexcept { return ::strcmp(c_str(), str.data()) < 0 ? std::strong_ordering::less : std::strong_ordering::greater; } inline std::strong_ordering string::operator<=>(const string_view& str) const noexcept { return ::strncmp(c_str(), str.data(), str.size()) < 0 ? std::strong_ordering::less : std::strong_ordering::greater; } inline std::strong_ordering string::operator<=>(const char* c_str) const noexcept { if (!c_str) [[unlikely]] return empty() ? std::strong_ordering::equal : std::strong_ordering::greater; return ::strcmp(this->c_str(), c_str) < 0 ? std::strong_ordering::less : std::strong_ordering::greater; } inline bool string::operator==(string const& str) const noexcept { return m_cell == str.m_cell; } inline bool string::operator!=(string const& str) const noexcept { return m_cell != str.m_cell; } inline bool string::operator==(std::string_view const& str) const noexcept { return compare(str) == 0; } inline bool string::operator!=(std::string_view const& str) const noexcept { return compare(str) != 0; } inline bool string::operator==(string_view const& str) const noexcept { return compare(str) == 0; } inline bool string::operator!=(string_view const& str) const noexcept { return compare(str) != 0; } inline bool string::operator==(char const* str) const noexcept { return compare(str) == 0; } inline bool string::operator!=(char const* str) const noexcept { return compare(str) != 0; } inline string string::operator+(string const& str) const noexcept { string ret(*this); ret.append(str); return ret; } inline string& string::operator+=(string const& str) noexcept { append(str); return *this; } inline string string::operator+(std::string_view const& str) const noexcept { string ret(*this); ret.append(str); return ret; } inline string& string::operator+=(std::string_view const& str) noexcept { append(str); return *this; } inline string string::operator+(string_view const& str) const noexcept { string ret(*this); ret.append(str); return ret; } inline string& string::operator+=(string_view const& str) noexcept { append(str); return *this; } inline string string::operator+(char const* cstr) const noexcept { string ret(*this); ret.append(cstr); return ret; } inline string& string::operator+=(char const* cstr) noexcept { append(cstr); return *this; } inline string string::operator+(char ch) const noexcept { string ret(*this); ret.append(ch); return ret; } inline string& string::operator+=(char ch) noexcept { append(ch); return *this; } inline string::operator std::string_view() const noexcept { return m_cell ? std::string_view{ cell_data(m_cell), m_cell->length } : std::string_view{}; } inline string::operator string_view() const noexcept { return m_cell ? string_view{ cell_data(m_cell), m_cell->length } : string_view{}; } inline char string::operator[](size_type index) const noexcept { if (m_cell) [[likely]] { TL_PLAIN_ASSERT(index < m_cell->length); return cell_data(m_cell)[index]; } else { TL_PLAIN_FAIL("Cell not assigned"); return 0; } } inline char string::back() const noexcept { if (m_cell) [[likely]] { TL_PLAIN_ASSERT(m_cell->length > 0); return cell_data(m_cell)[m_cell->length - 1]; } else { TL_PLAIN_FAIL("Cell not assigned"); return 0; } } inline char string::front() const noexcept { if (m_cell) [[likely]] { TL_PLAIN_ASSERT(m_cell->length > 0); return cell_data(m_cell)[0]; } else { TL_PLAIN_FAIL("Cell not assigned"); return 0; } } inline void string::push_back(char ch) noexcept { append(ch); } inline char const* string::c_str() const noexcept { return m_cell ? cell_data(m_cell) : ""; } inline char const* string::data() const noexcept { return m_cell ? cell_data(m_cell) : ""; } inline std::string string::std_str() const noexcept { return m_cell ? std::string(cell_data(m_cell), cell_data(m_cell) + m_cell->length) : std::string(); } inline eastl::string string::eastl_str() const noexcept { return m_cell ? eastl::string(cell_data(m_cell), cell_data(m_cell) + m_cell->length) : eastl::string(); } inline void string::clear() noexcept { *this = string(); } inline string::size_type string::length() const noexcept { return m_cell ? m_cell->length : 0; } inline string::size_type string::size() const noexcept { return m_cell ? m_cell->length : 0; } inline bool string::empty() const noexcept { return m_cell == nullptr; } inline string::size_type string::find(char ch, size_type off) const noexcept { return find_first_of(ch, off); } inline string::size_type string::find_ci(char ch, size_type off) const noexcept { return find_first_of_ci(ch, off); } inline string::size_type string::find(char const* cstr, size_type off) const noexcept { if (!cstr || cstr[0] == '\0') [[unlikely]] return off <= size() ? off : npos; if (off >= size()) [[unlikely]] return npos; char const* string = cell_data(m_cell); //we know the cell is non-null because off < size char const* c = ::strstr(string + off, cstr); return c ? c - string : npos; } inline string::size_type string::find_ci(char const* cstr, size_type off) const noexcept { if (!cstr || cstr[0] == '\0') [[unlikely]] return off <= size() ? off : npos; if (off >= size()) [[unlikely]] return npos; char const* string = cell_data(m_cell); //we know the cell is non-null because off < size char const* c = ascii::stristr(string + off, cstr); return c ? c - string : npos; } inline string::size_type string::find(string const& str, size_type off) const noexcept { return find(str.data(), str.size(), off); } inline string::size_type string::find_ci(string const& str, size_type off) const noexcept { return find_ci(str.data(), str.size(), off); } inline string::size_type string::find(std::string_view const& str, size_type off) const noexcept { return find(str.data(), str.size(), off); } inline string::size_type string::find_ci(std::string_view const& str, size_type off) const noexcept { return find_ci(str.data(), str.size(), off); } inline string::size_type string::find(string_view const& str, size_type off) const noexcept { return find(str.data(), str.size(), off); } inline string::size_type string::find_ci(string_view const& str, size_type off) const noexcept { return find_ci(str.data(), str.size(), off); } inline string::size_type string::find_first_of(char const* cstr, size_type off) const noexcept { return cstr ? find_first_of(cstr, strlen(cstr), off) : off; } inline string::size_type string::find_first_of_ci(char const* cstr, size_type off) const noexcept { return cstr ? find_first_of_ci(cstr, strlen(cstr), off) : off; } inline string::size_type string::find_first_of(string const& str, size_type off) const noexcept { return find_first_of(str.c_str(), str.size(), off); } inline string::size_type string::find_first_of_ci(string const& str, size_type off) const noexcept { return find_first_of_ci(str.c_str(), str.size(), off); } inline string::size_type string::find_first_of(std::string_view const& str, size_type off) const noexcept { return find_first_of(str.data(), str.size(), off); } inline string::size_type string::find_first_of_ci(std::string_view const& str, size_type off) const noexcept { return find_first_of_ci(str.data(), str.size(), off); } inline string::size_type string::find_first_of(string_view const& str, size_type off) const noexcept { return find_first_of(str.data(), str.size(), off); } inline string::size_type string::find_first_of_ci(string_view const& str, size_type off) const noexcept { return find_first_of_ci(str.data(), str.size(), off); } inline string::size_type string::find_first_not_of(string const& str, size_type off) const noexcept { return find_first_not_of(str.c_str(), str.size(), off); } inline string::size_type string::find_first_not_of_ci(string const& str, size_type off) const noexcept { return find_first_not_of_ci(str.c_str(), str.size(), off); } inline string::size_type string::find_first_not_of(std::string_view const& str, size_type off) const noexcept { return find_first_not_of(str.data(), str.size(), off); } inline string::size_type string::find_first_not_of_ci(std::string_view const& str, size_type off) const noexcept { return find_first_not_of_ci(str.data(), str.size(), off); } inline string::size_type string::find_first_not_of(string_view const& str, size_type off) const noexcept { return find_first_not_of(str.data(), str.size(), off); } inline string::size_type string::find_first_not_of_ci(string_view const& str, size_type off) const noexcept { return find_first_not_of_ci(str.data(), str.size(), off); } ////////////////////////////////////////////////////////////////////////// inline string::size_type string::find_last_of(string const& str, size_type off) const noexcept { return find_last_of(str.c_str(), str.size(), off); } inline string::size_type string::find_last_of_ci(string const& str, size_type off) const noexcept { return find_last_of_ci(str.c_str(), str.size(), off); } inline string::size_type string::find_last_of(std::string_view const& str, size_type off) const noexcept { return find_last_of(str.data(), str.size(), off); } inline string::size_type string::find_last_of_ci(std::string_view const& str, size_type off) const noexcept { return find_last_of_ci(str.data(), str.size(), off); } inline string::size_type string::find_last_of(string_view const& str, size_type off) const noexcept { return find_last_of(str.data(), str.size(), off); } inline string::size_type string::find_last_of_ci(string_view const& str, size_type off) const noexcept { return find_last_of_ci(str.data(), str.size(), off); } inline string::size_type string::find_last_not_of(string const& str, size_type off) const noexcept { return find_last_not_of(str.c_str(), str.size(), off); } inline string::size_type string::find_last_not_of_ci(string const& str, size_type off) const noexcept { return find_last_not_of_ci(str.c_str(), str.size(), off); } inline string::size_type string::find_last_not_of(std::string_view const& str, size_type off) const noexcept { return find_last_not_of(str.data(), str.size(), off); } inline string::size_type string::find_last_not_of_ci(std::string_view const& str, size_type off) const noexcept { return find_last_not_of_ci(str.data(), str.size(), off); } inline string::size_type string::find_last_not_of(string_view const& str, size_type off) const noexcept { return find_last_not_of(str.data(), str.size(), off); } inline string::size_type string::find_last_not_of_ci(string_view const& str, size_type off) const noexcept { return find_last_not_of_ci(str.data(), str.size(), off); } inline string::const_iterator string::begin() const noexcept { if (!m_cell) [[unlikely]] return nullptr; return cell_data(m_cell); } inline string::const_iterator string::end() const noexcept { if (!m_cell) [[unlikely]] return nullptr; return cell_data(m_cell) + m_cell->length; } inline void string::reserve(size_type) noexcept { // no way to reserve an interned string for now } inline string string::substr(size_type off, size_type count) const noexcept { if (count == npos) count = size() - off; TL_PLAIN_ASSERT(off + count <= size()); if (count == 0) [[unlikely]] return {}; const char* b = c_str() + off; return {b, b + count}; } inline void string::resize(size_type s) noexcept { if (size() == s) return; if (s == 0) { clear(); return; } if (s < size()) { const char* b = c_str(); *this = string(b, b + s); } else { fixed_vector b(s); memcpy(b.data(), c_str(), size()); memset(b.data() + size(), 0, s - size()); *this = string(b.data(), s); } } inline void string::swap(string& other) noexcept { std::swap(m_cell, other.m_cell); } inline int string::compare(string const& str) const noexcept { return m_cell == str.m_cell ? 0 : ::strcmp(c_str(), str.c_str()); } inline int string::compare_ci(string const& str) const noexcept { return m_cell == str.m_cell ? 0 : ascii::stricmp(c_str(), str.c_str()); } inline int string::compare(std::string_view const& str) const noexcept { return ::strncmp(c_str(), str.data(), str.size()); } inline int string::compare_ci(std::string_view const& str) const noexcept { return ascii::strnicmp(c_str(), str.data(), str.size()); } inline int string::compare(string_view const& str) const noexcept { return ::strncmp(c_str(), str.data(), str.size()); } inline int string::compare_ci(string_view const& str) const noexcept { return ascii::strnicmp(c_str(), str.data(), str.size()); } inline int string::compare(char const* str) const noexcept { TL_PLAIN_ASSERT(str); if (!str) [[unlikely]] return 1; return ::strcmp(c_str(), str); } inline int string::compare_ci(char const* str) const noexcept { TL_PLAIN_ASSERT(str); if (!str) [[unlikely]] return 1; return ascii::stricmp(c_str(), str); } inline uint32_t string::hash() const noexcept { return m_cell ? m_cell->hash : 0; } inline uint32_t string::raw_hash(const char* c_str) noexcept { if (!c_str || c_str[0] == 0) [[unlikely]] return 0; return murmur32(c_str, strlen(c_str), 0); } inline uint32_t string::raw_hash(const char* c_str, size_t length) noexcept { if (!c_str || c_str[0] == 0 || length == 0) [[unlikely]] return 0; return murmur32(c_str, length, 0); } inline size_t string::unique_key() const noexcept { return reinterpret_cast(m_cell); } inline bool operator==(char const* cstr, string const& str) noexcept { return str == cstr; } inline bool operator==(std::string_view const& stdstr, string const& str) noexcept { return str == stdstr; } inline bool operator==(string_view const& stdstr, string const& str) noexcept { return str == stdstr; } inline string operator+(char const* cstr, string const& str) noexcept { return string(cstr) + str; } inline string operator+(std::string_view const& stdstr, string const& str) noexcept { return string(stdstr) + str; } inline string operator+(string_view const& stdstr, string const& str) noexcept { return string(stdstr) + str; } template string& string::_append(Str const& str, size_t pos, size_t count) noexcept { const size_type sz = size(); const size_type strsz = count == npos ? str.size() - pos : count; //first some early outs if (strsz == 0) return *this; if (sz == 0) { *this = string(str.data() + pos, strsz); return *this; } detail::string_db& tldb = detail::string_db::get_instance(); cell_t* old_cell = m_cell; tldb.append_internalize(*old_cell, str.data() + pos, strsz, m_cell); if (old_cell->dec_ref_counter()) detail::string_db::get_instance_ptr()->free_cell(*old_cell); return *this; } } template <> struct std::formatter { constexpr auto parse(format_parse_context& ctx) noexcept { return ctx.begin(); } auto format(const tl::string& s, std::format_context& ctx) const { return format_to(ctx.out(), "{}", s.c_str()); } }; template <> struct std::formatter { constexpr auto parse(format_parse_context& ctx) noexcept { return ctx.begin(); } auto format(const eastl::string& s, std::format_context& ctx) const { return format_to(ctx.out(), "{}", s.c_str()); } }; template <> struct std::hash { size_t operator()(tl::string const& s) const noexcept { return static_cast(s.hash()); } }; template <> struct eastl::hash { size_t operator()(tl::string const& s) const { return static_cast(s.hash()); } }; struct tl_string_key_less { bool operator()(const tl::string& a, const tl::string& b) const { return a.unique_key() < b.unique_key(); } }; #undef cell_data