blob: d5a263f40aee53d4d2975617edd8924bb5ad6374 [file] [log] [blame]
//===--- TreeScopedHashTable.h - Hash table with multiple active scopes ---===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// Note: This file intentionally uses C++03 so that it can be eventually moved
// into LLVM ADT library.
#ifndef SWIFT_BASIC_TREESCOPEDHASHTABLE_H
#define SWIFT_BASIC_TREESCOPEDHASHTABLE_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Allocator.h"
#include "swift/Basic/Malloc.h"
#include <utility>
namespace swift {
template <typename K, typename V, typename AllocatorTy = llvm::MallocAllocator>
class TreeScopedHashTable;
template <typename K, typename V, typename AllocatorTy = llvm::MallocAllocator>
class TreeScopedHashTableScope;
template <typename K, typename V> class TreeScopedHashTableVal {
TreeScopedHashTableVal *NextInScope;
TreeScopedHashTableVal *NextForKey;
K Key;
V Val;
TreeScopedHashTableVal(const K &Key, const V &Val) : Key(Key), Val(Val) {}
public:
~TreeScopedHashTableVal() = default;
TreeScopedHashTableVal(const TreeScopedHashTableVal &) = delete;
TreeScopedHashTableVal(TreeScopedHashTableVal &&) = delete;
TreeScopedHashTableVal &operator=(const TreeScopedHashTableVal &) = delete;
TreeScopedHashTableVal &operator=(TreeScopedHashTableVal &&) = delete;
const K &getKey() const { return Key; }
const V &getValue() const { return Val; }
V &getValue() { return Val; }
TreeScopedHashTableVal *getNextForKey() { return NextForKey; }
const TreeScopedHashTableVal *getNextForKey() const { return NextForKey; }
TreeScopedHashTableVal *getNextInScope() { return NextInScope; }
template <typename AllocatorTy>
static TreeScopedHashTableVal *Create(TreeScopedHashTableVal *NextInScope,
TreeScopedHashTableVal *NextForKey,
const K &key, const V &val,
AllocatorTy &Allocator) {
TreeScopedHashTableVal *New =
Allocator.template Allocate<TreeScopedHashTableVal>();
// Set up the value.
new (New) TreeScopedHashTableVal(key, val);
New->NextInScope = NextInScope;
New->NextForKey = NextForKey;
return New;
}
template <typename AllocatorTy> void Destroy(AllocatorTy &Allocator) {
// Free memory referenced by the item.
this->~TreeScopedHashTableVal();
Allocator.Deallocate(this);
}
};
/// A reference-counted scope that actually owns the data in the
/// hashtable.
template <typename K, typename V, typename AllocatorTy = llvm::MallocAllocator>
class TreeScopedHashTableScopeImpl {
public:
typedef TreeScopedHashTable<K, V, AllocatorTy> HTTy;
/// The hashtable that we are active for.
HTTy *HT;
const typename HTTy::ScopeIDTy ID;
/// This is the scope that we are shadowing in HT.
TreeScopedHashTableScopeImpl *ParentScope;
/// This is the last value that was inserted for this scope or null if none
/// have been inserted yet.
TreeScopedHashTableVal<K, V> *LastValInScope;
bool MovedFrom;
bool OwnsParentScope;
unsigned RefCount;
TreeScopedHashTableScopeImpl(TreeScopedHashTableScopeImpl &) = delete;
void operator=(TreeScopedHashTableScopeImpl &) = delete;
TreeScopedHashTableScopeImpl()
: HT(0), ID(0), ParentScope(0), MovedFrom(true), RefCount(0) {}
TreeScopedHashTableScopeImpl(TreeScopedHashTable<K, V, AllocatorTy> *HT,
TreeScopedHashTableScopeImpl *ParentScope)
: HT(HT), ID(HT->getNextScopeID()), ParentScope(ParentScope),
LastValInScope(0), MovedFrom(false), OwnsParentScope(false),
RefCount(0) {}
TreeScopedHashTableScopeImpl(TreeScopedHashTableScopeImpl &&Other)
: HT(Other.HT), ID(Other.ID), ParentScope(Other.ParentScope),
LastValInScope(Other.LastValInScope), MovedFrom(false),
OwnsParentScope(Other.OwnsParentScope), RefCount(Other.RefCount) {
assert(!Other.MovedFrom && "moving from a moved-from scope");
Other.MovedFrom = true;
// *Don't* set Other.ID to a trap value so that the old scope object can
// be still used for lookup.
}
void retain() {
RefCount++;
}
void release() {
RefCount--;
if (RefCount == 0)
delete this;
}
~TreeScopedHashTableScopeImpl();
};
/// A scope that was detached from the stack to heap.
template <typename K, typename V, typename AllocatorTy = llvm::MallocAllocator>
class TreeScopedHashTableDetachedScope {
friend class TreeScopedHashTableScope<K, V, AllocatorTy>;
typedef TreeScopedHashTableScopeImpl<K, V, AllocatorTy> ImplTy;
ImplTy *DetachedImpl;
TreeScopedHashTableDetachedScope(TreeScopedHashTableDetachedScope &) = delete;
void operator=(TreeScopedHashTableDetachedScope &) = delete;
TreeScopedHashTableDetachedScope(ImplTy *DetachedImpl)
: DetachedImpl(DetachedImpl) {
DetachedImpl->retain();
}
const ImplTy *getImpl() { return DetachedImpl; }
public:
TreeScopedHashTableDetachedScope &
operator=(TreeScopedHashTableDetachedScope &&) = default;
TreeScopedHashTableDetachedScope() : DetachedImpl(0) {}
TreeScopedHashTableDetachedScope(TreeScopedHashTableDetachedScope &&Other)
: DetachedImpl(Other.DetachedImpl) {
Other.DetachedImpl = 0;
}
~TreeScopedHashTableDetachedScope() {
if (DetachedImpl)
DetachedImpl->release();
}
};
/// A normal hashtable scope. Objects of this class should be created only
/// on stack. A normal scope is faster to create than a detached scope because
/// it does not do heap allocation for the reference counted
/// \c TreeScopedHashTableScopeImpl.
template <typename K, typename V, typename AllocatorTy>
class TreeScopedHashTableScope {
friend class TreeScopedHashTable<K, V, AllocatorTy>;
typedef TreeScopedHashTableScopeImpl<K, V, AllocatorTy> ImplTy;
/// Inline storage for a reference-counted scope.
ImplTy InlineImpl;
/// Pointer to the reference-counted scope that was detached to the heap.
ImplTy *DetachedImpl;
TreeScopedHashTableScope *const ParentScope;
ImplTy *getImpl() {
assert(static_cast<bool>(DetachedImpl) == InlineImpl.MovedFrom);
return InlineImpl.MovedFrom ? DetachedImpl : &InlineImpl;
}
const ImplTy *getImpl() const {
assert(static_cast<bool>(DetachedImpl) == InlineImpl.MovedFrom);
return InlineImpl.MovedFrom ? DetachedImpl : &InlineImpl;
}
TreeScopedHashTableScope(TreeScopedHashTableScope &) = delete;
void operator=(TreeScopedHashTableScope &) = delete;
public:
/// Install this as the current scope for the hash table.
TreeScopedHashTableScope(TreeScopedHashTable<K, V, AllocatorTy> &HT,
TreeScopedHashTableScope *ParentScope)
: InlineImpl(&HT, ParentScope ? ParentScope->getImpl() : 0),
DetachedImpl(0), ParentScope(ParentScope) {}
TreeScopedHashTableScope(TreeScopedHashTableDetachedScope<K, V, AllocatorTy> &&DS)
: DetachedImpl(DS.DetachedImpl), ParentScope(0) {
DS.DetachedImpl = 0;
}
~TreeScopedHashTableScope() {
if (DetachedImpl)
DetachedImpl->release();
}
/// Detach this scope to the heap.
TreeScopedHashTableDetachedScope<K, V, AllocatorTy> detach() {
if (DetachedImpl)
return TreeScopedHashTableDetachedScope<K, V, AllocatorTy>(DetachedImpl);
// Detach all parent scopes recursively.
if (ParentScope && !ParentScope->DetachedImpl) {
ParentScope->detach();
InlineImpl.ParentScope = ParentScope->getImpl();
}
DetachedImpl = new ImplTy(std::move(InlineImpl));
DetachedImpl->retain();
if (DetachedImpl->ParentScope) {
DetachedImpl->ParentScope->retain();
DetachedImpl->OwnsParentScope = true;
}
return TreeScopedHashTableDetachedScope<K, V, AllocatorTy>(DetachedImpl);
}
};
template <typename K, typename V> class TreeScopedHashTableIterator {
TreeScopedHashTableVal<K, V> *Node;
public:
TreeScopedHashTableIterator(TreeScopedHashTableVal<K, V> *Node)
: Node(Node) {}
V &operator*() const {
assert(Node && "Dereference end()");
return Node->getValue();
}
V *operator->() const { return &Node->getValue(); }
bool operator==(const TreeScopedHashTableIterator &RHS) const {
return Node == RHS.Node;
}
bool operator!=(const TreeScopedHashTableIterator &RHS) const {
return Node != RHS.Node;
}
inline TreeScopedHashTableIterator &operator++() { // Preincrement
assert(Node && "incrementing past end()");
Node = Node->getNextForKey();
return *this;
}
TreeScopedHashTableIterator operator++(int) { // Postincrement
TreeScopedHashTableIterator tmp = *this;
++*this;
return tmp;
}
};
/// A scoped hashtable that can have multiple active scopes.
///
/// There are two kinds of scopes:
///
/// \li TreeScopedHashTableScope -- normal scopes, which should be created on
/// stack. Obviously, only one such scope can be active at a time. As soon as
/// the scope object is destroyed, corresponding data from the hashtable is
/// deleted.
///
/// \li TreeScopedHashTableDetachedScope -- detached scopes, can be stored on
/// heap. These can be created from normal scopes by calling \c detach().
/// Detaching a scope transparently detaches all its parent scopes.
/// A detached scope takes ownership of the corresponding data in the
/// hashtable.
///
/// All scopes should be destroyed before hashtable is destroyed.
template <typename K, typename V, typename AllocatorTy>
class TreeScopedHashTable {
public:
/// This is a helpful typedef that allows clients to get easy access
/// to the name of the scope for this hash table.
typedef TreeScopedHashTableScope<K, V, AllocatorTy> ScopeTy;
typedef TreeScopedHashTableDetachedScope<K, V, AllocatorTy> DetachedScopeTy;
private:
typedef unsigned ScopeIDTy;
typedef std::pair<K, ScopeIDTy> KeyTy;
typedef TreeScopedHashTableVal<K, V> ValTy;
llvm::DenseMap<KeyTy, ValTy *> TopLevelMap;
AllocatorTy Allocator;
ScopeIDTy NextScopeID;
ScopeIDTy getNextScopeID() {
return NextScopeID++;
}
TreeScopedHashTable(const TreeScopedHashTable &) = delete;
void operator=(const TreeScopedHashTable &) = delete;
friend class TreeScopedHashTableScopeImpl<K, V, AllocatorTy>;
public:
TreeScopedHashTable() : NextScopeID(0) {}
TreeScopedHashTable(AllocatorTy A) : Allocator(A), NextScopeID(0) {}
~TreeScopedHashTable() {
assert(TopLevelMap.empty() && "Scope imbalance!");
}
/// Access to the allocator.
typedef AllocatorTy &AllocatorRefTy;
typedef const AllocatorTy &AllocatorCRefTy;
AllocatorRefTy getAllocator() { return Allocator; }
AllocatorCRefTy getAllocator() const { return Allocator; }
bool count(const ScopeTy &S, const K &Key) const {
const typename ScopeTy::ImplTy *CurrScope = S.getImpl();
while (CurrScope) {
if (TopLevelMap.count(std::make_pair(Key, CurrScope->ID))) {
return true;
}
CurrScope = CurrScope->ParentScope;
}
return false;
}
V lookup(const ScopeTy &S, const K &Key) {
const typename ScopeTy::ImplTy *CurrScope = S.getImpl();
while (CurrScope) {
typename llvm::DenseMap<KeyTy, ValTy *>::iterator I =
TopLevelMap.find(std::make_pair(Key, CurrScope->ID));
if (I != TopLevelMap.end())
return I->second->getValue();
CurrScope = CurrScope->ParentScope;
}
return V();
}
typedef TreeScopedHashTableIterator<K, V> iterator;
iterator end() { return iterator(0); }
iterator begin(ScopeTy &S, const K &Key) {
typename llvm::DenseMap<KeyTy, ValTy *>::iterator I =
TopLevelMap.find(std::make_pair(Key, S.getImpl()->ID));
if (I == TopLevelMap.end())
return end();
return iterator(I->second);
}
using DebugVisitValueTy = TreeScopedHashTableVal<K, V> *;
/// Visit each entry in the map without regard to order. Meant to be used with
/// in the debugger in coordination with other dumpers that can dump whatever
/// is stored in the map. No-op when asserts are disabled.
LLVM_ATTRIBUTE_DEPRECATED(
void debugVisit(std::function<void(const DebugVisitValueTy &)> &&func)
const LLVM_ATTRIBUTE_USED,
"Only for use in the debugger");
/// This inserts the specified key/value at the specified
/// (possibly not the current) scope. While it is ok to insert into a scope
/// that isn't the current one, it isn't ok to insert *underneath* an existing
/// value of the specified key.
void insertIntoScope(ScopeTy &S, const K &Key, const V &Val) {
TreeScopedHashTableVal<K, V> *PrevEntry = 0;
const typename ScopeTy::ImplTy *CurrScope = S.getImpl();
while (CurrScope) {
typename llvm::DenseMap<KeyTy, ValTy *>::iterator I =
TopLevelMap.find(std::make_pair(Key, CurrScope->ID));
if (I != TopLevelMap.end()) {
PrevEntry = I->second;
break;
}
CurrScope = CurrScope->ParentScope;
}
assert(TopLevelMap.find(std::make_pair(Key, S.getImpl()->ID)) == TopLevelMap.end());
TreeScopedHashTableVal<K, V> *&KeyEntry =
TopLevelMap[std::make_pair(Key, S.getImpl()->ID)];
KeyEntry =
ValTy::Create(S.getImpl()->LastValInScope, PrevEntry, Key, Val, Allocator);
S.getImpl()->LastValInScope = KeyEntry;
}
};
template <typename K, typename V, typename Allocator>
void TreeScopedHashTable<K, V, Allocator>::debugVisit(
std::function<void(const DebugVisitValueTy &)> &&func) const {
#ifndef NDEBUG
for (auto entry : TopLevelMap) {
func(entry.second);
}
#endif
}
template <typename K, typename V, typename Allocator>
TreeScopedHashTableScopeImpl<K, V, Allocator>::~TreeScopedHashTableScopeImpl() {
if (MovedFrom)
return;
// Pop and delete all values corresponding to this scope.
while (TreeScopedHashTableVal<K, V> *ThisEntry = LastValInScope) {
// Pop this value out of the TopLevelMap.
assert(HT->TopLevelMap[std::make_pair(ThisEntry->getKey(), this->ID)] ==
ThisEntry &&
"Scope imbalance!");
HT->TopLevelMap.erase(std::make_pair(ThisEntry->getKey(), this->ID));
// Pop this value out of the scope.
LastValInScope = ThisEntry->getNextInScope();
// Delete this entry.
ThisEntry->Destroy(HT->getAllocator());
}
if (OwnsParentScope)
ParentScope->release();
}
} // end namespace swift
#endif // SWIFT_BASIC_TREESCOPEDHASHTABLE_H