File: stringrefid.cpp

package info (click to toggle)
openmw 0.49.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,992 kB
  • sloc: cpp: 372,479; xml: 2,149; sh: 1,403; python: 797; makefile: 26
file content (148 lines) | stat: -rw-r--r-- 4,443 bytes parent folder | download
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;
    }
}