forked from OSchip/llvm-project
[esan] Add iterator to esan's generic hashtable
Summary: Adds simple iterator support to the esan hashtable. Reviewers: aizatsky Subscribers: vitalybuka, zhaoqin, kcc, eugenis, llvm-commits, kubabrecka Differential Revision: https://reviews.llvm.org/D22682 llvm-svn: 278027
This commit is contained in:
parent
cb1aef8de4
commit
3ee803a895
|
@ -66,19 +66,58 @@ public:
|
||||||
// If the table is internally-synchronized, this lock must not be held
|
// If the table is internally-synchronized, this lock must not be held
|
||||||
// while a hashtable function is called as it will deadlock: the lock
|
// while a hashtable function is called as it will deadlock: the lock
|
||||||
// is not recursive. This is meant for use with externally-synchronized
|
// is not recursive. This is meant for use with externally-synchronized
|
||||||
// tables.
|
// tables or with an iterator.
|
||||||
void lock();
|
void lock();
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resize();
|
|
||||||
|
|
||||||
struct HashEntry {
|
struct HashEntry {
|
||||||
KeyTy Key;
|
KeyTy Key;
|
||||||
DataTy Payload;
|
DataTy Payload;
|
||||||
HashEntry *Next;
|
HashEntry *Next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct HashPair {
|
||||||
|
HashPair(KeyTy Key, DataTy Data) : Key(Key), Data(Data) {}
|
||||||
|
KeyTy Key;
|
||||||
|
DataTy Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This iterator does not perform any synchronization.
|
||||||
|
// It expects the caller to lock the table across the whole iteration.
|
||||||
|
// Calling HashTable functions while using the iterator is not supported.
|
||||||
|
// The iterator returns copies of the keys and data.
|
||||||
|
class iterator {
|
||||||
|
public:
|
||||||
|
iterator(
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table);
|
||||||
|
iterator(const iterator &Src) = default;
|
||||||
|
iterator &operator=(const iterator &Src) = default;
|
||||||
|
HashPair operator*();
|
||||||
|
iterator &operator++();
|
||||||
|
iterator &operator++(int);
|
||||||
|
bool operator==(const iterator &Cmp) const;
|
||||||
|
bool operator!=(const iterator &Cmp) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
iterator(
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table,
|
||||||
|
int Idx);
|
||||||
|
friend HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>;
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table;
|
||||||
|
int Idx;
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::HashEntry
|
||||||
|
*Entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
// No erase or insert iterator supported
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resize();
|
||||||
|
|
||||||
HashEntry **Table;
|
HashEntry **Table;
|
||||||
u32 Capacity;
|
u32 Capacity;
|
||||||
u32 Entries;
|
u32 Entries;
|
||||||
|
@ -247,4 +286,96 @@ void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::unlock() {
|
||||||
Mutex.Unlock();
|
Mutex.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Iterator implementation
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||||
|
iterator(
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table)
|
||||||
|
: Table(Table), Idx(-1), Entry(nullptr) {
|
||||||
|
operator++();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||||
|
iterator(
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table,
|
||||||
|
int Idx)
|
||||||
|
: Table(Table), Idx(Idx), Entry(nullptr) {
|
||||||
|
CHECK(Idx >= (int)Table->Capacity); // Only used to create end().
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||||
|
EqualFuncTy>::HashPair
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||||
|
operator*() {
|
||||||
|
CHECK(Idx >= 0 && Idx < (int)Table->Capacity);
|
||||||
|
CHECK(Entry != nullptr);
|
||||||
|
return HashPair(Entry->Key, Entry->Payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||||
|
EqualFuncTy>::iterator &
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||||
|
operator++() {
|
||||||
|
if (Entry != nullptr)
|
||||||
|
Entry = Entry->Next;
|
||||||
|
while (Entry == nullptr) {
|
||||||
|
++Idx;
|
||||||
|
if (Idx >= (int)Table->Capacity)
|
||||||
|
break; // At end().
|
||||||
|
Entry = Table->Table[Idx];
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||||
|
EqualFuncTy>::iterator &
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||||
|
operator++(int) {
|
||||||
|
iterator Temp(*this);
|
||||||
|
operator++();
|
||||||
|
return Temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||||
|
operator==(const iterator &Cmp) const {
|
||||||
|
return Cmp.Table == Table && Cmp.Idx == Idx && Cmp.Entry == Entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||||
|
operator!=(const iterator &Cmp) const {
|
||||||
|
return Cmp.Table != Table || Cmp.Idx != Idx || Cmp.Entry != Entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||||
|
EqualFuncTy>::iterator
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::begin() {
|
||||||
|
return iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||||
|
typename HashFuncTy, typename EqualFuncTy>
|
||||||
|
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||||
|
EqualFuncTy>::iterator
|
||||||
|
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::end() {
|
||||||
|
return iterator(this, Capacity);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace __esan
|
} // namespace __esan
|
||||||
|
|
|
@ -56,6 +56,15 @@ int main()
|
||||||
{
|
{
|
||||||
__esan::HashTable<int, int> IntTable;
|
__esan::HashTable<int, int> IntTable;
|
||||||
assert(IntTable.size() == 0);
|
assert(IntTable.size() == 0);
|
||||||
|
|
||||||
|
// Test iteration on an empty table.
|
||||||
|
int Count = 0;
|
||||||
|
for (auto Iter = IntTable.begin(); Iter != IntTable.end();
|
||||||
|
++Iter, ++Count) {
|
||||||
|
// Empty.
|
||||||
|
}
|
||||||
|
assert(Count == 0);
|
||||||
|
|
||||||
bool Added = IntTable.add(4, 42);
|
bool Added = IntTable.add(4, 42);
|
||||||
assert(Added);
|
assert(Added);
|
||||||
assert(!IntTable.add(4, 42));
|
assert(!IntTable.add(4, 42));
|
||||||
|
@ -63,9 +72,21 @@ int main()
|
||||||
int Value;
|
int Value;
|
||||||
bool Found = IntTable.lookup(4, Value);
|
bool Found = IntTable.lookup(4, Value);
|
||||||
assert(Found && Value == 42);
|
assert(Found && Value == 42);
|
||||||
|
|
||||||
|
// Test iterator.
|
||||||
|
IntTable.lock();
|
||||||
|
for (auto Iter = IntTable.begin(); Iter != IntTable.end();
|
||||||
|
++Iter, ++Count) {
|
||||||
|
assert((*Iter).Key == 4);
|
||||||
|
assert((*Iter).Data == 42);
|
||||||
|
}
|
||||||
|
IntTable.unlock();
|
||||||
|
assert(Count == 1);
|
||||||
|
assert(Count == IntTable.size());
|
||||||
assert(!IntTable.remove(5));
|
assert(!IntTable.remove(5));
|
||||||
assert(IntTable.remove(4));
|
assert(IntTable.remove(4));
|
||||||
|
|
||||||
|
// Test a more complex payload.
|
||||||
__esan::HashTable<int, MyDataPayload> DataTable(4);
|
__esan::HashTable<int, MyDataPayload> DataTable(4);
|
||||||
MyDataPayload NewData(new MyData("mystring"));
|
MyDataPayload NewData(new MyData("mystring"));
|
||||||
Added = DataTable.add(4, NewData);
|
Added = DataTable.add(4, NewData);
|
||||||
|
@ -86,6 +107,30 @@ int main()
|
||||||
Found = DataTable.lookup(i+1, FoundData);
|
Found = DataTable.lookup(i+1, FoundData);
|
||||||
assert(Found && strcmp(FoundData.Data->Buf, "delete-at-end") == 0);
|
assert(Found && strcmp(FoundData.Data->Buf, "delete-at-end") == 0);
|
||||||
}
|
}
|
||||||
|
DataTable.lock();
|
||||||
|
Count = 0;
|
||||||
|
for (auto Iter = DataTable.begin(); Iter != DataTable.end();
|
||||||
|
++Iter, ++Count) {
|
||||||
|
int Key = (*Iter).Key;
|
||||||
|
FoundData = (*Iter).Data;
|
||||||
|
assert(Key >= 1 && Key <= 4);
|
||||||
|
assert(strcmp(FoundData.Data->Buf, "delete-at-end") == 0);
|
||||||
|
}
|
||||||
|
DataTable.unlock();
|
||||||
|
assert(Count == 4);
|
||||||
|
assert(Count == DataTable.size());
|
||||||
|
|
||||||
|
// Ensure the iterator supports a range-based for loop.
|
||||||
|
DataTable.lock();
|
||||||
|
Count = 0;
|
||||||
|
for (auto Pair : DataTable) {
|
||||||
|
assert(Pair.Key >= 1 && Pair.Key <= 4);
|
||||||
|
assert(strcmp(Pair.Data.Data->Buf, "delete-at-end") == 0);
|
||||||
|
++Count;
|
||||||
|
}
|
||||||
|
DataTable.unlock();
|
||||||
|
assert(Count == 4);
|
||||||
|
assert(Count == DataTable.size());
|
||||||
|
|
||||||
// Test payload freeing via smart pointer wrapper.
|
// Test payload freeing via smart pointer wrapper.
|
||||||
__esan::HashTable<MyDataPayload, MyDataPayload, true> DataKeyTable;
|
__esan::HashTable<MyDataPayload, MyDataPayload, true> DataKeyTable;
|
||||||
|
|
Loading…
Reference in New Issue