Browse Source

AK: Implement reverse iterators for `OrderedHashTable`

Daniel Bertalan 1 year ago
parent
commit
4d2af7c3d6
2 changed files with 89 additions and 0 deletions
  1. 58 0
      AK/HashTable.h
  2. 31 0
      Tests/AK/TestHashTable.cpp

+ 58 - 0
AK/HashTable.h

@@ -9,6 +9,7 @@
 
 #include <AK/Concepts.h>
 #include <AK/Error.h>
+#include <AK/ReverseIterator.h>
 #include <AK/StdLibExtras.h>
 #include <AK/Traits.h>
 #include <AK/Types.h>
@@ -92,6 +93,27 @@ private:
     BucketType* m_bucket { nullptr };
 };
 
+template<typename OrderedHashTableType, typename T, typename BucketType>
+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<typename T, typename TraitsForT, bool IsOrdered>
 class HashTable {
     static constexpr size_t grow_capacity_at_least = 8;
@@ -275,6 +297,42 @@ public:
         return ConstIterator(nullptr, nullptr);
     }
 
+    using ReverseIterator = Conditional<IsOrdered,
+        ReverseOrderedHashTableIterator<HashTable, T, BucketType>,
+        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<IsOrdered,
+        ReverseOrderedHashTableIterator<HashTable const, T const, BucketType const>,
+        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();

+ 31 - 0
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<DeprecatedString> 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<DeprecatedString> 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<int, 4> { 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)