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