mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
AK: Consolidate iterators for HashTable and DoublyLinkedList respectively.
Get rid of the ConstIterator classes for these containers and use templated FooIterator<T, ...> and FooIterator<const T, ...> helpers. This makes the HashTable class a lot easier to read.
This commit is contained in:
parent
50700c107f
commit
516d736afe
Notes:
sideshowbarker
2024-07-19 13:28:10 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/516d736afe1
5 changed files with 150 additions and 240 deletions
|
@ -5,6 +5,30 @@
|
||||||
|
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
|
||||||
|
template<typename ListType, typename ElementType>
|
||||||
|
class DoublyLinkedListIterator {
|
||||||
|
public:
|
||||||
|
bool operator!=(const DoublyLinkedListIterator& other) const { return m_node != other.m_node; }
|
||||||
|
bool operator==(const DoublyLinkedListIterator& other) const { return m_node == other.m_node; }
|
||||||
|
DoublyLinkedListIterator& operator++()
|
||||||
|
{
|
||||||
|
m_node = m_node->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ElementType& operator*() { return m_node->value; }
|
||||||
|
ElementType* operator->() { return &m_node->value; }
|
||||||
|
bool is_end() const { return !m_node; }
|
||||||
|
static DoublyLinkedListIterator universal_end() { return DoublyLinkedListIterator(nullptr); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend ListType;
|
||||||
|
explicit DoublyLinkedListIterator(typename ListType::Node* node)
|
||||||
|
: m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
typename ListType::Node* m_node;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class DoublyLinkedList {
|
class DoublyLinkedList {
|
||||||
private:
|
private:
|
||||||
|
@ -79,55 +103,13 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Iterator {
|
using Iterator = DoublyLinkedListIterator<DoublyLinkedList, T>;
|
||||||
public:
|
friend Iterator;
|
||||||
bool operator!=(const Iterator& other) const { return m_node != other.m_node; }
|
|
||||||
bool operator==(const Iterator& other) const { return m_node == other.m_node; }
|
|
||||||
Iterator& operator++()
|
|
||||||
{
|
|
||||||
m_node = m_node->next;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
T& operator*() { return m_node->value; }
|
|
||||||
T* operator->() { return &m_node->value; }
|
|
||||||
bool is_end() const { return !m_node; }
|
|
||||||
static Iterator universal_end() { return Iterator(nullptr); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class DoublyLinkedList;
|
|
||||||
explicit Iterator(DoublyLinkedList::Node* node)
|
|
||||||
: m_node(node)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
DoublyLinkedList::Node* m_node;
|
|
||||||
};
|
|
||||||
|
|
||||||
Iterator begin() { return Iterator(m_head); }
|
Iterator begin() { return Iterator(m_head); }
|
||||||
Iterator end() { return Iterator::universal_end(); }
|
Iterator end() { return Iterator::universal_end(); }
|
||||||
|
|
||||||
class ConstIterator {
|
using ConstIterator = DoublyLinkedListIterator<const DoublyLinkedList, const T>;
|
||||||
public:
|
friend ConstIterator;
|
||||||
bool operator!=(const ConstIterator& other) const { return m_node != other.m_node; }
|
|
||||||
bool operator==(const ConstIterator& other) const { return m_node == other.m_node; }
|
|
||||||
ConstIterator& operator++()
|
|
||||||
{
|
|
||||||
m_node = m_node->next;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
const T& operator*() const { return m_node->value; }
|
|
||||||
const T* operator->() const { return &m_node->value; }
|
|
||||||
bool is_end() const { return !m_node; }
|
|
||||||
static ConstIterator universal_end() { return ConstIterator(nullptr); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class DoublyLinkedList;
|
|
||||||
explicit ConstIterator(const DoublyLinkedList::Node* node)
|
|
||||||
: m_node(node)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
const DoublyLinkedList::Node* m_node;
|
|
||||||
};
|
|
||||||
|
|
||||||
ConstIterator begin() const { return ConstIterator(m_head); }
|
ConstIterator begin() const { return ConstIterator(m_head); }
|
||||||
ConstIterator end() const { return ConstIterator::universal_end(); }
|
ConstIterator end() const { return ConstIterator::universal_end(); }
|
||||||
|
|
||||||
|
@ -171,8 +153,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Iterator;
|
|
||||||
|
|
||||||
void append_node(Node* node)
|
void append_node(Node* node)
|
||||||
{
|
{
|
||||||
if (!m_head) {
|
if (!m_head) {
|
||||||
|
|
276
AK/HashTable.h
276
AK/HashTable.h
|
@ -6,19 +6,76 @@
|
||||||
#include "Traits.h"
|
#include "Traits.h"
|
||||||
#include "kstdio.h"
|
#include "kstdio.h"
|
||||||
|
|
||||||
//#define HASHTABLE_DEBUG
|
|
||||||
|
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
|
||||||
template<typename T, typename = Traits<T>>
|
template<typename T, typename = Traits<T>>
|
||||||
class HashTable;
|
class HashTable;
|
||||||
|
|
||||||
|
template<typename HashTableType, typename ElementType, typename BucketIteratorType>
|
||||||
|
class HashTableIterator {
|
||||||
|
public:
|
||||||
|
bool operator!=(const HashTableIterator& other) const
|
||||||
|
{
|
||||||
|
if (m_is_end && other.m_is_end)
|
||||||
|
return false;
|
||||||
|
return &m_table != &other.m_table
|
||||||
|
|| m_is_end != other.m_is_end
|
||||||
|
|| m_bucket_index != other.m_bucket_index
|
||||||
|
|| m_bucket_iterator != other.m_bucket_iterator;
|
||||||
|
}
|
||||||
|
bool operator==(const HashTableIterator& other) const { return !(*this != other); }
|
||||||
|
ElementType& operator*() { return *m_bucket_iterator; }
|
||||||
|
ElementType* operator->() { return m_bucket_iterator.operator->(); }
|
||||||
|
HashTableIterator& operator++()
|
||||||
|
{
|
||||||
|
skip_to_next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip_to_next()
|
||||||
|
{
|
||||||
|
while (!m_is_end) {
|
||||||
|
if (m_bucket_iterator.is_end()) {
|
||||||
|
++m_bucket_index;
|
||||||
|
if (m_bucket_index >= m_table.capacity()) {
|
||||||
|
m_is_end = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_bucket_iterator = m_table.bucket(m_bucket_index).begin();
|
||||||
|
} else {
|
||||||
|
++m_bucket_iterator;
|
||||||
|
}
|
||||||
|
if (!m_bucket_iterator.is_end())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend HashTableType;
|
||||||
|
|
||||||
|
explicit HashTableIterator(HashTableType& table, bool is_end, BucketIteratorType bucket_iterator = BucketIteratorType::universal_end(), int bucket_index = 0)
|
||||||
|
: m_table(table)
|
||||||
|
, m_bucket_index(bucket_index)
|
||||||
|
, m_is_end(is_end)
|
||||||
|
, m_bucket_iterator(bucket_iterator)
|
||||||
|
{
|
||||||
|
if (!is_end && !m_table.is_empty() && !(m_bucket_iterator != BucketIteratorType::universal_end())) {
|
||||||
|
m_bucket_iterator = m_table.bucket(0).begin();
|
||||||
|
if (m_bucket_iterator.is_end())
|
||||||
|
skip_to_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTableType& m_table;
|
||||||
|
int m_bucket_index { 0 };
|
||||||
|
bool m_is_end { false };
|
||||||
|
BucketIteratorType m_bucket_iterator;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T, typename TraitsForT>
|
template<typename T, typename TraitsForT>
|
||||||
class HashTable {
|
class HashTable {
|
||||||
private:
|
private:
|
||||||
struct Bucket {
|
using Bucket = DoublyLinkedList<T>;
|
||||||
DoublyLinkedList<T> chain;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashTable() {}
|
HashTable() {}
|
||||||
|
@ -79,161 +136,13 @@ public:
|
||||||
|
|
||||||
void dump() const;
|
void dump() const;
|
||||||
|
|
||||||
class Iterator {
|
using Iterator = HashTableIterator<HashTable, T, typename DoublyLinkedList<T>::Iterator>;
|
||||||
public:
|
friend Iterator;
|
||||||
bool operator!=(const Iterator& other) const
|
|
||||||
{
|
|
||||||
if (m_is_end && other.m_is_end)
|
|
||||||
return false;
|
|
||||||
return &m_table != &other.m_table
|
|
||||||
|| m_is_end != other.m_is_end
|
|
||||||
|| m_bucket_index != other.m_bucket_index
|
|
||||||
|| m_bucket_iterator != other.m_bucket_iterator;
|
|
||||||
}
|
|
||||||
bool operator==(const Iterator& other) const { return !(*this != other); }
|
|
||||||
T& operator*()
|
|
||||||
{
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("retrieve { bucket_index: %u, is_end: %u }\n", m_bucket_index, m_is_end);
|
|
||||||
#endif
|
|
||||||
return *m_bucket_iterator;
|
|
||||||
}
|
|
||||||
T* operator->() { return m_bucket_iterator.operator->(); }
|
|
||||||
Iterator& operator++()
|
|
||||||
{
|
|
||||||
skip_to_next();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip_to_next()
|
|
||||||
{
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
unsigned pass = 0;
|
|
||||||
#endif
|
|
||||||
while (!m_is_end) {
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
++pass;
|
|
||||||
kprintf("skip_to_next pass %u, m_bucket_index=%u\n", pass, m_bucket_index);
|
|
||||||
#endif
|
|
||||||
if (m_bucket_iterator.is_end()) {
|
|
||||||
++m_bucket_index;
|
|
||||||
if (m_bucket_index >= m_table.capacity()) {
|
|
||||||
m_is_end = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_bucket_iterator = m_table.m_buckets[m_bucket_index].chain.begin();
|
|
||||||
} else {
|
|
||||||
++m_bucket_iterator;
|
|
||||||
}
|
|
||||||
if (!m_bucket_iterator.is_end())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class HashTable;
|
|
||||||
explicit Iterator(HashTable& table, bool is_end, typename DoublyLinkedList<T>::Iterator bucket_iterator = DoublyLinkedList<T>::Iterator::universal_end(), int bucket_index = 0)
|
|
||||||
: m_table(table)
|
|
||||||
, m_bucket_index(bucket_index)
|
|
||||||
, m_is_end(is_end)
|
|
||||||
, m_bucket_iterator(bucket_iterator)
|
|
||||||
{
|
|
||||||
if (!is_end && !m_table.is_empty() && !(m_bucket_iterator != DoublyLinkedList<T>::Iterator::universal_end())) {
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("bucket iterator init!\n");
|
|
||||||
#endif
|
|
||||||
m_bucket_iterator = m_table.m_buckets[0].chain.begin();
|
|
||||||
if (m_bucket_iterator.is_end())
|
|
||||||
skip_to_next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HashTable& m_table;
|
|
||||||
int m_bucket_index { 0 };
|
|
||||||
bool m_is_end { false };
|
|
||||||
typename DoublyLinkedList<T>::Iterator m_bucket_iterator;
|
|
||||||
};
|
|
||||||
|
|
||||||
Iterator begin() { return Iterator(*this, is_empty()); }
|
Iterator begin() { return Iterator(*this, is_empty()); }
|
||||||
Iterator end() { return Iterator(*this, true); }
|
Iterator end() { return Iterator(*this, true); }
|
||||||
|
|
||||||
class ConstIterator {
|
using ConstIterator = HashTableIterator<const HashTable, const T, typename DoublyLinkedList<T>::ConstIterator>;
|
||||||
public:
|
friend ConstIterator;
|
||||||
bool operator!=(const ConstIterator& other) const
|
|
||||||
{
|
|
||||||
if (m_is_end && other.m_is_end)
|
|
||||||
return false;
|
|
||||||
return &m_table != &other.m_table
|
|
||||||
|| m_is_end != other.m_is_end
|
|
||||||
|| m_bucket_index != other.m_bucket_index
|
|
||||||
|| m_bucket_iterator != other.m_bucket_iterator;
|
|
||||||
}
|
|
||||||
bool operator==(const ConstIterator& other) const { return !(*this != other); }
|
|
||||||
const T& operator*() const
|
|
||||||
{
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("retrieve { bucket_index: %u, is_end: %u }\n", m_bucket_index, m_is_end);
|
|
||||||
#endif
|
|
||||||
return *m_bucket_iterator;
|
|
||||||
}
|
|
||||||
const T* operator->() const { return m_bucket_iterator.operator->(); }
|
|
||||||
ConstIterator& operator++()
|
|
||||||
{
|
|
||||||
skip_to_next();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip_to_next()
|
|
||||||
{
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
unsigned pass = 0;
|
|
||||||
#endif
|
|
||||||
while (!m_is_end) {
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
++pass;
|
|
||||||
kprintf("skip_to_next pass %u, m_bucket_index=%u\n", pass, m_bucket_index);
|
|
||||||
#endif
|
|
||||||
if (m_bucket_iterator.is_end()) {
|
|
||||||
++m_bucket_index;
|
|
||||||
if (m_bucket_index >= m_table.capacity()) {
|
|
||||||
m_is_end = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const DoublyLinkedList<T>& chain = m_table.m_buckets[m_bucket_index].chain;
|
|
||||||
m_bucket_iterator = chain.begin();
|
|
||||||
} else {
|
|
||||||
++m_bucket_iterator;
|
|
||||||
}
|
|
||||||
if (!m_bucket_iterator.is_end())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class HashTable;
|
|
||||||
ConstIterator(const HashTable& table, bool is_end, typename DoublyLinkedList<T>::ConstIterator bucket_iterator = DoublyLinkedList<T>::ConstIterator::universal_end(), int bucket_index = 0)
|
|
||||||
: m_table(table)
|
|
||||||
, m_bucket_index(bucket_index)
|
|
||||||
, m_is_end(is_end)
|
|
||||||
, m_bucket_iterator(bucket_iterator)
|
|
||||||
{
|
|
||||||
if (!is_end && !m_table.is_empty() && !(m_bucket_iterator != DoublyLinkedList<T>::ConstIterator::universal_end())) {
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("const bucket iterator init!\n");
|
|
||||||
#endif
|
|
||||||
const DoublyLinkedList<T>& chain = m_table.m_buckets[0].chain;
|
|
||||||
m_bucket_iterator = chain.begin();
|
|
||||||
if (m_bucket_iterator.is_end())
|
|
||||||
skip_to_next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HashTable& m_table;
|
|
||||||
int m_bucket_index { 0 };
|
|
||||||
bool m_is_end { false };
|
|
||||||
typename DoublyLinkedList<T>::ConstIterator m_bucket_iterator;
|
|
||||||
};
|
|
||||||
|
|
||||||
ConstIterator begin() const { return ConstIterator(*this, is_empty()); }
|
ConstIterator begin() const { return ConstIterator(*this, is_empty()); }
|
||||||
ConstIterator end() const { return ConstIterator(*this, true); }
|
ConstIterator end() const { return ConstIterator(*this, true); }
|
||||||
|
|
||||||
|
@ -256,6 +165,9 @@ private:
|
||||||
void insert(const T&);
|
void insert(const T&);
|
||||||
void insert(T&&);
|
void insert(T&&);
|
||||||
|
|
||||||
|
Bucket& bucket(int index) { return m_buckets[index]; }
|
||||||
|
const Bucket& bucket(int index) const { return m_buckets[index]; }
|
||||||
|
|
||||||
Bucket* m_buckets { nullptr };
|
Bucket* m_buckets { nullptr };
|
||||||
|
|
||||||
int m_size { 0 };
|
int m_size { 0 };
|
||||||
|
@ -268,7 +180,7 @@ void HashTable<T, TraitsForT>::set(T&& value)
|
||||||
if (!m_capacity)
|
if (!m_capacity)
|
||||||
rehash(1);
|
rehash(1);
|
||||||
auto& bucket = lookup(value);
|
auto& bucket = lookup(value);
|
||||||
for (auto& e : bucket.chain) {
|
for (auto& e : bucket) {
|
||||||
if (e == value) {
|
if (e == value) {
|
||||||
e = move(value);
|
e = move(value);
|
||||||
return;
|
return;
|
||||||
|
@ -278,7 +190,7 @@ void HashTable<T, TraitsForT>::set(T&& value)
|
||||||
rehash(size() + 1);
|
rehash(size() + 1);
|
||||||
insert(move(value));
|
insert(move(value));
|
||||||
} else {
|
} else {
|
||||||
bucket.chain.append(move(value));
|
bucket.append(move(value));
|
||||||
}
|
}
|
||||||
m_size++;
|
m_size++;
|
||||||
}
|
}
|
||||||
|
@ -289,7 +201,7 @@ void HashTable<T, TraitsForT>::set(const T& value)
|
||||||
if (!m_capacity)
|
if (!m_capacity)
|
||||||
rehash(1);
|
rehash(1);
|
||||||
auto& bucket = lookup(value);
|
auto& bucket = lookup(value);
|
||||||
for (auto& e : bucket.chain) {
|
for (auto& e : bucket) {
|
||||||
if (e == value) {
|
if (e == value) {
|
||||||
e = move(value);
|
e = move(value);
|
||||||
return;
|
return;
|
||||||
|
@ -299,7 +211,7 @@ void HashTable<T, TraitsForT>::set(const T& value)
|
||||||
rehash(size() + 1);
|
rehash(size() + 1);
|
||||||
insert(value);
|
insert(value);
|
||||||
} else {
|
} else {
|
||||||
bucket.chain.append(value);
|
bucket.append(value);
|
||||||
}
|
}
|
||||||
m_size++;
|
m_size++;
|
||||||
}
|
}
|
||||||
|
@ -308,20 +220,14 @@ template<typename T, typename TraitsForT>
|
||||||
void HashTable<T, TraitsForT>::rehash(int new_capacity)
|
void HashTable<T, TraitsForT>::rehash(int new_capacity)
|
||||||
{
|
{
|
||||||
new_capacity *= 2;
|
new_capacity *= 2;
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("rehash to %u buckets\n", new_capacity);
|
|
||||||
#endif
|
|
||||||
auto* new_buckets = new Bucket[new_capacity];
|
auto* new_buckets = new Bucket[new_capacity];
|
||||||
auto* old_buckets = m_buckets;
|
auto* old_buckets = m_buckets;
|
||||||
int old_capacity = m_capacity;
|
int old_capacity = m_capacity;
|
||||||
m_buckets = new_buckets;
|
m_buckets = new_buckets;
|
||||||
m_capacity = new_capacity;
|
m_capacity = new_capacity;
|
||||||
|
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("reinsert %u buckets\n", old_capacity);
|
|
||||||
#endif
|
|
||||||
for (int i = 0; i < old_capacity; ++i) {
|
for (int i = 0; i < old_capacity; ++i) {
|
||||||
for (auto& value : old_buckets[i].chain) {
|
for (auto& value : old_buckets[i]) {
|
||||||
insert(move(value));
|
insert(move(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,14 +250,14 @@ template<typename T, typename TraitsForT>
|
||||||
void HashTable<T, TraitsForT>::insert(T&& value)
|
void HashTable<T, TraitsForT>::insert(T&& value)
|
||||||
{
|
{
|
||||||
auto& bucket = lookup(value);
|
auto& bucket = lookup(value);
|
||||||
bucket.chain.append(move(value));
|
bucket.append(move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename TraitsForT>
|
template<typename T, typename TraitsForT>
|
||||||
void HashTable<T, TraitsForT>::insert(const T& value)
|
void HashTable<T, TraitsForT>::insert(const T& value)
|
||||||
{
|
{
|
||||||
auto& bucket = lookup(value);
|
auto& bucket = lookup(value);
|
||||||
bucket.chain.append(value);
|
bucket.append(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename TraitsForT>
|
template<typename T, typename TraitsForT>
|
||||||
|
@ -360,7 +266,7 @@ bool HashTable<T, TraitsForT>::contains(const T& value) const
|
||||||
if (is_empty())
|
if (is_empty())
|
||||||
return false;
|
return false;
|
||||||
auto& bucket = lookup(value);
|
auto& bucket = lookup(value);
|
||||||
for (auto& e : bucket.chain) {
|
for (auto& e : bucket) {
|
||||||
if (e == value)
|
if (e == value)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -374,8 +280,8 @@ auto HashTable<T, TraitsForT>::find(const T& value) -> Iterator
|
||||||
return end();
|
return end();
|
||||||
int bucket_index;
|
int bucket_index;
|
||||||
auto& bucket = lookup(value, &bucket_index);
|
auto& bucket = lookup(value, &bucket_index);
|
||||||
auto bucket_iterator = bucket.chain.find(value);
|
auto bucket_iterator = bucket.find(value);
|
||||||
if (bucket_iterator != bucket.chain.end())
|
if (bucket_iterator != bucket.end())
|
||||||
return Iterator(*this, false, bucket_iterator, bucket_index);
|
return Iterator(*this, false, bucket_iterator, bucket_index);
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
@ -386,9 +292,9 @@ auto HashTable<T, TraitsForT>::find(const T& value) const -> ConstIterator
|
||||||
if (is_empty())
|
if (is_empty())
|
||||||
return end();
|
return end();
|
||||||
int bucket_index;
|
int bucket_index;
|
||||||
auto& bucket = lookup(value, &bucket_index);
|
const auto& bucket = lookup(value, &bucket_index);
|
||||||
auto bucket_iterator = bucket.chain.find(value);
|
auto bucket_iterator = bucket.find(value);
|
||||||
if (bucket_iterator != bucket.chain.end())
|
if (bucket_iterator != bucket.end())
|
||||||
return ConstIterator(*this, false, bucket_iterator, bucket_index);
|
return ConstIterator(*this, false, bucket_iterator, bucket_index);
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
@ -397,33 +303,23 @@ template<typename T, typename TraitsForT>
|
||||||
void HashTable<T, TraitsForT>::remove(Iterator it)
|
void HashTable<T, TraitsForT>::remove(Iterator it)
|
||||||
{
|
{
|
||||||
ASSERT(!is_empty());
|
ASSERT(!is_empty());
|
||||||
m_buckets[it.m_bucket_index].chain.remove(it.m_bucket_iterator);
|
m_buckets[it.m_bucket_index].remove(it.m_bucket_iterator);
|
||||||
--m_size;
|
--m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename TraitsForT>
|
template<typename T, typename TraitsForT>
|
||||||
typename HashTable<T, TraitsForT>::Bucket& HashTable<T, TraitsForT>::lookup(const T& value, int* bucket_index)
|
auto HashTable<T, TraitsForT>::lookup(const T& value, int* bucket_index) -> Bucket&
|
||||||
{
|
{
|
||||||
unsigned hash = TraitsForT::hash(value);
|
unsigned hash = TraitsForT::hash(value);
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("hash for ");
|
|
||||||
TraitsForT::dump(value);
|
|
||||||
kprintf(" is %u\n", hash);
|
|
||||||
#endif
|
|
||||||
if (bucket_index)
|
if (bucket_index)
|
||||||
*bucket_index = hash % m_capacity;
|
*bucket_index = hash % m_capacity;
|
||||||
return m_buckets[hash % m_capacity];
|
return m_buckets[hash % m_capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename TraitsForT>
|
template<typename T, typename TraitsForT>
|
||||||
const typename HashTable<T, TraitsForT>::Bucket& HashTable<T, TraitsForT>::lookup(const T& value, int* bucket_index) const
|
auto HashTable<T, TraitsForT>::lookup(const T& value, int* bucket_index) const -> const Bucket&
|
||||||
{
|
{
|
||||||
unsigned hash = TraitsForT::hash(value);
|
unsigned hash = TraitsForT::hash(value);
|
||||||
#ifdef HASHTABLE_DEBUG
|
|
||||||
kprintf("hash for ");
|
|
||||||
TraitsForT::dump(value);
|
|
||||||
kprintf(" is %u\n", hash);
|
|
||||||
#endif
|
|
||||||
if (bucket_index)
|
if (bucket_index)
|
||||||
*bucket_index = hash % m_capacity;
|
*bucket_index = hash % m_capacity;
|
||||||
return m_buckets[hash % m_capacity];
|
return m_buckets[hash % m_capacity];
|
||||||
|
@ -436,7 +332,7 @@ void HashTable<T, TraitsForT>::dump() const
|
||||||
for (int i = 0; i < m_capacity; ++i) {
|
for (int i = 0; i < m_capacity; ++i) {
|
||||||
auto& bucket = m_buckets[i];
|
auto& bucket = m_buckets[i];
|
||||||
kprintf("Bucket %u\n", i);
|
kprintf("Bucket %u\n", i);
|
||||||
for (auto& e : bucket.chain) {
|
for (auto& e : bucket) {
|
||||||
kprintf(" > ");
|
kprintf(" > ");
|
||||||
TraitsForT::dump(e);
|
TraitsForT::dump(e);
|
||||||
kprintf("\n");
|
kprintf("\n");
|
||||||
|
|
1
AK/Tests/.gitignore
vendored
1
AK/Tests/.gitignore
vendored
|
@ -1,5 +1,6 @@
|
||||||
TestString
|
TestString
|
||||||
TestQueue
|
TestQueue
|
||||||
TestVector
|
TestVector
|
||||||
|
TestHashMap
|
||||||
*.d
|
*.d
|
||||||
*.o
|
*.o
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
all: TestString TestQueue TestVector
|
PROGRAMS = TestString TestQueue TestVector TestHashMap
|
||||||
|
|
||||||
|
all: $(PROGRAMS)
|
||||||
|
|
||||||
CXXFLAGS = -std=c++17 -Wall -Wextra
|
CXXFLAGS = -std=c++17 -Wall -Wextra
|
||||||
|
|
||||||
|
@ -11,5 +13,8 @@ TestQueue: TestQueue.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ..
|
||||||
TestVector: TestVector.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp TestHelpers.h
|
TestVector: TestVector.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp TestHelpers.h
|
||||||
$(CXX) $(CXXFLAGS) -I../ -I../../ -o $@ TestVector.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp
|
$(CXX) $(CXXFLAGS) -I../ -I../../ -o $@ TestVector.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp
|
||||||
|
|
||||||
|
TestHashMap: TestHashMap.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp TestHelpers.h
|
||||||
|
$(CXX) $(CXXFLAGS) -I../ -I../../ -o $@ TestHashMap.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f TestString TestQueue
|
rm -f $(PROGRAMS)
|
||||||
|
|
28
AK/Tests/TestHashMap.cpp
Normal file
28
AK/Tests/TestHashMap.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
#include <AK/AKString.h>
|
||||||
|
#include <AK/HashMap.h>
|
||||||
|
|
||||||
|
typedef HashMap<int, int> IntIntMap;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
EXPECT(IntIntMap().is_empty());
|
||||||
|
EXPECT(IntIntMap().size() == 0);
|
||||||
|
|
||||||
|
HashMap<int, String> number_to_string;
|
||||||
|
number_to_string.set(1, "One");
|
||||||
|
number_to_string.set(2, "Two");
|
||||||
|
number_to_string.set(3, "Three");
|
||||||
|
|
||||||
|
EXPECT_EQ(number_to_string.is_empty(), false);
|
||||||
|
EXPECT_EQ(number_to_string.size(), 3);
|
||||||
|
|
||||||
|
int loop_counter = 0;
|
||||||
|
for (auto& it : number_to_string) {
|
||||||
|
EXPECT(!it.value.is_null());
|
||||||
|
++loop_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(loop_counter, 3);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue