1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
#include "stringrefid.hpp"
#include "serializerefid.hpp"
#include <charconv>
#include <iomanip>
#include <mutex>
#include <ostream>
#include <sstream>
#include <system_error>
#include <unordered_set>
#include "components/misc/guarded.hpp"
#include "components/misc/strings/algorithm.hpp"
#include "components/misc/utf8stream.hpp"
namespace ESM
{
namespace
{
using StringsSet = std::unordered_set<std::string, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>;
const std::string emptyString;
Misc::ScopeGuarded<StringsSet>& getRefIds()
{
static Misc::ScopeGuarded<StringsSet> refIds;
return refIds;
}
Misc::NotNullPtr<const std::string> getOrInsertString(std::string_view id)
{
const auto locked = getRefIds().lock();
auto it = locked->find(id);
if (it == locked->end())
it = locked->emplace(id).first;
return &*it;
}
void addHex(unsigned char value, std::string& result)
{
const std::size_t size = 2 + getHexIntegralSize(value);
const std::size_t shift = result.size();
result.resize(shift + size);
result[shift] = '\\';
result[shift + 1] = 'x';
const auto [end, ec] = std::to_chars(result.data() + shift + 2, result.data() + result.size(), value, 16);
if (ec != std::errc())
throw std::system_error(std::make_error_code(ec));
}
}
StringRefId::StringRefId()
: mValue(&emptyString)
{
}
StringRefId::StringRefId(std::string_view value)
: mValue(getOrInsertString(value))
{
}
bool StringRefId::operator==(std::string_view rhs) const noexcept
{
return Misc::StringUtils::ciEqual(*mValue, rhs);
}
bool StringRefId::operator<(StringRefId rhs) const noexcept
{
return Misc::StringUtils::ciLess(*mValue, *rhs.mValue);
}
bool operator<(StringRefId lhs, std::string_view rhs) noexcept
{
return Misc::StringUtils::ciLess(*lhs.mValue, rhs);
}
bool operator<(std::string_view lhs, StringRefId rhs) noexcept
{
return Misc::StringUtils::ciLess(lhs, *rhs.mValue);
}
std::ostream& operator<<(std::ostream& stream, StringRefId value)
{
return stream << value.toDebugString();
}
std::string StringRefId::toDebugString() const
{
std::string result;
result.reserve(2 + mValue->size());
result.push_back('"');
const unsigned char* ptr = reinterpret_cast<const unsigned char*>(mValue->data());
const unsigned char* const end = reinterpret_cast<const unsigned char*>(mValue->data() + mValue->size());
while (ptr != end)
{
if (Utf8Stream::isAscii(*ptr))
{
if (std::isprint(*ptr) && *ptr != '\t' && *ptr != '\n' && *ptr != '\r')
result.push_back(*ptr);
else
addHex(*ptr, result);
++ptr;
continue;
}
const auto [octets, first] = Utf8Stream::getOctetCount(*ptr);
const auto [chr, next] = Utf8Stream::decode(ptr + 1, end, first, octets);
if (chr == Utf8Stream::sBadChar())
{
while (ptr != std::min(end, ptr + octets))
{
addHex(*ptr, result);
++ptr;
}
continue;
}
result.append(ptr, next);
ptr = next;
}
result.push_back('"');
return result;
}
bool StringRefId::startsWith(std::string_view prefix) const
{
return Misc::StringUtils::ciStartsWith(*mValue, prefix);
}
bool StringRefId::endsWith(std::string_view suffix) const
{
return Misc::StringUtils::ciEndsWith(*mValue, suffix);
}
bool StringRefId::contains(std::string_view subString) const
{
return Misc::StringUtils::ciFind(*mValue, subString) != std::string_view::npos;
}
std::optional<StringRefId> StringRefId::deserializeExisting(std::string_view value)
{
const auto locked = getRefIds().lock();
auto it = locked->find(value);
if (it == locked->end())
return {};
StringRefId id;
id.mValue = &*it;
return id;
}
}
|