mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 01:26:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			148 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #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;
 | |
|     }
 | |
| }
 |