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