xref: /openbsd-src/gnu/llvm/compiler-rt/lib/orc/string_pool.h (revision 810390e339a5425391477d5d41c78d7cab2424ac)
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