1 //===-- ConstString.cpp -----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "lldb/Utility/ConstString.h"
10
11 #include "lldb/Utility/Stream.h"
12
13 #include "llvm/ADT/StringMap.h"
14 #include "llvm/ADT/iterator.h"
15 #include "llvm/Support/Allocator.h"
16 #include "llvm/Support/DJB.h"
17 #include "llvm/Support/FormatProviders.h"
18 #include "llvm/Support/RWMutex.h"
19 #include "llvm/Support/Threading.h"
20
21 #include <array>
22 #include <utility>
23
24 #include <inttypes.h>
25 #include <stdint.h>
26 #include <string.h>
27
28 using namespace lldb_private;
29
30 class Pool {
31 public:
32 typedef const char *StringPoolValueType;
33 typedef llvm::StringMap<StringPoolValueType, llvm::BumpPtrAllocator>
34 StringPool;
35 typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;
36
37 static StringPoolEntryType &
GetStringMapEntryFromKeyData(const char * keyData)38 GetStringMapEntryFromKeyData(const char *keyData) {
39 return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);
40 }
41
GetConstCStringLength(const char * ccstr)42 static size_t GetConstCStringLength(const char *ccstr) {
43 if (ccstr != nullptr) {
44 // Since the entry is read only, and we derive the entry entirely from
45 // the pointer, we don't need the lock.
46 const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);
47 return entry.getKey().size();
48 }
49 return 0;
50 }
51
GetMangledCounterpart(const char * ccstr) const52 StringPoolValueType GetMangledCounterpart(const char *ccstr) const {
53 if (ccstr != nullptr) {
54 const uint8_t h = hash(llvm::StringRef(ccstr));
55 llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
56 return GetStringMapEntryFromKeyData(ccstr).getValue();
57 }
58 return nullptr;
59 }
60
GetConstCString(const char * cstr)61 const char *GetConstCString(const char *cstr) {
62 if (cstr != nullptr)
63 return GetConstCStringWithLength(cstr, strlen(cstr));
64 return nullptr;
65 }
66
GetConstCStringWithLength(const char * cstr,size_t cstr_len)67 const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) {
68 if (cstr != nullptr)
69 return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len));
70 return nullptr;
71 }
72
GetConstCStringWithStringRef(const llvm::StringRef & string_ref)73 const char *GetConstCStringWithStringRef(const llvm::StringRef &string_ref) {
74 if (string_ref.data()) {
75 const uint8_t h = hash(string_ref);
76
77 {
78 llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
79 auto it = m_string_pools[h].m_string_map.find(string_ref);
80 if (it != m_string_pools[h].m_string_map.end())
81 return it->getKeyData();
82 }
83
84 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
85 StringPoolEntryType &entry =
86 *m_string_pools[h]
87 .m_string_map.insert(std::make_pair(string_ref, nullptr))
88 .first;
89 return entry.getKeyData();
90 }
91 return nullptr;
92 }
93
94 const char *
GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,const char * mangled_ccstr)95 GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,
96 const char *mangled_ccstr) {
97 const char *demangled_ccstr = nullptr;
98
99 {
100 const uint8_t h = hash(demangled);
101 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
102
103 // Make or update string pool entry with the mangled counterpart
104 StringPool &map = m_string_pools[h].m_string_map;
105 StringPoolEntryType &entry = *map.try_emplace(demangled).first;
106
107 entry.second = mangled_ccstr;
108
109 // Extract the const version of the demangled_cstr
110 demangled_ccstr = entry.getKeyData();
111 }
112
113 {
114 // Now assign the demangled const string as the counterpart of the
115 // mangled const string...
116 const uint8_t h = hash(llvm::StringRef(mangled_ccstr));
117 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
118 GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);
119 }
120
121 // Return the constant demangled C string
122 return demangled_ccstr;
123 }
124
GetConstTrimmedCStringWithLength(const char * cstr,size_t cstr_len)125 const char *GetConstTrimmedCStringWithLength(const char *cstr,
126 size_t cstr_len) {
127 if (cstr != nullptr) {
128 const size_t trimmed_len = strnlen(cstr, cstr_len);
129 return GetConstCStringWithLength(cstr, trimmed_len);
130 }
131 return nullptr;
132 }
133
134 // Return the size in bytes that this object and any items in its collection
135 // of uniqued strings + data count values takes in memory.
MemorySize() const136 size_t MemorySize() const {
137 size_t mem_size = sizeof(Pool);
138 for (const auto &pool : m_string_pools) {
139 llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
140 for (const auto &entry : pool.m_string_map)
141 mem_size += sizeof(StringPoolEntryType) + entry.getKey().size();
142 }
143 return mem_size;
144 }
145
146 protected:
hash(const llvm::StringRef & s) const147 uint8_t hash(const llvm::StringRef &s) const {
148 uint32_t h = llvm::djbHash(s);
149 return ((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff;
150 }
151
152 struct PoolEntry {
153 mutable llvm::sys::SmartRWMutex<false> m_mutex;
154 StringPool m_string_map;
155 };
156
157 std::array<PoolEntry, 256> m_string_pools;
158 };
159
160 // Frameworks and dylibs aren't supposed to have global C++ initializers so we
161 // hide the string pool in a static function so that it will get initialized on
162 // the first call to this static function.
163 //
164 // Note, for now we make the string pool a pointer to the pool, because we
165 // can't guarantee that some objects won't get destroyed after the global
166 // destructor chain is run, and trying to make sure no destructors touch
167 // ConstStrings is difficult. So we leak the pool instead.
StringPool()168 static Pool &StringPool() {
169 static llvm::once_flag g_pool_initialization_flag;
170 static Pool *g_string_pool = nullptr;
171
172 llvm::call_once(g_pool_initialization_flag,
173 []() { g_string_pool = new Pool(); });
174
175 return *g_string_pool;
176 }
177
ConstString(const char * cstr)178 ConstString::ConstString(const char *cstr)
179 : m_string(StringPool().GetConstCString(cstr)) {}
180
ConstString(const char * cstr,size_t cstr_len)181 ConstString::ConstString(const char *cstr, size_t cstr_len)
182 : m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {}
183
ConstString(const llvm::StringRef & s)184 ConstString::ConstString(const llvm::StringRef &s)
185 : m_string(StringPool().GetConstCStringWithStringRef(s)) {}
186
operator <(ConstString rhs) const187 bool ConstString::operator<(ConstString rhs) const {
188 if (m_string == rhs.m_string)
189 return false;
190
191 llvm::StringRef lhs_string_ref(GetStringRef());
192 llvm::StringRef rhs_string_ref(rhs.GetStringRef());
193
194 // If both have valid C strings, then return the comparison
195 if (lhs_string_ref.data() && rhs_string_ref.data())
196 return lhs_string_ref < rhs_string_ref;
197
198 // Else one of them was nullptr, so if LHS is nullptr then it is less than
199 return lhs_string_ref.data() == nullptr;
200 }
201
operator <<(Stream & s,ConstString str)202 Stream &lldb_private::operator<<(Stream &s, ConstString str) {
203 const char *cstr = str.GetCString();
204 if (cstr != nullptr)
205 s << cstr;
206
207 return s;
208 }
209
GetLength() const210 size_t ConstString::GetLength() const {
211 return Pool::GetConstCStringLength(m_string);
212 }
213
Equals(ConstString lhs,ConstString rhs,const bool case_sensitive)214 bool ConstString::Equals(ConstString lhs, ConstString rhs,
215 const bool case_sensitive) {
216 if (lhs.m_string == rhs.m_string)
217 return true;
218
219 // Since the pointers weren't equal, and identical ConstStrings always have
220 // identical pointers, the result must be false for case sensitive equality
221 // test.
222 if (case_sensitive)
223 return false;
224
225 // perform case insensitive equality test
226 llvm::StringRef lhs_string_ref(lhs.GetStringRef());
227 llvm::StringRef rhs_string_ref(rhs.GetStringRef());
228 return lhs_string_ref.equals_lower(rhs_string_ref);
229 }
230
Compare(ConstString lhs,ConstString rhs,const bool case_sensitive)231 int ConstString::Compare(ConstString lhs, ConstString rhs,
232 const bool case_sensitive) {
233 // If the iterators are the same, this is the same string
234 const char *lhs_cstr = lhs.m_string;
235 const char *rhs_cstr = rhs.m_string;
236 if (lhs_cstr == rhs_cstr)
237 return 0;
238 if (lhs_cstr && rhs_cstr) {
239 llvm::StringRef lhs_string_ref(lhs.GetStringRef());
240 llvm::StringRef rhs_string_ref(rhs.GetStringRef());
241
242 if (case_sensitive) {
243 return lhs_string_ref.compare(rhs_string_ref);
244 } else {
245 return lhs_string_ref.compare_lower(rhs_string_ref);
246 }
247 }
248
249 if (lhs_cstr)
250 return +1; // LHS isn't nullptr but RHS is
251 else
252 return -1; // LHS is nullptr but RHS isn't
253 }
254
Dump(Stream * s,const char * fail_value) const255 void ConstString::Dump(Stream *s, const char *fail_value) const {
256 if (s != nullptr) {
257 const char *cstr = AsCString(fail_value);
258 if (cstr != nullptr)
259 s->PutCString(cstr);
260 }
261 }
262
DumpDebug(Stream * s) const263 void ConstString::DumpDebug(Stream *s) const {
264 const char *cstr = GetCString();
265 size_t cstr_len = GetLength();
266 // Only print the parens if we have a non-nullptr string
267 const char *parens = cstr ? "\"" : "";
268 s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,
269 static_cast<int>(sizeof(void *) * 2),
270 static_cast<const void *>(this), parens, cstr, parens,
271 static_cast<uint64_t>(cstr_len));
272 }
273
SetCString(const char * cstr)274 void ConstString::SetCString(const char *cstr) {
275 m_string = StringPool().GetConstCString(cstr);
276 }
277
SetString(const llvm::StringRef & s)278 void ConstString::SetString(const llvm::StringRef &s) {
279 m_string = StringPool().GetConstCStringWithLength(s.data(), s.size());
280 }
281
SetStringWithMangledCounterpart(llvm::StringRef demangled,ConstString mangled)282 void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled,
283 ConstString mangled) {
284 m_string = StringPool().GetConstCStringAndSetMangledCounterPart(
285 demangled, mangled.m_string);
286 }
287
GetMangledCounterpart(ConstString & counterpart) const288 bool ConstString::GetMangledCounterpart(ConstString &counterpart) const {
289 counterpart.m_string = StringPool().GetMangledCounterpart(m_string);
290 return (bool)counterpart;
291 }
292
SetCStringWithLength(const char * cstr,size_t cstr_len)293 void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) {
294 m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);
295 }
296
SetTrimmedCStringWithLength(const char * cstr,size_t cstr_len)297 void ConstString::SetTrimmedCStringWithLength(const char *cstr,
298 size_t cstr_len) {
299 m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len);
300 }
301
StaticMemorySize()302 size_t ConstString::StaticMemorySize() {
303 // Get the size of the static string pool
304 return StringPool().MemorySize();
305 }
306
format(const ConstString & CS,llvm::raw_ostream & OS,llvm::StringRef Options)307 void llvm::format_provider<ConstString>::format(const ConstString &CS,
308 llvm::raw_ostream &OS,
309 llvm::StringRef Options) {
310 format_provider<StringRef>::format(CS.AsCString(), OS, Options);
311 }
312