341 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- ConstString.cpp -----------------------------------------*- C++ -*-===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "lldb/Utility/ConstString.h"
 | |
| 
 | |
| #include "lldb/Utility/Stream.h"
 | |
| 
 | |
| #include "llvm/ADT/StringExtras.h"
 | |
| #include "llvm/ADT/StringMap.h"
 | |
| #include "llvm/ADT/iterator.h"            // for iterator_facade_base
 | |
| #include "llvm/Support/Allocator.h"       // for BumpPtrAllocator
 | |
| #include "llvm/Support/FormatProviders.h" // for format_provider
 | |
| #include "llvm/Support/RWMutex.h"
 | |
| #include "llvm/Support/Threading.h"
 | |
| 
 | |
| #include <algorithm> // for min
 | |
| #include <array>
 | |
| #include <utility> // for make_pair, pair
 | |
| 
 | |
| #include <inttypes.h> // for PRIu64
 | |
| #include <stdint.h>   // for uint8_t, uint32_t, uint64_t
 | |
| #include <string.h>   // for size_t, strlen
 | |
| 
 | |
| using namespace lldb_private;
 | |
| 
 | |
| class Pool {
 | |
| public:
 | |
|   typedef const char *StringPoolValueType;
 | |
|   typedef llvm::StringMap<StringPoolValueType, llvm::BumpPtrAllocator>
 | |
|       StringPool;
 | |
|   typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;
 | |
| 
 | |
|   static StringPoolEntryType &
 | |
|   GetStringMapEntryFromKeyData(const char *keyData) {
 | |
|     return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);
 | |
|   }
 | |
| 
 | |
|   static size_t GetConstCStringLength(const char *ccstr) {
 | |
|     if (ccstr != nullptr) {
 | |
|       // Since the entry is read only, and we derive the entry entirely from the
 | |
|       // pointer, we don't need the lock.
 | |
|       const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);
 | |
|       return entry.getKey().size();
 | |
|     }
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   StringPoolValueType GetMangledCounterpart(const char *ccstr) const {
 | |
|     if (ccstr != nullptr) {
 | |
|       const uint8_t h = hash(llvm::StringRef(ccstr));
 | |
|       llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
 | |
|       return GetStringMapEntryFromKeyData(ccstr).getValue();
 | |
|     }
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   bool SetMangledCounterparts(const char *key_ccstr, const char *value_ccstr) {
 | |
|     if (key_ccstr != nullptr && value_ccstr != nullptr) {
 | |
|       {
 | |
|         const uint8_t h = hash(llvm::StringRef(key_ccstr));
 | |
|         llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
 | |
|         GetStringMapEntryFromKeyData(key_ccstr).setValue(value_ccstr);
 | |
|       }
 | |
|       {
 | |
|         const uint8_t h = hash(llvm::StringRef(value_ccstr));
 | |
|         llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
 | |
|         GetStringMapEntryFromKeyData(value_ccstr).setValue(key_ccstr);
 | |
|       }
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   const char *GetConstCString(const char *cstr) {
 | |
|     if (cstr != nullptr)
 | |
|       return GetConstCStringWithLength(cstr, strlen(cstr));
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) {
 | |
|     if (cstr != nullptr)
 | |
|       return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len));
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   const char *GetConstCStringWithStringRef(const llvm::StringRef &string_ref) {
 | |
|     if (string_ref.data()) {
 | |
|       const uint8_t h = hash(string_ref);
 | |
| 
 | |
|       {
 | |
|         llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
 | |
|         auto it = m_string_pools[h].m_string_map.find(string_ref);
 | |
|         if (it != m_string_pools[h].m_string_map.end())
 | |
|           return it->getKeyData();
 | |
|       }
 | |
| 
 | |
|       llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
 | |
|       StringPoolEntryType &entry =
 | |
|           *m_string_pools[h]
 | |
|                .m_string_map.insert(std::make_pair(string_ref, nullptr))
 | |
|                .first;
 | |
|       return entry.getKeyData();
 | |
|     }
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   const char *
 | |
|   GetConstCStringAndSetMangledCounterPart(const char *demangled_cstr,
 | |
|                                           const char *mangled_ccstr) {
 | |
|     if (demangled_cstr != nullptr) {
 | |
|       const char *demangled_ccstr = nullptr;
 | |
| 
 | |
|       {
 | |
|         llvm::StringRef string_ref(demangled_cstr);
 | |
|         const uint8_t h = hash(string_ref);
 | |
|         llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
 | |
| 
 | |
|         // Make string pool entry with the mangled counterpart already set
 | |
|         StringPoolEntryType &entry =
 | |
|             *m_string_pools[h]
 | |
|                  .m_string_map.insert(std::make_pair(string_ref, mangled_ccstr))
 | |
|                  .first;
 | |
| 
 | |
|         // Extract the const version of the demangled_cstr
 | |
|         demangled_ccstr = entry.getKeyData();
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Now assign the demangled const string as the counterpart of the
 | |
|         // mangled const string...
 | |
|         const uint8_t h = hash(llvm::StringRef(mangled_ccstr));
 | |
|         llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
 | |
|         GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);
 | |
|       }
 | |
| 
 | |
|       // Return the constant demangled C string
 | |
|       return demangled_ccstr;
 | |
|     }
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   const char *GetConstTrimmedCStringWithLength(const char *cstr,
 | |
|                                                size_t cstr_len) {
 | |
|     if (cstr != nullptr) {
 | |
|       const size_t trimmed_len = std::min<size_t>(strlen(cstr), cstr_len);
 | |
|       return GetConstCStringWithLength(cstr, trimmed_len);
 | |
|     }
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   //------------------------------------------------------------------
 | |
|   // Return the size in bytes that this object and any items in its
 | |
|   // collection of uniqued strings + data count values takes in
 | |
|   // memory.
 | |
|   //------------------------------------------------------------------
 | |
|   size_t MemorySize() const {
 | |
|     size_t mem_size = sizeof(Pool);
 | |
|     for (const auto &pool : m_string_pools) {
 | |
|       llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
 | |
|       for (const auto &entry : pool.m_string_map)
 | |
|         mem_size += sizeof(StringPoolEntryType) + entry.getKey().size();
 | |
|     }
 | |
|     return mem_size;
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   uint8_t hash(const llvm::StringRef &s) const {
 | |
|     uint32_t h = llvm::HashString(s);
 | |
|     return ((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff;
 | |
|   }
 | |
| 
 | |
|   struct PoolEntry {
 | |
|     mutable llvm::sys::SmartRWMutex<false> m_mutex;
 | |
|     StringPool m_string_map;
 | |
|   };
 | |
| 
 | |
|   std::array<PoolEntry, 256> m_string_pools;
 | |
| };
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| // Frameworks and dylibs aren't supposed to have global C++
 | |
| // initializers so we hide the string pool in a static function so
 | |
| // that it will get initialized on the first call to this static
 | |
| // function.
 | |
| //
 | |
| // Note, for now we make the string pool a pointer to the pool, because
 | |
| // we can't guarantee that some objects won't get destroyed after the
 | |
| // global destructor chain is run, and trying to make sure no destructors
 | |
| // touch ConstStrings is difficult.  So we leak the pool instead.
 | |
| //----------------------------------------------------------------------
 | |
| static Pool &StringPool() {
 | |
|   static llvm::once_flag g_pool_initialization_flag;
 | |
|   static Pool *g_string_pool = nullptr;
 | |
| 
 | |
|   llvm::call_once(g_pool_initialization_flag,
 | |
|                  []() { g_string_pool = new Pool(); });
 | |
| 
 | |
|   return *g_string_pool;
 | |
| }
 | |
| 
 | |
| ConstString::ConstString(const char *cstr)
 | |
|     : m_string(StringPool().GetConstCString(cstr)) {}
 | |
| 
 | |
| ConstString::ConstString(const char *cstr, size_t cstr_len)
 | |
|     : m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {}
 | |
| 
 | |
| ConstString::ConstString(const llvm::StringRef &s)
 | |
|     : m_string(StringPool().GetConstCStringWithLength(s.data(), s.size())) {}
 | |
| 
 | |
| bool ConstString::operator<(const ConstString &rhs) const {
 | |
|   if (m_string == rhs.m_string)
 | |
|     return false;
 | |
| 
 | |
|   llvm::StringRef lhs_string_ref(GetStringRef());
 | |
|   llvm::StringRef rhs_string_ref(rhs.GetStringRef());
 | |
| 
 | |
|   // If both have valid C strings, then return the comparison
 | |
|   if (lhs_string_ref.data() && rhs_string_ref.data())
 | |
|     return lhs_string_ref < rhs_string_ref;
 | |
| 
 | |
|   // Else one of them was nullptr, so if LHS is nullptr then it is less than
 | |
|   return lhs_string_ref.data() == nullptr;
 | |
| }
 | |
| 
 | |
| Stream &lldb_private::operator<<(Stream &s, const ConstString &str) {
 | |
|   const char *cstr = str.GetCString();
 | |
|   if (cstr != nullptr)
 | |
|     s << cstr;
 | |
| 
 | |
|   return s;
 | |
| }
 | |
| 
 | |
| size_t ConstString::GetLength() const {
 | |
|   return Pool::GetConstCStringLength(m_string);
 | |
| }
 | |
| 
 | |
| bool ConstString::Equals(const ConstString &lhs, const ConstString &rhs,
 | |
|                          const bool case_sensitive) {
 | |
|   if (lhs.m_string == rhs.m_string)
 | |
|     return true;
 | |
| 
 | |
|   // Since the pointers weren't equal, and identical ConstStrings always have
 | |
|   // identical pointers,
 | |
|   // the result must be false for case sensitive equality test.
 | |
|   if (case_sensitive)
 | |
|     return false;
 | |
| 
 | |
|   // perform case insensitive equality test
 | |
|   llvm::StringRef lhs_string_ref(lhs.GetStringRef());
 | |
|   llvm::StringRef rhs_string_ref(rhs.GetStringRef());
 | |
|   return lhs_string_ref.equals_lower(rhs_string_ref);
 | |
| }
 | |
| 
 | |
| int ConstString::Compare(const ConstString &lhs, const ConstString &rhs,
 | |
|                          const bool case_sensitive) {
 | |
|   // If the iterators are the same, this is the same string
 | |
|   const char *lhs_cstr = lhs.m_string;
 | |
|   const char *rhs_cstr = rhs.m_string;
 | |
|   if (lhs_cstr == rhs_cstr)
 | |
|     return 0;
 | |
|   if (lhs_cstr && rhs_cstr) {
 | |
|     llvm::StringRef lhs_string_ref(lhs.GetStringRef());
 | |
|     llvm::StringRef rhs_string_ref(rhs.GetStringRef());
 | |
| 
 | |
|     if (case_sensitive) {
 | |
|       return lhs_string_ref.compare(rhs_string_ref);
 | |
|     } else {
 | |
|       return lhs_string_ref.compare_lower(rhs_string_ref);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (lhs_cstr)
 | |
|     return +1; // LHS isn't nullptr but RHS is
 | |
|   else
 | |
|     return -1; // LHS is nullptr but RHS isn't
 | |
| }
 | |
| 
 | |
| void ConstString::Dump(Stream *s, const char *fail_value) const {
 | |
|   if (s != nullptr) {
 | |
|     const char *cstr = AsCString(fail_value);
 | |
|     if (cstr != nullptr)
 | |
|       s->PutCString(cstr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ConstString::DumpDebug(Stream *s) const {
 | |
|   const char *cstr = GetCString();
 | |
|   size_t cstr_len = GetLength();
 | |
|   // Only print the parens if we have a non-nullptr string
 | |
|   const char *parens = cstr ? "\"" : "";
 | |
|   s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,
 | |
|             static_cast<int>(sizeof(void *) * 2),
 | |
|             static_cast<const void *>(this), parens, cstr, parens,
 | |
|             static_cast<uint64_t>(cstr_len));
 | |
| }
 | |
| 
 | |
| void ConstString::SetCString(const char *cstr) {
 | |
|   m_string = StringPool().GetConstCString(cstr);
 | |
| }
 | |
| 
 | |
| void ConstString::SetString(const llvm::StringRef &s) {
 | |
|   m_string = StringPool().GetConstCStringWithLength(s.data(), s.size());
 | |
| }
 | |
| 
 | |
| void ConstString::SetCStringWithMangledCounterpart(const char *demangled,
 | |
|                                                    const ConstString &mangled) {
 | |
|   m_string = StringPool().GetConstCStringAndSetMangledCounterPart(
 | |
|       demangled, mangled.m_string);
 | |
| }
 | |
| 
 | |
| bool ConstString::GetMangledCounterpart(ConstString &counterpart) const {
 | |
|   counterpart.m_string = StringPool().GetMangledCounterpart(m_string);
 | |
|   return (bool)counterpart;
 | |
| }
 | |
| 
 | |
| void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) {
 | |
|   m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);
 | |
| }
 | |
| 
 | |
| void ConstString::SetTrimmedCStringWithLength(const char *cstr,
 | |
|                                               size_t cstr_len) {
 | |
|   m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len);
 | |
| }
 | |
| 
 | |
| size_t ConstString::StaticMemorySize() {
 | |
|   // Get the size of the static string pool
 | |
|   return StringPool().MemorySize();
 | |
| }
 | |
| 
 | |
| void llvm::format_provider<ConstString>::format(const ConstString &CS,
 | |
|                                                 llvm::raw_ostream &OS,
 | |
|                                                 llvm::StringRef Options) {
 | |
|   format_provider<StringRef>::format(CS.AsCString(), OS, Options);
 | |
| }
 |