180814287SRaphael Isemann //===-- ConstString.cpp ---------------------------------------------------===//
2bf9a7730SZachary Turner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bf9a7730SZachary Turner //
7bf9a7730SZachary Turner //===----------------------------------------------------------------------===//
8bf9a7730SZachary Turner
9bf9a7730SZachary Turner #include "lldb/Utility/ConstString.h"
10bf9a7730SZachary Turner
114479ac15SZachary Turner #include "lldb/Utility/Stream.h"
12bf9a7730SZachary Turner
13bf9a7730SZachary Turner #include "llvm/ADT/StringMap.h"
14672d2c12SJonas Devlieghere #include "llvm/ADT/iterator.h"
15672d2c12SJonas Devlieghere #include "llvm/Support/Allocator.h"
16672d2c12SJonas Devlieghere #include "llvm/Support/DJB.h"
17672d2c12SJonas Devlieghere #include "llvm/Support/FormatProviders.h"
18bf9a7730SZachary Turner #include "llvm/Support/RWMutex.h"
19c5f28e2aSKamil Rytarowski #include "llvm/Support/Threading.h"
204479ac15SZachary Turner
214479ac15SZachary Turner #include <array>
22672d2c12SJonas Devlieghere #include <utility>
234479ac15SZachary Turner
2476e47d48SRaphael Isemann #include <cinttypes>
2576e47d48SRaphael Isemann #include <cstdint>
2676e47d48SRaphael Isemann #include <cstring>
27bf9a7730SZachary Turner
28bf9a7730SZachary Turner using namespace lldb_private;
29bf9a7730SZachary Turner
30bf9a7730SZachary Turner class Pool {
31bf9a7730SZachary Turner public:
32fad012bcSRaphael Isemann /// The default BumpPtrAllocatorImpl slab size.
33fad012bcSRaphael Isemann static const size_t AllocatorSlabSize = 4096;
34fad012bcSRaphael Isemann static const size_t SizeThreshold = AllocatorSlabSize;
35fad012bcSRaphael Isemann /// Every Pool has its own allocator which receives an equal share of
36fad012bcSRaphael Isemann /// the ConstString allocations. This means that when allocating many
37fad012bcSRaphael Isemann /// ConstStrings, every allocator sees only its small share of allocations and
38fad012bcSRaphael Isemann /// assumes LLDB only allocated a small amount of memory so far. In reality
39fad012bcSRaphael Isemann /// LLDB allocated a total memory that is N times as large as what the
40fad012bcSRaphael Isemann /// allocator sees (where N is the number of string pools). This causes that
41fad012bcSRaphael Isemann /// the BumpPtrAllocator continues a long time to allocate memory in small
42fad012bcSRaphael Isemann /// chunks which only makes sense when allocating a small amount of memory
43fad012bcSRaphael Isemann /// (which is true from the perspective of a single allocator). On some
44fad012bcSRaphael Isemann /// systems doing all these small memory allocations causes LLDB to spend
45fad012bcSRaphael Isemann /// a lot of time in malloc, so we need to force all these allocators to
46fad012bcSRaphael Isemann /// behave like one allocator in terms of scaling their memory allocations
47fad012bcSRaphael Isemann /// with increased demand. To do this we set the growth delay for each single
48fad012bcSRaphael Isemann /// allocator to a rate so that our pool of allocators scales their memory
49fad012bcSRaphael Isemann /// allocations similar to a single BumpPtrAllocatorImpl.
50fad012bcSRaphael Isemann ///
51fad012bcSRaphael Isemann /// Currently we have 256 string pools and the normal growth delay of the
52fad012bcSRaphael Isemann /// BumpPtrAllocatorImpl is 128 (i.e., the memory allocation size increases
53fad012bcSRaphael Isemann /// every 128 full chunks), so by changing the delay to 1 we get a
54fad012bcSRaphael Isemann /// total growth delay in our allocator collection of 256/1 = 256. This is
55fad012bcSRaphael Isemann /// still only half as fast as a normal allocator but we can't go any faster
56fad012bcSRaphael Isemann /// without decreasing the number of string pools.
57fad012bcSRaphael Isemann static const size_t AllocatorGrowthDelay = 1;
58fad012bcSRaphael Isemann typedef llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, AllocatorSlabSize,
59fad012bcSRaphael Isemann SizeThreshold, AllocatorGrowthDelay>
60fad012bcSRaphael Isemann Allocator;
61bf9a7730SZachary Turner typedef const char *StringPoolValueType;
62fad012bcSRaphael Isemann typedef llvm::StringMap<StringPoolValueType, Allocator> StringPool;
63bf9a7730SZachary Turner typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;
64bf9a7730SZachary Turner
65bf9a7730SZachary Turner static StringPoolEntryType &
GetStringMapEntryFromKeyData(const char * keyData)66bf9a7730SZachary Turner GetStringMapEntryFromKeyData(const char *keyData) {
678070bf0aSPavel Labath return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);
68bf9a7730SZachary Turner }
69bf9a7730SZachary Turner
GetConstCStringLength(const char * ccstr)708070bf0aSPavel Labath static size_t GetConstCStringLength(const char *ccstr) {
71bf9a7730SZachary Turner if (ccstr != nullptr) {
7205097246SAdrian Prantl // Since the entry is read only, and we derive the entry entirely from
7305097246SAdrian Prantl // the pointer, we don't need the lock.
74bf9a7730SZachary Turner const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);
75bf9a7730SZachary Turner return entry.getKey().size();
76bf9a7730SZachary Turner }
77bf9a7730SZachary Turner return 0;
78bf9a7730SZachary Turner }
79bf9a7730SZachary Turner
GetMangledCounterpart(const char * ccstr)80*f6b38758SDavid Blaikie StringPoolValueType GetMangledCounterpart(const char *ccstr) {
81bf9a7730SZachary Turner if (ccstr != nullptr) {
82*f6b38758SDavid Blaikie const PoolEntry &pool = selectPool(llvm::StringRef(ccstr));
83*f6b38758SDavid Blaikie llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
84bf9a7730SZachary Turner return GetStringMapEntryFromKeyData(ccstr).getValue();
85bf9a7730SZachary Turner }
86bf9a7730SZachary Turner return nullptr;
87bf9a7730SZachary Turner }
88bf9a7730SZachary Turner
GetConstCString(const char * cstr)89bf9a7730SZachary Turner const char *GetConstCString(const char *cstr) {
90bf9a7730SZachary Turner if (cstr != nullptr)
91bf9a7730SZachary Turner return GetConstCStringWithLength(cstr, strlen(cstr));
92bf9a7730SZachary Turner return nullptr;
93bf9a7730SZachary Turner }
94bf9a7730SZachary Turner
GetConstCStringWithLength(const char * cstr,size_t cstr_len)95bf9a7730SZachary Turner const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) {
96bf9a7730SZachary Turner if (cstr != nullptr)
97bf9a7730SZachary Turner return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len));
98bf9a7730SZachary Turner return nullptr;
99bf9a7730SZachary Turner }
100bf9a7730SZachary Turner
GetConstCStringWithStringRef(llvm::StringRef string_ref)1018a463692SAlex Langford const char *GetConstCStringWithStringRef(llvm::StringRef string_ref) {
102bf9a7730SZachary Turner if (string_ref.data()) {
103*f6b38758SDavid Blaikie const uint32_t string_hash = StringPool::hash(string_ref);
104*f6b38758SDavid Blaikie PoolEntry &pool = selectPool(string_hash);
105bf9a7730SZachary Turner
106bf9a7730SZachary Turner {
107*f6b38758SDavid Blaikie llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
108*f6b38758SDavid Blaikie auto it = pool.m_string_map.find(string_ref, string_hash);
109*f6b38758SDavid Blaikie if (it != pool.m_string_map.end())
110bf9a7730SZachary Turner return it->getKeyData();
111bf9a7730SZachary Turner }
112bf9a7730SZachary Turner
113*f6b38758SDavid Blaikie llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);
114bf9a7730SZachary Turner StringPoolEntryType &entry =
115*f6b38758SDavid Blaikie *pool.m_string_map
116*f6b38758SDavid Blaikie .insert(std::make_pair(string_ref, nullptr), string_hash)
117bf9a7730SZachary Turner .first;
118bf9a7730SZachary Turner return entry.getKeyData();
119bf9a7730SZachary Turner }
120bf9a7730SZachary Turner return nullptr;
121bf9a7730SZachary Turner }
122bf9a7730SZachary Turner
123bf9a7730SZachary Turner const char *
GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,const char * mangled_ccstr)12419a357adSPavel Labath GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,
125bf9a7730SZachary Turner const char *mangled_ccstr) {
126bf9a7730SZachary Turner const char *demangled_ccstr = nullptr;
127bf9a7730SZachary Turner
128bf9a7730SZachary Turner {
129*f6b38758SDavid Blaikie const uint32_t demangled_hash = StringPool::hash(demangled);
130*f6b38758SDavid Blaikie PoolEntry &pool = selectPool(demangled_hash);
131*f6b38758SDavid Blaikie llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);
132bf9a7730SZachary Turner
1332397a2b6SStefan Granitz // Make or update string pool entry with the mangled counterpart
134*f6b38758SDavid Blaikie StringPool &map = pool.m_string_map;
135*f6b38758SDavid Blaikie StringPoolEntryType &entry =
136*f6b38758SDavid Blaikie *map.try_emplace_with_hash(demangled, demangled_hash).first;
1372397a2b6SStefan Granitz
1382397a2b6SStefan Granitz entry.second = mangled_ccstr;
139bf9a7730SZachary Turner
140bf9a7730SZachary Turner // Extract the const version of the demangled_cstr
141bf9a7730SZachary Turner demangled_ccstr = entry.getKeyData();
142bf9a7730SZachary Turner }
143bf9a7730SZachary Turner
144bf9a7730SZachary Turner {
145bf9a7730SZachary Turner // Now assign the demangled const string as the counterpart of the
146bf9a7730SZachary Turner // mangled const string...
147*f6b38758SDavid Blaikie PoolEntry &pool = selectPool(llvm::StringRef(mangled_ccstr));
148*f6b38758SDavid Blaikie llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);
149bf9a7730SZachary Turner GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);
150bf9a7730SZachary Turner }
151bf9a7730SZachary Turner
152bf9a7730SZachary Turner // Return the constant demangled C string
153bf9a7730SZachary Turner return demangled_ccstr;
154bf9a7730SZachary Turner }
155bf9a7730SZachary Turner
GetConstTrimmedCStringWithLength(const char * cstr,size_t cstr_len)156bf9a7730SZachary Turner const char *GetConstTrimmedCStringWithLength(const char *cstr,
157bf9a7730SZachary Turner size_t cstr_len) {
158bf9a7730SZachary Turner if (cstr != nullptr) {
159bb3609e4SJan Kratochvil const size_t trimmed_len = strnlen(cstr, cstr_len);
160bf9a7730SZachary Turner return GetConstCStringWithLength(cstr, trimmed_len);
161bf9a7730SZachary Turner }
162bf9a7730SZachary Turner return nullptr;
163bf9a7730SZachary Turner }
164bf9a7730SZachary Turner
GetMemoryStats() const165cd8122b2SJonas Devlieghere ConstString::MemoryStats GetMemoryStats() const {
166cd8122b2SJonas Devlieghere ConstString::MemoryStats stats;
167cd8122b2SJonas Devlieghere for (const auto &pool : m_string_pools) {
168cd8122b2SJonas Devlieghere llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
169cd8122b2SJonas Devlieghere const Allocator &alloc = pool.m_string_map.getAllocator();
170cd8122b2SJonas Devlieghere stats.bytes_total += alloc.getTotalMemory();
171cd8122b2SJonas Devlieghere stats.bytes_used += alloc.getBytesAllocated();
172cd8122b2SJonas Devlieghere }
173cd8122b2SJonas Devlieghere return stats;
174cd8122b2SJonas Devlieghere }
175cd8122b2SJonas Devlieghere
176bf9a7730SZachary Turner protected:
177bf9a7730SZachary Turner struct PoolEntry {
178bf9a7730SZachary Turner mutable llvm::sys::SmartRWMutex<false> m_mutex;
179755420c0SRaphael Isemann StringPool m_string_map;
180bf9a7730SZachary Turner };
181bf9a7730SZachary Turner
182bf9a7730SZachary Turner std::array<PoolEntry, 256> m_string_pools;
183*f6b38758SDavid Blaikie
selectPool(const llvm::StringRef & s)184*f6b38758SDavid Blaikie PoolEntry &selectPool(const llvm::StringRef &s) {
185*f6b38758SDavid Blaikie return selectPool(StringPool::hash(s));
186*f6b38758SDavid Blaikie }
187*f6b38758SDavid Blaikie
selectPool(uint32_t h)188*f6b38758SDavid Blaikie PoolEntry &selectPool(uint32_t h) {
189*f6b38758SDavid Blaikie return m_string_pools[((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff];
190*f6b38758SDavid Blaikie }
191bf9a7730SZachary Turner };
192bf9a7730SZachary Turner
19305097246SAdrian Prantl // Frameworks and dylibs aren't supposed to have global C++ initializers so we
19405097246SAdrian Prantl // hide the string pool in a static function so that it will get initialized on
19505097246SAdrian Prantl // the first call to this static function.
196bf9a7730SZachary Turner //
19705097246SAdrian Prantl // Note, for now we make the string pool a pointer to the pool, because we
19805097246SAdrian Prantl // can't guarantee that some objects won't get destroyed after the global
19905097246SAdrian Prantl // destructor chain is run, and trying to make sure no destructors touch
20005097246SAdrian Prantl // ConstStrings is difficult. So we leak the pool instead.
StringPool()201bf9a7730SZachary Turner static Pool &StringPool() {
202c5f28e2aSKamil Rytarowski static llvm::once_flag g_pool_initialization_flag;
203bf9a7730SZachary Turner static Pool *g_string_pool = nullptr;
204bf9a7730SZachary Turner
205c5f28e2aSKamil Rytarowski llvm::call_once(g_pool_initialization_flag,
206bf9a7730SZachary Turner []() { g_string_pool = new Pool(); });
207bf9a7730SZachary Turner
208bf9a7730SZachary Turner return *g_string_pool;
209bf9a7730SZachary Turner }
210bf9a7730SZachary Turner
ConstString(const char * cstr)211bf9a7730SZachary Turner ConstString::ConstString(const char *cstr)
212bf9a7730SZachary Turner : m_string(StringPool().GetConstCString(cstr)) {}
213bf9a7730SZachary Turner
ConstString(const char * cstr,size_t cstr_len)214bf9a7730SZachary Turner ConstString::ConstString(const char *cstr, size_t cstr_len)
215bf9a7730SZachary Turner : m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {}
216bf9a7730SZachary Turner
ConstString(llvm::StringRef s)2178a463692SAlex Langford ConstString::ConstString(llvm::StringRef s)
21812886f04SRaphael Isemann : m_string(StringPool().GetConstCStringWithStringRef(s)) {}
219bf9a7730SZachary Turner
operator <(ConstString rhs) const220ccdb5b4bSJonas Devlieghere bool ConstString::operator<(ConstString rhs) const {
221bf9a7730SZachary Turner if (m_string == rhs.m_string)
222bf9a7730SZachary Turner return false;
223bf9a7730SZachary Turner
2248070bf0aSPavel Labath llvm::StringRef lhs_string_ref(GetStringRef());
2258070bf0aSPavel Labath llvm::StringRef rhs_string_ref(rhs.GetStringRef());
226bf9a7730SZachary Turner
227bf9a7730SZachary Turner // If both have valid C strings, then return the comparison
228bf9a7730SZachary Turner if (lhs_string_ref.data() && rhs_string_ref.data())
229bf9a7730SZachary Turner return lhs_string_ref < rhs_string_ref;
230bf9a7730SZachary Turner
231bf9a7730SZachary Turner // Else one of them was nullptr, so if LHS is nullptr then it is less than
232bf9a7730SZachary Turner return lhs_string_ref.data() == nullptr;
233bf9a7730SZachary Turner }
234bf9a7730SZachary Turner
operator <<(Stream & s,ConstString str)235ccdb5b4bSJonas Devlieghere Stream &lldb_private::operator<<(Stream &s, ConstString str) {
236bf9a7730SZachary Turner const char *cstr = str.GetCString();
237bf9a7730SZachary Turner if (cstr != nullptr)
238bf9a7730SZachary Turner s << cstr;
239bf9a7730SZachary Turner
240bf9a7730SZachary Turner return s;
241bf9a7730SZachary Turner }
242bf9a7730SZachary Turner
GetLength() const243bf9a7730SZachary Turner size_t ConstString::GetLength() const {
2448070bf0aSPavel Labath return Pool::GetConstCStringLength(m_string);
245bf9a7730SZachary Turner }
246bf9a7730SZachary Turner
Equals(ConstString lhs,ConstString rhs,const bool case_sensitive)247ccdb5b4bSJonas Devlieghere bool ConstString::Equals(ConstString lhs, ConstString rhs,
248bf9a7730SZachary Turner const bool case_sensitive) {
249bf9a7730SZachary Turner if (lhs.m_string == rhs.m_string)
250bf9a7730SZachary Turner return true;
251bf9a7730SZachary Turner
252bf9a7730SZachary Turner // Since the pointers weren't equal, and identical ConstStrings always have
25305097246SAdrian Prantl // identical pointers, the result must be false for case sensitive equality
25405097246SAdrian Prantl // test.
255bf9a7730SZachary Turner if (case_sensitive)
256bf9a7730SZachary Turner return false;
257bf9a7730SZachary Turner
258bf9a7730SZachary Turner // perform case insensitive equality test
2598070bf0aSPavel Labath llvm::StringRef lhs_string_ref(lhs.GetStringRef());
2608070bf0aSPavel Labath llvm::StringRef rhs_string_ref(rhs.GetStringRef());
261e50f9c41SMartin Storsjö return lhs_string_ref.equals_insensitive(rhs_string_ref);
262bf9a7730SZachary Turner }
263bf9a7730SZachary Turner
Compare(ConstString lhs,ConstString rhs,const bool case_sensitive)264ccdb5b4bSJonas Devlieghere int ConstString::Compare(ConstString lhs, ConstString rhs,
265bf9a7730SZachary Turner const bool case_sensitive) {
266bf9a7730SZachary Turner // If the iterators are the same, this is the same string
267bf9a7730SZachary Turner const char *lhs_cstr = lhs.m_string;
268bf9a7730SZachary Turner const char *rhs_cstr = rhs.m_string;
269bf9a7730SZachary Turner if (lhs_cstr == rhs_cstr)
270bf9a7730SZachary Turner return 0;
271bf9a7730SZachary Turner if (lhs_cstr && rhs_cstr) {
2728070bf0aSPavel Labath llvm::StringRef lhs_string_ref(lhs.GetStringRef());
2738070bf0aSPavel Labath llvm::StringRef rhs_string_ref(rhs.GetStringRef());
274bf9a7730SZachary Turner
275bf9a7730SZachary Turner if (case_sensitive) {
276bf9a7730SZachary Turner return lhs_string_ref.compare(rhs_string_ref);
277bf9a7730SZachary Turner } else {
278e50f9c41SMartin Storsjö return lhs_string_ref.compare_insensitive(rhs_string_ref);
279bf9a7730SZachary Turner }
280bf9a7730SZachary Turner }
281bf9a7730SZachary Turner
282bf9a7730SZachary Turner if (lhs_cstr)
283bf9a7730SZachary Turner return +1; // LHS isn't nullptr but RHS is
284bf9a7730SZachary Turner else
285bf9a7730SZachary Turner return -1; // LHS is nullptr but RHS isn't
286bf9a7730SZachary Turner }
287bf9a7730SZachary Turner
Dump(Stream * s,const char * fail_value) const288bf9a7730SZachary Turner void ConstString::Dump(Stream *s, const char *fail_value) const {
289bf9a7730SZachary Turner if (s != nullptr) {
290bf9a7730SZachary Turner const char *cstr = AsCString(fail_value);
291bf9a7730SZachary Turner if (cstr != nullptr)
292bf9a7730SZachary Turner s->PutCString(cstr);
293bf9a7730SZachary Turner }
294bf9a7730SZachary Turner }
295bf9a7730SZachary Turner
DumpDebug(Stream * s) const296bf9a7730SZachary Turner void ConstString::DumpDebug(Stream *s) const {
297bf9a7730SZachary Turner const char *cstr = GetCString();
298bf9a7730SZachary Turner size_t cstr_len = GetLength();
299bf9a7730SZachary Turner // Only print the parens if we have a non-nullptr string
300bf9a7730SZachary Turner const char *parens = cstr ? "\"" : "";
301bf9a7730SZachary Turner s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,
302bf9a7730SZachary Turner static_cast<int>(sizeof(void *) * 2),
303bf9a7730SZachary Turner static_cast<const void *>(this), parens, cstr, parens,
304bf9a7730SZachary Turner static_cast<uint64_t>(cstr_len));
305bf9a7730SZachary Turner }
306bf9a7730SZachary Turner
SetCString(const char * cstr)307bf9a7730SZachary Turner void ConstString::SetCString(const char *cstr) {
308bf9a7730SZachary Turner m_string = StringPool().GetConstCString(cstr);
309bf9a7730SZachary Turner }
310bf9a7730SZachary Turner
SetString(llvm::StringRef s)3118a463692SAlex Langford void ConstString::SetString(llvm::StringRef s) {
3128a463692SAlex Langford m_string = StringPool().GetConstCStringWithStringRef(s);
313bf9a7730SZachary Turner }
314bf9a7730SZachary Turner
SetStringWithMangledCounterpart(llvm::StringRef demangled,ConstString mangled)31519a357adSPavel Labath void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled,
316ccdb5b4bSJonas Devlieghere ConstString mangled) {
317bf9a7730SZachary Turner m_string = StringPool().GetConstCStringAndSetMangledCounterPart(
318bf9a7730SZachary Turner demangled, mangled.m_string);
319bf9a7730SZachary Turner }
320bf9a7730SZachary Turner
GetMangledCounterpart(ConstString & counterpart) const321bf9a7730SZachary Turner bool ConstString::GetMangledCounterpart(ConstString &counterpart) const {
322bf9a7730SZachary Turner counterpart.m_string = StringPool().GetMangledCounterpart(m_string);
323bf9a7730SZachary Turner return (bool)counterpart;
324bf9a7730SZachary Turner }
325bf9a7730SZachary Turner
SetCStringWithLength(const char * cstr,size_t cstr_len)326bf9a7730SZachary Turner void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) {
327bf9a7730SZachary Turner m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);
328bf9a7730SZachary Turner }
329bf9a7730SZachary Turner
SetTrimmedCStringWithLength(const char * cstr,size_t cstr_len)330bf9a7730SZachary Turner void ConstString::SetTrimmedCStringWithLength(const char *cstr,
331bf9a7730SZachary Turner size_t cstr_len) {
332bf9a7730SZachary Turner m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len);
333bf9a7730SZachary Turner }
334bf9a7730SZachary Turner
GetMemoryStats()335cd8122b2SJonas Devlieghere ConstString::MemoryStats ConstString::GetMemoryStats() {
336cd8122b2SJonas Devlieghere return StringPool().GetMemoryStats();
337cd8122b2SJonas Devlieghere }
338cd8122b2SJonas Devlieghere
format(const ConstString & CS,llvm::raw_ostream & OS,llvm::StringRef Options)3393b7e1981SPavel Labath void llvm::format_provider<ConstString>::format(const ConstString &CS,
3403b7e1981SPavel Labath llvm::raw_ostream &OS,
3413b7e1981SPavel Labath llvm::StringRef Options) {
342642bc15dSRaphael Isemann format_provider<StringRef>::format(CS.GetStringRef(), OS, Options);
3433b7e1981SPavel Labath }
344