#ifndef ORC_RT_STRING_POOL_H
#define ORC_RT_STRING_POOL_H
#include <atomic>
#include <cassert>
#include <functional>
#include <mutex>
#include <string>
#include <unordered_map>
namespace __orc_rt {
class PooledStringPtr;
class StringPool {
friend class PooledStringPtr;
public:
~StringPool();
PooledStringPtr intern(std::string S);
void clearDeadEntries();
bool empty() const;
private:
using RefCountType = std::atomic<size_t>;
using PoolMap = std::unordered_map<std::string, RefCountType>;
using PoolMapEntry = PoolMap::value_type;
mutable std::mutex PoolMutex;
PoolMap Pool;
};
class PooledStringPtr {
friend class StringPool;
friend struct std::hash<PooledStringPtr>;
public:
PooledStringPtr() = default;
PooledStringPtr(std::nullptr_t) {}
PooledStringPtr(const PooledStringPtr &Other) : S(Other.S) {
if (S)
++S->second;
}
PooledStringPtr &operator=(const PooledStringPtr &Other) {
if (S) {
assert(S->second && "Releasing PooledStringPtr with zero ref count");
--S->second;
}
S = Other.S;
if (S)
++S->second;
return *this;
}
PooledStringPtr(PooledStringPtr &&Other) : S(nullptr) {
std::swap(S, Other.S);
}
PooledStringPtr &operator=(PooledStringPtr &&Other) {
if (S) {
assert(S->second && "Releasing PooledStringPtr with zero ref count");
--S->second;
}
S = nullptr;
std::swap(S, Other.S);
return *this;
}
~PooledStringPtr() {
if (S) {
assert(S->second && "Releasing PooledStringPtr with zero ref count");
--S->second;
}
}
explicit operator bool() const { return S; }
const std::string &operator*() const { return S->first; }
friend bool operator==(const PooledStringPtr &LHS,
const PooledStringPtr &RHS) {
return LHS.S == RHS.S;
}
friend bool operator!=(const PooledStringPtr &LHS,
const PooledStringPtr &RHS) {
return !(LHS == RHS);
}
friend bool operator<(const PooledStringPtr &LHS,
const PooledStringPtr &RHS) {
return LHS.S < RHS.S;
}
private:
using PoolEntry = StringPool::PoolMapEntry;
using PoolEntryPtr = PoolEntry *;
PooledStringPtr(StringPool::PoolMapEntry *S) : S(S) {
if (S)
++S->second;
}
PoolEntryPtr S = nullptr;
};
inline StringPool::~StringPool() {
#ifndef NDEBUG
clearDeadEntries();
assert(Pool.empty() && "Dangling references at pool destruction time");
#endif
}
inline PooledStringPtr StringPool::intern(std::string S) {
std::lock_guard<std::mutex> Lock(PoolMutex);
PoolMap::iterator I;
bool Added;
std::tie(I, Added) = Pool.try_emplace(std::move(S), 0);
return PooledStringPtr(&*I);
}
inline void StringPool::clearDeadEntries() {
std::lock_guard<std::mutex> Lock(PoolMutex);
for (auto I = Pool.begin(), E = Pool.end(); I != E;) {
auto Tmp = I++;
if (Tmp->second == 0)
Pool.erase(Tmp);
}
}
inline bool StringPool::empty() const {
std::lock_guard<std::mutex> Lock(PoolMutex);
return Pool.empty();
}
}
namespace std {
template <> struct hash<__orc_rt::PooledStringPtr> {
size_t operator()(const __orc_rt::PooledStringPtr &A) const {
return hash<__orc_rt::PooledStringPtr::PoolEntryPtr>()(A.S);
}
};
}
#endif