summaryrefslogtreecommitdiff
path: root/src/interned_string.hh
blob: d875829d930d16bac7daa5e7226302b439a54f0c (plain)
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
#ifndef interned_string_hh_INCLUDED
#define interned_string_hh_INCLUDED

#include "string.hh"
#include "utils.hh"

#include <unordered_map>

namespace Kakoune
{

class InternedString;

class StringRegistry : public Singleton<StringRegistry>
{
private:
    friend class InternedString;

    InternedString acquire(StringView str);
    void acquire(size_t slot);
    void release(size_t slot) noexcept;

    std::unordered_map<StringView, size_t> m_slot_map;
    std::vector<size_t> m_free_slots;
    using DataAndRefCount = std::pair<std::vector<char>, int>;
    std::vector<DataAndRefCount> m_storage;
};

class InternedString : public StringView
{
public:
    InternedString() = default;

    InternedString(const InternedString& str) : InternedString(str, str.m_slot)
    {
        if (m_slot != -1)
            StringRegistry::instance().acquire(m_slot);
    }

    InternedString(InternedString&& str) noexcept : StringView(str)
    {
        m_slot = str.m_slot;
        str.m_slot = -1;
    }

    InternedString(const char* str) : StringView() { acquire_ifn(str); }
    InternedString(StringView str) : StringView() { acquire_ifn(str); }

    InternedString& operator=(const InternedString& str)
    {
        if (str.data() == data() and str.length() == length())
            return *this;
        static_cast<StringView&>(*this) = str;
        if (str.m_slot != m_slot)
        {
            release_ifn();
            m_slot = str.m_slot;
            if (str.m_slot != -1)
                StringRegistry::instance().acquire(str.m_slot);
        }

        return *this;
    }

    InternedString& operator=(InternedString&& str) noexcept
    {
        release_ifn();

        static_cast<StringView&>(*this) = str;
        m_slot = str.m_slot;
        str.m_slot = -1;
        return *this;
    }

    ~InternedString()
    {
        release_ifn();
    }

    using StringView::operator==;
    using StringView::operator!=;

    InternedString acquire_substr(ByteCount from, ByteCount length = INT_MAX) const
    {
        if (m_slot == -1)
            return InternedString{};
        StringRegistry::instance().acquire(m_slot);
        return InternedString{StringView::substr(from, length), m_slot};
    }
    InternedString acquire_substr(CharCount from, CharCount length = INT_MAX) const
    {
        if (m_slot == -1)
            return InternedString{};
        StringRegistry::instance().acquire(m_slot);
        return InternedString{StringView::substr(from, length), m_slot};
    }

private:
    friend class StringRegistry;

    InternedString(StringView str, size_t slot)
        : StringView(str), m_slot(slot) {}

    void acquire_ifn(StringView str)
    {
        if (str.empty())
        {
            static_cast<StringView&>(*this) = StringView{};
            m_slot = -1;
        }
        else
            *this = StringRegistry::instance().acquire(str);
    }

    void release_ifn() noexcept
    {
        if (m_slot != -1)
            StringRegistry::instance().release(m_slot);
    }

    size_t m_slot = -1;
};

}

namespace std
{
    template<>
    struct hash<Kakoune::InternedString>
    {
        size_t operator()(const Kakoune::InternedString& str) const
        {
            return Kakoune::hash_data(str.data(), (int)str.length());
        }
    };
}

#endif // interned_string_hh_INCLUDED