pw_kvs: Define mutable iterator for EntryCache

Template the EntryCache iterator to support both const and mutable
iterators.

Change-Id: I1f2102325414d9703ecc0afc26633fec22b16a3c
diff --git a/pw_kvs/entry_cache_test.cc b/pw_kvs/entry_cache_test.cc
index 3b9d1a1..615d78c 100644
--- a/pw_kvs/entry_cache_test.cc
+++ b/pw_kvs/entry_cache_test.cc
@@ -166,6 +166,34 @@
   }
 }
 
+TEST_F(EmptyEntryCache, Iterator_Mutable_CanModify) {
+  entries_.AddNew(kDescriptor, 1);
+  EntryCache::iterator it = entries_.begin();
+
+  static_assert(kRedundancy > 1);
+  it->AddNewAddress(1234);
+
+  EXPECT_EQ(1u, it->first_address());
+  EXPECT_EQ(1u, (*it).addresses()[0]);
+  EXPECT_EQ(1234u, it->addresses()[1]);
+}
+
+TEST_F(EmptyEntryCache, Iterator_Const) {
+  entries_.AddNew(kDescriptor, 99);
+  EntryCache::const_iterator it = entries_.cbegin();
+
+  EXPECT_EQ(99u, (*it).first_address());
+  EXPECT_EQ(99u, it->first_address());
+}
+
+TEST_F(EmptyEntryCache, Iterator_Const_CanBeAssignedFromMutable) {
+  entries_.AddNew(kDescriptor, 99);
+  EntryCache::const_iterator it = entries_.begin();
+
+  EXPECT_EQ(99u, (*it).first_address());
+  EXPECT_EQ(99u, it->first_address());
+}
+
 constexpr auto kTheEntry = AsBytes(uint32_t(12345),  // magic
                                    uint32_t(0),      // checksum
                                    uint8_t(0),       // alignment (16 B)
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index 13708ae..d0ab512 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -381,7 +381,7 @@
 }
 
 KeyValueStore::iterator KeyValueStore::begin() const {
-  internal::EntryCache::iterator cache_iterator = entry_cache_.begin();
+  internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
   // Skip over any deleted entries at the start of the descriptor list.
   while (cache_iterator != entry_cache_.end() &&
          cache_iterator->state() != EntryState::kValid) {
diff --git a/pw_kvs/public/pw_kvs/internal/entry_cache.h b/pw_kvs/public/pw_kvs/internal/entry_cache.h
index 99c2008..48688a6 100644
--- a/pw_kvs/public/pw_kvs/internal/entry_cache.h
+++ b/pw_kvs/public/pw_kvs/internal/entry_cache.h
@@ -16,6 +16,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <string_view>
+#include <type_traits>
 
 #include "pw_containers/vector.h"
 #include "pw_kvs/flash_memory.h"
@@ -74,7 +75,61 @@
 // Tracks entry metadata. Combines KeyDescriptors and with their associated
 // addresses.
 class EntryCache {
+ private:
+  enum Constness : bool { kMutable = false, kConst = true };
+
+  // Iterates over the EntryCache as EntryMetadata objects.
+  template <Constness kIsConst>
+  class Iterator {
+   public:
+    using value_type =
+        std::conditional_t<kIsConst, const EntryMetadata, EntryMetadata>;
+
+    Iterator& operator++() {
+      ++metadata_.descriptor_;
+      return *this;
+    }
+    Iterator& operator++(int) { return operator++(); }
+
+    // Updates the internal EntryMetadata object.
+    value_type& operator*() const {
+      metadata_.addresses_ = entry_cache_->addresses(
+          metadata_.descriptor_ - entry_cache_->descriptors_.begin());
+      return metadata_;
+    }
+    value_type* operator->() const { return &operator*(); }
+
+    constexpr bool operator==(const Iterator& rhs) const {
+      return metadata_.descriptor_ == rhs.metadata_.descriptor_;
+    }
+    constexpr bool operator!=(const Iterator& rhs) const {
+      return metadata_.descriptor_ != rhs.metadata_.descriptor_;
+    }
+
+    // Allow non-const to convert to const.
+    operator Iterator<kConst>() const {
+      return {entry_cache_, metadata_.descriptor_};
+    }
+
+   private:
+    friend class EntryCache;
+
+    using Cache = std::conditional_t<kIsConst, const EntryCache, EntryCache>;
+
+    constexpr Iterator(Cache* entry_cache, KeyDescriptor* descriptor)
+        : entry_cache_(entry_cache), metadata_(*descriptor, {}) {}
+
+    Cache* entry_cache_;
+
+    // Mark this mutable so it can be updated in the const operator*() method.
+    // This allows lazy updating of the EntryMetadata.
+    mutable EntryMetadata metadata_;
+  };
+
  public:
+  using iterator = Iterator<kMutable>;
+  using const_iterator = Iterator<kConst>;
+
   using Address = FlashPartition::Address;
 
   // The type to use for an address list with the specified number of entries
@@ -152,47 +207,13 @@
   // The maximum number of entries supported by this EntryCache.
   size_t max_entries() const { return descriptors_.max_size(); }
 
-  // Iterates over the EntryCache as EntryMetadata objects.
-  class iterator {
-   public:
-    iterator& operator++() {
-      ++metadata_.descriptor_;
-      return *this;
-    }
-    iterator& operator++(int) { return operator++(); }
+  iterator begin() { return {this, descriptors_.begin()}; }
+  const_iterator begin() const { return cbegin(); }
+  const_iterator cbegin() const { return {this, descriptors_.begin()}; }
 
-    // Updates the EntryMetadata object.
-    const EntryMetadata& operator*() const {
-      metadata_.addresses_ = entry_cache_.addresses(
-          metadata_.descriptor_ - entry_cache_.descriptors_.begin());
-      return metadata_;
-    }
-    const EntryMetadata* operator->() const { return &operator*(); }
-
-    constexpr bool operator==(const iterator& rhs) const {
-      return metadata_.descriptor_ == rhs.metadata_.descriptor_;
-    }
-    constexpr bool operator!=(const iterator& rhs) const {
-      return metadata_.descriptor_ != rhs.metadata_.descriptor_;
-    }
-
-   private:
-    friend class EntryCache;
-
-    constexpr iterator(const EntryCache* entry_cache, KeyDescriptor* descriptor)
-        : entry_cache_(*entry_cache), metadata_(*descriptor, {}) {}
-
-    const EntryCache& entry_cache_;
-
-    // Mark this mutable so it can be updated in the const operator*() method.
-    // This allows lazy updating of the EntryMetadata.
-    mutable EntryMetadata metadata_;
-  };
-
-  using const_iterator = iterator;
-
-  iterator begin() const { return iterator(this, descriptors_.begin()); }
-  iterator end() const { return iterator(this, descriptors_.end()); }
+  iterator end() { return {this, descriptors_.end()}; }
+  const_iterator end() const { return cend(); }
+  const_iterator cend() const { return {this, descriptors_.end()}; }
 
  private:
   int FindIndex(uint32_t key_hash) const;
diff --git a/pw_kvs/public/pw_kvs/key_value_store.h b/pw_kvs/public/pw_kvs/key_value_store.h
index fe705b6..69193e4 100644
--- a/pw_kvs/public/pw_kvs/key_value_store.h
+++ b/pw_kvs/public/pw_kvs/key_value_store.h
@@ -213,13 +213,13 @@
     friend class iterator;
 
     constexpr Item(const KeyValueStore& kvs,
-                   const internal::EntryCache::iterator& iterator)
+                   const internal::EntryCache::const_iterator& iterator)
         : kvs_(kvs), iterator_(iterator), key_buffer_{} {}
 
     void ReadKey();
 
     const KeyValueStore& kvs_;
-    internal::EntryCache::iterator iterator_;
+    internal::EntryCache::const_iterator iterator_;
 
     // Buffer large enough for a null-terminated version of any valid key.
     std::array<char, internal::Entry::kMaxKeyLength + 1> key_buffer_;
@@ -253,7 +253,7 @@
     friend class KeyValueStore;
 
     constexpr iterator(const KeyValueStore& kvs,
-                       const internal::EntryCache::iterator& iterator)
+                       const internal::EntryCache::const_iterator& iterator)
         : item_(kvs, iterator) {}
 
     Item item_;