260 lines
7.1 KiB
C++
260 lines
7.1 KiB
C++
#pragma once
|
|
|
|
#include "RE/B/BSCRC32.h"
|
|
#include "RE/B/BSStringPool.h"
|
|
|
|
namespace RE
|
|
{
|
|
namespace detail
|
|
{
|
|
template <class CharT, bool CS>
|
|
class BSFixedString
|
|
{
|
|
public:
|
|
using size_type = std::uint32_t;
|
|
using value_type = CharT;
|
|
using pointer = value_type*;
|
|
using const_pointer = const value_type*;
|
|
using reference = value_type&;
|
|
using const_reference = const value_type&;
|
|
|
|
BSFixedString() noexcept = default;
|
|
BSFixedString(const volatile BSFixedString&) = delete;
|
|
BSFixedString& operator=(const volatile BSFixedString&) = delete;
|
|
|
|
template <bool B>
|
|
BSFixedString(const BSFixedString<value_type, B>& a_rhs) :
|
|
_data(a_rhs._data)
|
|
{
|
|
try_acquire();
|
|
}
|
|
|
|
template <bool B>
|
|
BSFixedString(BSFixedString<value_type, B>&& a_rhs) noexcept :
|
|
_data(std::exchange(a_rhs._data, nullptr))
|
|
{}
|
|
|
|
BSFixedString(const_pointer a_string)
|
|
{
|
|
if (a_string) {
|
|
GetEntry<value_type>(_data, a_string, CS);
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
BSFixedString(const T& a_string) //
|
|
requires(std::convertible_to<const T&, std::basic_string_view<value_type>> &&
|
|
!std::convertible_to<const T&, const_pointer> &&
|
|
!std::same_as<T, BSFixedString<value_type, true>> &&
|
|
!std::same_as<T, BSFixedString<value_type, false>>)
|
|
{
|
|
const auto view = static_cast<std::basic_string_view<value_type>>(a_string);
|
|
if (!view.empty()) {
|
|
assert(view.data()[view.length()] == value_type{});
|
|
GetEntry<value_type>(_data, view.data(), CS);
|
|
}
|
|
}
|
|
|
|
~BSFixedString() { try_release(); }
|
|
|
|
template <bool B>
|
|
BSFixedString& operator=(const BSFixedString<value_type, B>& a_rhs)
|
|
{
|
|
if (this != std::addressof(a_rhs)) {
|
|
try_release();
|
|
_data = a_rhs._data;
|
|
try_acquire();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <bool B>
|
|
BSFixedString& operator=(BSFixedString<value_type, B>&& a_rhs)
|
|
{
|
|
if (this != std::addressof(a_rhs)) {
|
|
std::swap(this->_data, a_rhs._data);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
BSFixedString& operator=(const_pointer a_string)
|
|
{
|
|
try_release();
|
|
if (a_string) {
|
|
GetEntry<value_type>(_data, a_string, CS);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <class T>
|
|
BSFixedString& operator=(const T& a_string) //
|
|
requires(std::convertible_to<const T&, std::basic_string_view<value_type>> &&
|
|
!std::convertible_to<const T&, const_pointer> &&
|
|
!std::same_as<T, BSFixedString<value_type, true>> &&
|
|
!std::same_as<T, BSFixedString<value_type, false>>)
|
|
{
|
|
const auto view = static_cast<std::basic_string_view<value_type>>(a_string);
|
|
try_release();
|
|
if (!view.empty()) {
|
|
assert(view.data()[view.length()] == value_type{});
|
|
GetEntry<value_type>(_data, view.data(), CS);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
[[nodiscard]] const_reference front() const noexcept { return data()[0]; }
|
|
[[nodiscard]] const_reference back() const noexcept { return data()[size() - 1]; }
|
|
|
|
[[nodiscard]] const_pointer data() const noexcept
|
|
{
|
|
const auto cstr = _data ? _data->data<value_type>() : nullptr;
|
|
return cstr ? cstr : EMPTY;
|
|
}
|
|
|
|
[[nodiscard]] const_pointer c_str() const noexcept { return data(); }
|
|
|
|
[[nodiscard]] operator std::basic_string_view<value_type>() const noexcept { return { c_str(), length() }; }
|
|
|
|
[[nodiscard]] bool empty() const noexcept { return size() == 0; }
|
|
|
|
[[nodiscard]] size_type size() const noexcept { return _data ? _data->size() : 0; }
|
|
[[nodiscard]] size_type length() const noexcept { return _data ? _data->length() : 0; }
|
|
|
|
template <class T>
|
|
[[nodiscard]] friend bool operator==(const T& a_lhs, const T& a_rhs) noexcept
|
|
requires(std::same_as<T, BSFixedString>)
|
|
{
|
|
const auto leaf = [](const BSFixedString& a_elem) { return a_elem._data ? a_elem._data->leaf() : nullptr; };
|
|
const auto lLeaf = leaf(a_lhs);
|
|
const auto rLeaf = leaf(a_rhs);
|
|
if (lLeaf == rLeaf) {
|
|
return true;
|
|
} else if (a_lhs.empty() && a_rhs.empty()) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] friend bool operator==(const BSFixedString& a_lhs, std::basic_string_view<value_type> a_rhs)
|
|
{
|
|
if (a_lhs.empty() && a_rhs.empty()) {
|
|
return true;
|
|
} else if (const auto length = a_lhs.length(); length != a_rhs.length()) {
|
|
return false;
|
|
} else {
|
|
return strncmp(a_lhs.c_str(), a_rhs.data(), length) == 0;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] bool contains(std::basic_string_view<value_type> a_rhs) const
|
|
{
|
|
if (a_rhs.length() > length()) {
|
|
return false;
|
|
}
|
|
for (size_type i = 0; i < length(); ++i) {
|
|
if (strncmp(&c_str()[i], a_rhs.data(), a_rhs.length()) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
template <class>
|
|
friend struct RE::BSCRC32;
|
|
|
|
[[nodiscard]] const void* hash_accessor() const noexcept { return _data; }
|
|
|
|
private:
|
|
template <class, bool>
|
|
friend class BSFixedString;
|
|
|
|
[[nodiscard]] static int strncmp(const char* a_lhs, const char* a_rhs, std::size_t a_length)
|
|
{
|
|
if constexpr (CS) {
|
|
return std::strncmp(a_lhs, a_rhs, a_length);
|
|
} else {
|
|
return _strnicmp(a_lhs, a_rhs, a_length);
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] static int strncmp(const wchar_t* a_lhs, const wchar_t* a_rhs, std::size_t a_length)
|
|
{
|
|
if constexpr (CS) {
|
|
return std::wcsncmp(a_lhs, a_rhs, a_length);
|
|
} else {
|
|
return _wcsnicmp(a_lhs, a_rhs, a_length);
|
|
}
|
|
}
|
|
|
|
void try_acquire()
|
|
{
|
|
if (_data) {
|
|
_data->acquire();
|
|
}
|
|
}
|
|
|
|
void try_release() { BSStringPool::Entry::release(_data); }
|
|
|
|
static constexpr const value_type EMPTY[]{ 0 };
|
|
|
|
// members
|
|
BSStringPool::Entry* _data{ nullptr }; // 0
|
|
};
|
|
|
|
extern template class BSFixedString<char, false>;
|
|
extern template class BSFixedString<char, true>;
|
|
extern template class BSFixedString<wchar_t, false>;
|
|
extern template class BSFixedString<wchar_t, true>;
|
|
}
|
|
|
|
using BSFixedString = detail::BSFixedString<char, false>;
|
|
using BSFixedStringCS = detail::BSFixedString<char, true>;
|
|
using BSFixedStringW = detail::BSFixedString<wchar_t, false>;
|
|
using BSFixedStringWCS = detail::BSFixedString<wchar_t, true>;
|
|
|
|
namespace BSScript
|
|
{
|
|
template <class>
|
|
struct script_traits;
|
|
|
|
template <>
|
|
struct script_traits<RE::BSFixedString> final
|
|
{
|
|
using is_string = std::true_type;
|
|
};
|
|
|
|
template <>
|
|
struct script_traits<RE::BSFixedStringCS> final
|
|
{
|
|
using is_string = std::true_type;
|
|
};
|
|
}
|
|
|
|
template <class CharT, bool CS>
|
|
struct BSCRC32<detail::BSFixedString<CharT, CS>>
|
|
{
|
|
public:
|
|
[[nodiscard]] std::uint32_t operator()(const detail::BSFixedString<CharT, CS>& a_key) const noexcept
|
|
{
|
|
return BSCRC32<const void*>()(a_key.hash_accessor());
|
|
}
|
|
};
|
|
|
|
extern template struct BSCRC32<BSFixedString>;
|
|
extern template struct BSCRC32<BSFixedStringCS>;
|
|
extern template struct BSCRC32<BSFixedStringW>;
|
|
extern template struct BSCRC32<BSFixedStringWCS>;
|
|
}
|
|
|
|
template <class CharT, bool CS>
|
|
struct std::formatter<RE::detail::BSFixedString<CharT, CS>, CharT> : formatter<std::string, CharT>
|
|
{
|
|
template <class FormatContext>
|
|
constexpr auto format(const RE::detail::BSFixedString<CharT, CS>& a_version, FormatContext& a_ctx) const
|
|
{
|
|
return formatter<std::string, CharT>::format(a_version.c_str(), a_ctx);
|
|
}
|
|
};
|