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