1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "CStrIntern.h"
22 #include "lib/fnv_hash.h"
23 #include "ps/CLogger.h"
24 #include "ps/ThreadUtil.h"
26 #include <unordered_map>
28 class CStrInternInternals
31 CStrInternInternals(const char* str
, size_t len
)
32 : data(str
, str
+len
), hash(fnv_hash(str
, len
))
34 // LOGWARNING("New interned string '%s'", data.c_str());
37 bool operator==(const CStrInternInternals
& b
) const
39 // Compare hash first for quick rejection of inequal strings
40 return (hash
== b
.hash
&& data
== b
.data
);
43 const std::string data
;
44 const u32 hash
; // fnv_hash of data
47 CStrInternInternals
& operator=(const CStrInternInternals
&);
50 // Interned strings are stored in a hash table, indexed by string:
52 using StringsKey
= std::string
;
56 size_t operator()(const StringsKey
& key
) const
58 return fnv_hash(key
.c_str(), key
.length());
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:
66 struct StringsKeyProxy
72 struct StringsKeyProxyHash
74 size_t operator()(const StringsKeyProxy
& key
) const
76 return fnv_hash(key
.str
, key
.len
);
80 struct StringsKeyProxyEq
82 bool operator()(const StringsKeyProxy
& proxy
, const StringsKey
& key
) const
84 return (proxy
.len
== key
.length() && memcmp(proxy
.str
, key
.c_str(), proxy
.len
) == 0);
88 static std::unordered_map
<StringsKey
, std::shared_ptr
<CStrInternInternals
>, StringsKeyHash
> g_Strings
;
90 #define X(id) CStrIntern str_##id(#id);
91 #define X2(id, str) CStrIntern str_##id(str);
92 #include "CStrInternStatic.h"
96 static CStrInternInternals
* GetString(const char* str
, size_t len
)
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 ENSURE(Threading::IsMainThread());
103 std::unordered_map
<StringsKey
, std::shared_ptr
<CStrInternInternals
> >::iterator it
= g_Strings
.find(str
);
105 if (it
!= g_Strings
.end())
106 return it
->second
.get();
108 std::shared_ptr
<CStrInternInternals
> internals
= std::make_shared
<CStrInternInternals
>(str
, len
);
109 g_Strings
.insert(std::make_pair(internals
->data
, internals
));
110 return internals
.get();
113 CStrIntern::CStrIntern()
115 *this = str__emptystring
;
118 CStrIntern::CStrIntern(const char* str
)
120 m
= GetString(str
, strlen(str
));
123 CStrIntern::CStrIntern(const std::string
& str
)
125 m
= GetString(str
.c_str(), str
.length());
128 u32
CStrIntern::GetHash() const
133 const char* CStrIntern::c_str() const
135 return m
->data
.c_str();
138 size_t CStrIntern::length() const
140 return m
->data
.length();
143 bool CStrIntern::empty() const
145 return m
->data
.empty();
148 const std::string
& CStrIntern::string() const