Files
TL/include/tl/string.h
T
jeanlemotan 8297b0b45f First
2024-07-02 18:06:33 +02:00

1191 lines
35 KiB
C++

#pragma once
#include "tl/detail/prologue.h"
#include <string>
#include <compare>
#include <format>
#include <EASTL/string.h>
#include <tl/fixed_vector.h>
#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<char*>(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<size_type>(-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<typename Str>
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<char, 512> 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<size_t>(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<typename Str>
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<tl::string>
{
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<eastl::string>
{
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<tl::string>
{
size_t operator()(tl::string const& s) const noexcept
{
return static_cast<size_t>(s.hash());
}
};
template <> struct eastl::hash<tl::string>
{
size_t operator()(tl::string const& s) const
{
return static_cast<size_t>(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