1191 lines
35 KiB
C++
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
|