From 4d2af7c3d6e5ecfa4873050b8a7dc4dbb8c91515 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Sun, 24 Sep 2023 20:34:55 +0200 Subject: [PATCH] AK: Implement reverse iterators for `OrderedHashTable` --- AK/HashTable.h | 58 ++++++++++++++++++++++++++++++++++++++ Tests/AK/TestHashTable.cpp | 31 ++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/AK/HashTable.h b/AK/HashTable.h index 56794d60474..b56c0ce3bcc 100644 --- a/AK/HashTable.h +++ b/AK/HashTable.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -92,6 +93,27 @@ private: BucketType* m_bucket { nullptr }; }; +template +class ReverseOrderedHashTableIterator { + friend OrderedHashTableType; + +public: + bool operator==(ReverseOrderedHashTableIterator const& other) const { return m_bucket == other.m_bucket; } + bool operator!=(ReverseOrderedHashTableIterator const& other) const { return m_bucket != other.m_bucket; } + T& operator*() { return *m_bucket->slot(); } + T* operator->() { return m_bucket->slot(); } + void operator++() { m_bucket = m_bucket->previous; } + void operator--() { m_bucket = m_bucket->next; } + +private: + ReverseOrderedHashTableIterator(BucketType* bucket) + : m_bucket(bucket) + { + } + + BucketType* m_bucket { nullptr }; +}; + template class HashTable { static constexpr size_t grow_capacity_at_least = 8; @@ -275,6 +297,42 @@ public: return ConstIterator(nullptr, nullptr); } + using ReverseIterator = Conditional, + void>; + + [[nodiscard]] ReverseIterator rbegin() + requires(IsOrdered) + { + return ReverseIterator(m_collection_data.tail); + } + + [[nodiscard]] ReverseIterator rend() + requires(IsOrdered) + { + return ReverseIterator(nullptr); + } + + auto in_reverse() { return ReverseWrapper::in_reverse(*this); } + + using ReverseConstIterator = Conditional, + void>; + + [[nodiscard]] ReverseConstIterator rbegin() const + requires(IsOrdered) + { + return ReverseConstIterator(m_collection_data.tail); + } + + [[nodiscard]] ReverseConstIterator rend() const + requires(IsOrdered) + { + return ReverseConstIterator(nullptr); + } + + auto in_reverse() const { return ReverseWrapper::in_reverse(*this); } + void clear() { *this = HashTable(); diff --git a/Tests/AK/TestHashTable.cpp b/Tests/AK/TestHashTable.cpp index 5592871ed1a..c399418b7b5 100644 --- a/Tests/AK/TestHashTable.cpp +++ b/Tests/AK/TestHashTable.cpp @@ -70,6 +70,24 @@ TEST_CASE(range_loop) EXPECT_EQ(loop_counter, 3); } +TEST_CASE(range_loop_reverse) +{ + Array strings = { "One"sv, "Two"sv, "Three"sv }; + OrderedHashTable table; + EXPECT_EQ(table.set(strings[0]), AK::HashSetResult::InsertedNewEntry); + EXPECT_EQ(table.set(strings[1]), AK::HashSetResult::InsertedNewEntry); + EXPECT_EQ(table.set(strings[2]), AK::HashSetResult::InsertedNewEntry); + + int loop_counter = 0; + int index = strings.size() - 1; + for (auto& it : table.in_reverse()) { + EXPECT_EQ(it, strings[index]); + ++loop_counter; + --index; + } + EXPECT_EQ(loop_counter, 3); +} + TEST_CASE(table_remove) { HashTable strings; @@ -326,6 +344,12 @@ TEST_CASE(ordered_insertion_and_deletion) EXPECT_EQ(*it, values[index]); EXPECT(table.contains(values[index])); } + + index = table.size() - 1; + for (auto it = table.rbegin(); it != table.rend(); ++it, --index) { + EXPECT_EQ(*it, values[index]); + EXPECT(table.contains(values[index])); + } }; expect_table(table, Array { 0, 1, 2, 3 }); @@ -357,6 +381,13 @@ TEST_CASE(ordered_deletion_and_reinsertion) EXPECT_EQ(*it, 1); ++it; EXPECT_EQ(it, table.end()); + + auto rit = table.rbegin(); + EXPECT_EQ(*rit, 1); + ++rit; + EXPECT_EQ(*rit, 3); + ++rit; + EXPECT_EQ(rit, table.rend()); } TEST_CASE(ordered_take_last)