1*810390e3Srobert //===------- string_pool.h - Thread-safe pool for strings -------*- C++ -*-===// 2*810390e3Srobert // 3*810390e3Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*810390e3Srobert // See https://llvm.org/LICENSE.txt for license information. 5*810390e3Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*810390e3Srobert // 7*810390e3Srobert //===----------------------------------------------------------------------===// 8*810390e3Srobert // 9*810390e3Srobert // Contains a thread-safe string pool. Strings are ref-counted, but not 10*810390e3Srobert // automatically deallocated. Unused entries can be cleared by calling 11*810390e3Srobert // StringPool::clearDeadEntries. 12*810390e3Srobert // 13*810390e3Srobert //===----------------------------------------------------------------------===// 14*810390e3Srobert 15*810390e3Srobert #ifndef ORC_RT_STRING_POOL_H 16*810390e3Srobert #define ORC_RT_STRING_POOL_H 17*810390e3Srobert 18*810390e3Srobert #include <atomic> 19*810390e3Srobert #include <cassert> 20*810390e3Srobert #include <functional> 21*810390e3Srobert #include <mutex> 22*810390e3Srobert #include <string> 23*810390e3Srobert #include <unordered_map> 24*810390e3Srobert 25*810390e3Srobert namespace __orc_rt { 26*810390e3Srobert 27*810390e3Srobert class PooledStringPtr; 28*810390e3Srobert 29*810390e3Srobert /// String pool for strings names used by the ORC runtime. 30*810390e3Srobert class StringPool { 31*810390e3Srobert friend class PooledStringPtr; 32*810390e3Srobert 33*810390e3Srobert public: 34*810390e3Srobert /// Destroy a StringPool. 35*810390e3Srobert ~StringPool(); 36*810390e3Srobert 37*810390e3Srobert /// Create a string pointer from the given string. 38*810390e3Srobert PooledStringPtr intern(std::string S); 39*810390e3Srobert 40*810390e3Srobert /// Remove from the pool any entries that are no longer referenced. 41*810390e3Srobert void clearDeadEntries(); 42*810390e3Srobert 43*810390e3Srobert /// Returns true if the pool is empty. 44*810390e3Srobert bool empty() const; 45*810390e3Srobert 46*810390e3Srobert private: 47*810390e3Srobert using RefCountType = std::atomic<size_t>; 48*810390e3Srobert using PoolMap = std::unordered_map<std::string, RefCountType>; 49*810390e3Srobert using PoolMapEntry = PoolMap::value_type; 50*810390e3Srobert mutable std::mutex PoolMutex; 51*810390e3Srobert PoolMap Pool; 52*810390e3Srobert }; 53*810390e3Srobert 54*810390e3Srobert /// Pointer to a pooled string. 55*810390e3Srobert class PooledStringPtr { 56*810390e3Srobert friend class StringPool; 57*810390e3Srobert friend struct std::hash<PooledStringPtr>; 58*810390e3Srobert 59*810390e3Srobert public: 60*810390e3Srobert PooledStringPtr() = default; 61*810390e3Srobert PooledStringPtr(std::nullptr_t) {} 62*810390e3Srobert PooledStringPtr(const PooledStringPtr &Other) : S(Other.S) { 63*810390e3Srobert if (S) 64*810390e3Srobert ++S->second; 65*810390e3Srobert } 66*810390e3Srobert 67*810390e3Srobert PooledStringPtr &operator=(const PooledStringPtr &Other) { 68*810390e3Srobert if (S) { 69*810390e3Srobert assert(S->second && "Releasing PooledStringPtr with zero ref count"); 70*810390e3Srobert --S->second; 71*810390e3Srobert } 72*810390e3Srobert S = Other.S; 73*810390e3Srobert if (S) 74*810390e3Srobert ++S->second; 75*810390e3Srobert return *this; 76*810390e3Srobert } 77*810390e3Srobert 78*810390e3Srobert PooledStringPtr(PooledStringPtr &&Other) : S(nullptr) { 79*810390e3Srobert std::swap(S, Other.S); 80*810390e3Srobert } 81*810390e3Srobert 82*810390e3Srobert PooledStringPtr &operator=(PooledStringPtr &&Other) { 83*810390e3Srobert if (S) { 84*810390e3Srobert assert(S->second && "Releasing PooledStringPtr with zero ref count"); 85*810390e3Srobert --S->second; 86*810390e3Srobert } 87*810390e3Srobert S = nullptr; 88*810390e3Srobert std::swap(S, Other.S); 89*810390e3Srobert return *this; 90*810390e3Srobert } 91*810390e3Srobert 92*810390e3Srobert ~PooledStringPtr() { 93*810390e3Srobert if (S) { 94*810390e3Srobert assert(S->second && "Releasing PooledStringPtr with zero ref count"); 95*810390e3Srobert --S->second; 96*810390e3Srobert } 97*810390e3Srobert } 98*810390e3Srobert 99*810390e3Srobert explicit operator bool() const { return S; } 100*810390e3Srobert 101*810390e3Srobert const std::string &operator*() const { return S->first; } 102*810390e3Srobert 103*810390e3Srobert friend bool operator==(const PooledStringPtr &LHS, 104*810390e3Srobert const PooledStringPtr &RHS) { 105*810390e3Srobert return LHS.S == RHS.S; 106*810390e3Srobert } 107*810390e3Srobert 108*810390e3Srobert friend bool operator!=(const PooledStringPtr &LHS, 109*810390e3Srobert const PooledStringPtr &RHS) { 110*810390e3Srobert return !(LHS == RHS); 111*810390e3Srobert } 112*810390e3Srobert 113*810390e3Srobert friend bool operator<(const PooledStringPtr &LHS, 114*810390e3Srobert const PooledStringPtr &RHS) { 115*810390e3Srobert return LHS.S < RHS.S; 116*810390e3Srobert } 117*810390e3Srobert 118*810390e3Srobert private: 119*810390e3Srobert using PoolEntry = StringPool::PoolMapEntry; 120*810390e3Srobert using PoolEntryPtr = PoolEntry *; 121*810390e3Srobert 122*810390e3Srobert PooledStringPtr(StringPool::PoolMapEntry *S) : S(S) { 123*810390e3Srobert if (S) 124*810390e3Srobert ++S->second; 125*810390e3Srobert } 126*810390e3Srobert 127*810390e3Srobert PoolEntryPtr S = nullptr; 128*810390e3Srobert }; 129*810390e3Srobert 130*810390e3Srobert inline StringPool::~StringPool() { 131*810390e3Srobert #ifndef NDEBUG 132*810390e3Srobert clearDeadEntries(); 133*810390e3Srobert assert(Pool.empty() && "Dangling references at pool destruction time"); 134*810390e3Srobert #endif // NDEBUG 135*810390e3Srobert } 136*810390e3Srobert 137*810390e3Srobert inline PooledStringPtr StringPool::intern(std::string S) { 138*810390e3Srobert std::lock_guard<std::mutex> Lock(PoolMutex); 139*810390e3Srobert PoolMap::iterator I; 140*810390e3Srobert bool Added; 141*810390e3Srobert std::tie(I, Added) = Pool.try_emplace(std::move(S), 0); 142*810390e3Srobert return PooledStringPtr(&*I); 143*810390e3Srobert } 144*810390e3Srobert 145*810390e3Srobert inline void StringPool::clearDeadEntries() { 146*810390e3Srobert std::lock_guard<std::mutex> Lock(PoolMutex); 147*810390e3Srobert for (auto I = Pool.begin(), E = Pool.end(); I != E;) { 148*810390e3Srobert auto Tmp = I++; 149*810390e3Srobert if (Tmp->second == 0) 150*810390e3Srobert Pool.erase(Tmp); 151*810390e3Srobert } 152*810390e3Srobert } 153*810390e3Srobert 154*810390e3Srobert inline bool StringPool::empty() const { 155*810390e3Srobert std::lock_guard<std::mutex> Lock(PoolMutex); 156*810390e3Srobert return Pool.empty(); 157*810390e3Srobert } 158*810390e3Srobert 159*810390e3Srobert } // end namespace __orc_rt 160*810390e3Srobert 161*810390e3Srobert namespace std { 162*810390e3Srobert 163*810390e3Srobert // Make PooledStringPtrs hashable. 164*810390e3Srobert template <> struct hash<__orc_rt::PooledStringPtr> { 165*810390e3Srobert size_t operator()(const __orc_rt::PooledStringPtr &A) const { 166*810390e3Srobert return hash<__orc_rt::PooledStringPtr::PoolEntryPtr>()(A.S); 167*810390e3Srobert } 168*810390e3Srobert }; 169*810390e3Srobert 170*810390e3Srobert } // namespace std 171*810390e3Srobert 172*810390e3Srobert #endif // ORC_RT_REF_COUNTED_STRING_POOL_H 173