Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "CStrIntern.h"
21 :
22 : #include "lib/fnv_hash.h"
23 : #include "ps/CLogger.h"
24 : #include "ps/ThreadUtil.h"
25 :
26 : #include <unordered_map>
27 :
28 164 : class CStrInternInternals
29 : {
30 : public:
31 164 : CStrInternInternals(const char* str, size_t len)
32 164 : : data(str, str+len), hash(fnv_hash(str, len))
33 : {
34 : // LOGWARNING("New interned string '%s'", data.c_str());
35 164 : }
36 :
37 : bool operator==(const CStrInternInternals& b) const
38 : {
39 : // Compare hash first for quick rejection of inequal strings
40 : return (hash == b.hash && data == b.data);
41 : }
42 :
43 : const std::string data;
44 : const u32 hash; // fnv_hash of data
45 :
46 : private:
47 : CStrInternInternals& operator=(const CStrInternInternals&);
48 : };
49 :
50 : // Interned strings are stored in a hash table, indexed by string:
51 :
52 : using StringsKey = std::string;
53 :
54 : struct StringsKeyHash
55 : {
56 534 : size_t operator()(const StringsKey& key) const
57 : {
58 534 : return fnv_hash(key.c_str(), key.length());
59 : }
60 : };
61 :
62 : // To avoid std::string memory allocations when GetString does lookups in the
63 : // hash table of interned strings, we make use of std::unordered_map's ability
64 : // to do lookups with a functionally equivalent proxy object:
65 :
66 : struct StringsKeyProxy
67 : {
68 : const char* str;
69 : size_t len;
70 : };
71 :
72 : struct StringsKeyProxyHash
73 : {
74 : size_t operator()(const StringsKeyProxy& key) const
75 : {
76 : return fnv_hash(key.str, key.len);
77 : }
78 : };
79 :
80 : struct StringsKeyProxyEq
81 : {
82 : bool operator()(const StringsKeyProxy& proxy, const StringsKey& key) const
83 : {
84 : return (proxy.len == key.length() && memcmp(proxy.str, key.c_str(), proxy.len) == 0);
85 : }
86 : };
87 :
88 1 : static std::unordered_map<StringsKey, std::shared_ptr<CStrInternInternals>, StringsKeyHash> g_Strings;
89 :
90 : #define X(id) CStrIntern str_##id(#id);
91 : #define X2(id, str) CStrIntern str_##id(str);
92 : #include "CStrInternStatic.h"
93 : #undef X
94 : #undef X2
95 :
96 370 : static CStrInternInternals* GetString(const char* str, size_t len)
97 : {
98 : // g_Strings is not thread-safe, so complain if anyone is using this
99 : // type in non-main threads. (If that's desired, g_Strings should be changed
100 : // to be thread-safe, preferably without sacrificing performance.)
101 370 : ENSURE(Threading::IsMainThread());
102 :
103 370 : std::unordered_map<StringsKey, std::shared_ptr<CStrInternInternals> >::iterator it = g_Strings.find(str);
104 :
105 370 : if (it != g_Strings.end())
106 206 : return it->second.get();
107 :
108 328 : std::shared_ptr<CStrInternInternals> internals = std::make_shared<CStrInternInternals>(str, len);
109 164 : g_Strings.insert(std::make_pair(internals->data, internals));
110 164 : return internals.get();
111 : }
112 :
113 1935 : CStrIntern::CStrIntern()
114 : {
115 1935 : *this = str__emptystring;
116 1935 : }
117 :
118 199 : CStrIntern::CStrIntern(const char* str)
119 : {
120 199 : m = GetString(str, strlen(str));
121 199 : }
122 :
123 171 : CStrIntern::CStrIntern(const std::string& str)
124 : {
125 171 : m = GetString(str.c_str(), str.length());
126 171 : }
127 :
128 3221 : u32 CStrIntern::GetHash() const
129 : {
130 3221 : return m->hash;
131 : }
132 :
133 66 : const char* CStrIntern::c_str() const
134 : {
135 66 : return m->data.c_str();
136 : }
137 :
138 0 : size_t CStrIntern::length() const
139 : {
140 0 : return m->data.length();
141 : }
142 :
143 0 : bool CStrIntern::empty() const
144 : {
145 0 : return m->data.empty();
146 : }
147 :
148 70 : const std::string& CStrIntern::string() const
149 : {
150 70 : return m->data;
151 3 : }
|