1 //===-- ConstString.cpp -----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Utility/ConstString.h"
11 #include "lldb/Utility/Stream.h"
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"
29 using namespace lldb_private;
33 typedef const char *StringPoolValueType;
34 typedef llvm::StringMap<StringPoolValueType, llvm::BumpPtrAllocator>
36 typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;
38 static StringPoolEntryType &
39 GetStringMapEntryFromKeyData(const char *keyData) {
40 return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);
43 static size_t GetConstCStringLength(const char *ccstr) {
44 if (ccstr != nullptr) {
45 // Since the entry is read only, and we derive the entry entirely from
46 // the pointer, we don't need the lock.
47 const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);
48 return entry.getKey().size();
53 StringPoolValueType GetMangledCounterpart(const char *ccstr) const {
54 if (ccstr != nullptr) {
55 const uint8_t h = hash(llvm::StringRef(ccstr));
56 llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
57 return GetStringMapEntryFromKeyData(ccstr).getValue();
62 bool SetMangledCounterparts(const char *key_ccstr, const char *value_ccstr) {
63 if (key_ccstr != nullptr && value_ccstr != nullptr) {
65 const uint8_t h = hash(llvm::StringRef(key_ccstr));
66 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
67 GetStringMapEntryFromKeyData(key_ccstr).setValue(value_ccstr);
70 const uint8_t h = hash(llvm::StringRef(value_ccstr));
71 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
72 GetStringMapEntryFromKeyData(value_ccstr).setValue(key_ccstr);
79 const char *GetConstCString(const char *cstr) {
81 return GetConstCStringWithLength(cstr, strlen(cstr));
85 const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) {
87 return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len));
91 const char *GetConstCStringWithStringRef(const llvm::StringRef &string_ref) {
92 if (string_ref.data()) {
93 const uint8_t h = hash(string_ref);
96 llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
97 auto it = m_string_pools[h].m_string_map.find(string_ref);
98 if (it != m_string_pools[h].m_string_map.end())
99 return it->getKeyData();
102 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
103 StringPoolEntryType &entry =
105 .m_string_map.insert(std::make_pair(string_ref, nullptr))
107 return entry.getKeyData();
113 GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,
114 const char *mangled_ccstr) {
115 const char *demangled_ccstr = nullptr;
118 const uint8_t h = hash(demangled);
119 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
121 // Make or update string pool entry with the mangled counterpart
122 StringPool &map = m_string_pools[h].m_string_map;
123 StringPoolEntryType &entry = *map.try_emplace(demangled).first;
125 entry.second = mangled_ccstr;
127 // Extract the const version of the demangled_cstr
128 demangled_ccstr = entry.getKeyData();
132 // Now assign the demangled const string as the counterpart of the
133 // mangled const string...
134 const uint8_t h = hash(llvm::StringRef(mangled_ccstr));
135 llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
136 GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);
139 // Return the constant demangled C string
140 return demangled_ccstr;
143 const char *GetConstTrimmedCStringWithLength(const char *cstr,
145 if (cstr != nullptr) {
146 const size_t trimmed_len = strnlen(cstr, cstr_len);
147 return GetConstCStringWithLength(cstr, trimmed_len);
152 // Return the size in bytes that this object and any items in its collection
153 // of uniqued strings + data count values takes in memory.
154 size_t MemorySize() const {
155 size_t mem_size = sizeof(Pool);
156 for (const auto &pool : m_string_pools) {
157 llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
158 for (const auto &entry : pool.m_string_map)
159 mem_size += sizeof(StringPoolEntryType) + entry.getKey().size();
165 uint8_t hash(const llvm::StringRef &s) const {
166 uint32_t h = llvm::djbHash(s);
167 return ((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff;
171 mutable llvm::sys::SmartRWMutex<false> m_mutex;
172 StringPool m_string_map;
175 std::array<PoolEntry, 256> m_string_pools;
178 // Frameworks and dylibs aren't supposed to have global C++ initializers so we
179 // hide the string pool in a static function so that it will get initialized on
180 // the first call to this static function.
182 // Note, for now we make the string pool a pointer to the pool, because we
183 // can't guarantee that some objects won't get destroyed after the global
184 // destructor chain is run, and trying to make sure no destructors touch
185 // ConstStrings is difficult. So we leak the pool instead.
186 static Pool &StringPool() {
187 static llvm::once_flag g_pool_initialization_flag;
188 static Pool *g_string_pool = nullptr;
190 llvm::call_once(g_pool_initialization_flag,
191 []() { g_string_pool = new Pool(); });
193 return *g_string_pool;
196 ConstString::ConstString(const char *cstr)
197 : m_string(StringPool().GetConstCString(cstr)) {}
199 ConstString::ConstString(const char *cstr, size_t cstr_len)
200 : m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {}
202 ConstString::ConstString(const llvm::StringRef &s)
203 : m_string(StringPool().GetConstCStringWithStringRef(s)) {}
205 bool ConstString::operator<(ConstString rhs) const {
206 if (m_string == rhs.m_string)
209 llvm::StringRef lhs_string_ref(GetStringRef());
210 llvm::StringRef rhs_string_ref(rhs.GetStringRef());
212 // If both have valid C strings, then return the comparison
213 if (lhs_string_ref.data() && rhs_string_ref.data())
214 return lhs_string_ref < rhs_string_ref;
216 // Else one of them was nullptr, so if LHS is nullptr then it is less than
217 return lhs_string_ref.data() == nullptr;
220 Stream &lldb_private::operator<<(Stream &s, ConstString str) {
221 const char *cstr = str.GetCString();
228 size_t ConstString::GetLength() const {
229 return Pool::GetConstCStringLength(m_string);
232 bool ConstString::Equals(ConstString lhs, ConstString rhs,
233 const bool case_sensitive) {
234 if (lhs.m_string == rhs.m_string)
237 // Since the pointers weren't equal, and identical ConstStrings always have
238 // identical pointers, the result must be false for case sensitive equality
243 // perform case insensitive equality test
244 llvm::StringRef lhs_string_ref(lhs.GetStringRef());
245 llvm::StringRef rhs_string_ref(rhs.GetStringRef());
246 return lhs_string_ref.equals_lower(rhs_string_ref);
249 int ConstString::Compare(ConstString lhs, ConstString rhs,
250 const bool case_sensitive) {
251 // If the iterators are the same, this is the same string
252 const char *lhs_cstr = lhs.m_string;
253 const char *rhs_cstr = rhs.m_string;
254 if (lhs_cstr == rhs_cstr)
256 if (lhs_cstr && rhs_cstr) {
257 llvm::StringRef lhs_string_ref(lhs.GetStringRef());
258 llvm::StringRef rhs_string_ref(rhs.GetStringRef());
260 if (case_sensitive) {
261 return lhs_string_ref.compare(rhs_string_ref);
263 return lhs_string_ref.compare_lower(rhs_string_ref);
268 return +1; // LHS isn't nullptr but RHS is
270 return -1; // LHS is nullptr but RHS isn't
273 void ConstString::Dump(Stream *s, const char *fail_value) const {
275 const char *cstr = AsCString(fail_value);
281 void ConstString::DumpDebug(Stream *s) const {
282 const char *cstr = GetCString();
283 size_t cstr_len = GetLength();
284 // Only print the parens if we have a non-nullptr string
285 const char *parens = cstr ? "\"" : "";
286 s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,
287 static_cast<int>(sizeof(void *) * 2),
288 static_cast<const void *>(this), parens, cstr, parens,
289 static_cast<uint64_t>(cstr_len));
292 void ConstString::SetCString(const char *cstr) {
293 m_string = StringPool().GetConstCString(cstr);
296 void ConstString::SetString(const llvm::StringRef &s) {
297 m_string = StringPool().GetConstCStringWithLength(s.data(), s.size());
300 void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled,
301 ConstString mangled) {
302 m_string = StringPool().GetConstCStringAndSetMangledCounterPart(
303 demangled, mangled.m_string);
306 bool ConstString::GetMangledCounterpart(ConstString &counterpart) const {
307 counterpart.m_string = StringPool().GetMangledCounterpart(m_string);
308 return (bool)counterpart;
311 void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) {
312 m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);
315 void ConstString::SetTrimmedCStringWithLength(const char *cstr,
317 m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len);
320 size_t ConstString::StaticMemorySize() {
321 // Get the size of the static string pool
322 return StringPool().MemorySize();
325 void llvm::format_provider<ConstString>::format(const ConstString &CS,
326 llvm::raw_ostream &OS,
327 llvm::StringRef Options) {
328 format_provider<StringRef>::format(CS.AsCString(), OS, Options);