Feature/common storage class (#23409)

* Moved structures from group data that could be re-used in scenes in a comon location: PersistentData.h

* Refactored the name of Iterator class to CommonIterator

Co-authored-by: Andrei Litvin <andy314@gmail.com>
diff --git a/src/credentials/GroupDataProvider.h b/src/credentials/GroupDataProvider.h
index a782805..9b694d8 100644
--- a/src/credentials/GroupDataProvider.h
+++ b/src/credentials/GroupDataProvider.h
@@ -25,6 +25,7 @@
 #include <crypto/CHIPCryptoPAL.h>
 #include <lib/core/CHIPError.h>
 #include <lib/support/CHIPMemString.h>
+#include <lib/support/CommonIterator.h>
 
 namespace chip {
 namespace Credentials {
@@ -186,39 +187,11 @@
         virtual void OnGroupRemoved(FabricIndex fabric_index, const GroupInfo & old_group) = 0;
     };
 
-    /**
-     * Template used to iterate the stored group data
-     */
-    template <typename T>
-    class Iterator
-    {
-    public:
-        virtual ~Iterator() = default;
-        /**
-         *  @retval The number of entries in total that will be iterated.
-         */
-        virtual size_t Count() = 0;
-        /**
-         *   @param[out] item  Value associated with the next element in the iteration.
-         *  @retval true if the next entry is successfully retrieved.
-         *  @retval false if no more entries can be found.
-         */
-        virtual bool Next(T & item) = 0;
-        /**
-         * Release the memory allocated by this iterator.
-         * Must be called before the pointer goes out of scope.
-         */
-        virtual void Release() = 0;
-
-    protected:
-        Iterator() = default;
-    };
-
-    using GroupInfoIterator    = Iterator<GroupInfo>;
-    using GroupKeyIterator     = Iterator<GroupKey>;
-    using EndpointIterator     = Iterator<GroupEndpoint>;
-    using KeySetIterator       = Iterator<KeySet>;
-    using GroupSessionIterator = Iterator<GroupSession>;
+    using GroupInfoIterator    = CommonIterator<GroupInfo>;
+    using GroupKeyIterator     = CommonIterator<GroupKey>;
+    using EndpointIterator     = CommonIterator<GroupEndpoint>;
+    using KeySetIterator       = CommonIterator<KeySet>;
+    using GroupSessionIterator = CommonIterator<GroupSession>;
 
     GroupDataProvider(uint16_t maxGroupsPerFabric    = CHIP_CONFIG_MAX_GROUPS_PER_FABRIC,
                       uint16_t maxGroupKeysPerFabric = CHIP_CONFIG_MAX_GROUP_KEYS_PER_FABRIC) :
diff --git a/src/credentials/GroupDataProviderImpl.cpp b/src/credentials/GroupDataProviderImpl.cpp
index 180d5cb..87eb9d0 100644
--- a/src/credentials/GroupDataProviderImpl.cpp
+++ b/src/credentials/GroupDataProviderImpl.cpp
@@ -18,10 +18,11 @@
 #include <crypto/CHIPCryptoPAL.h>
 #include <lib/core/CHIPTLV.h>
 #include <lib/support/CodeUtils.h>
+#include <lib/support/CommonPersistentData.h>
 #include <lib/support/DefaultStorageKeyAllocator.h>
+#include <lib/support/PersistentData.h>
 #include <lib/support/Pool.h>
 #include <stdlib.h>
-#include <string.h>
 
 namespace chip {
 namespace Credentials {
@@ -32,69 +33,9 @@
 using EpochKey      = GroupDataProvider::EpochKey;
 using KeySet        = GroupDataProvider::KeySet;
 using GroupSession  = GroupDataProvider::GroupSession;
+using FabricList    = CommonPersistentData::FabricList;
 
-static constexpr size_t kPersistentBufferMax = 128;
-
-template <size_t kMaxSerializedSize>
-struct PersistentData
-{
-    virtual ~PersistentData() = default;
-
-    virtual CHIP_ERROR UpdateKey(StorageKeyName & key)          = 0;
-    virtual CHIP_ERROR Serialize(TLV::TLVWriter & writer) const = 0;
-    virtual CHIP_ERROR Deserialize(TLV::TLVReader & reader)     = 0;
-    virtual void Clear()                                        = 0;
-
-    virtual CHIP_ERROR Save(PersistentStorageDelegate * storage)
-    {
-        VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
-
-        uint8_t buffer[kMaxSerializedSize] = { 0 };
-        StorageKeyName key                 = StorageKeyName::Uninitialized();
-        ReturnErrorOnFailure(UpdateKey(key));
-
-        // Serialize the data
-        TLV::TLVWriter writer;
-        writer.Init(buffer, sizeof(buffer));
-        ReturnErrorOnFailure(Serialize(writer));
-
-        // Save serialized data
-        return storage->SyncSetKeyValue(key.KeyName(), buffer, static_cast<uint16_t>(writer.GetLengthWritten()));
-    }
-
-    CHIP_ERROR Load(PersistentStorageDelegate * storage)
-    {
-        VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
-
-        uint8_t buffer[kMaxSerializedSize] = { 0 };
-        StorageKeyName key                 = StorageKeyName::Uninitialized();
-
-        // Set data to defaults
-        Clear();
-        ReturnErrorOnFailure(UpdateKey(key));
-
-        // Load the serialized data
-        uint16_t size  = static_cast<uint16_t>(sizeof(buffer));
-        CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), buffer, size);
-        VerifyOrReturnError(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND != err, CHIP_ERROR_NOT_FOUND);
-        ReturnErrorOnFailure(err);
-
-        // Decode serialized data
-        TLV::TLVReader reader;
-        reader.Init(buffer, size);
-        return Deserialize(reader);
-    }
-
-    virtual CHIP_ERROR Delete(PersistentStorageDelegate * storage)
-    {
-        VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
-
-        StorageKeyName key = StorageKeyName::Uninitialized();
-        ReturnErrorOnFailure(UpdateKey(key));
-
-        return storage->SyncDeleteKeyValue(key.KeyName());
-    }
-};
+constexpr size_t kPersistentBufferMax = 128;
 
 struct LinkedData : public PersistentData<kPersistentBufferMax>
 {
@@ -110,59 +51,6 @@
     bool first      = true;
 };
 
-struct FabricList : public PersistentData<kPersistentBufferMax>
-{
-    static constexpr TLV::Tag TagFirstFabric() { return TLV::ContextTag(1); }
-    static constexpr TLV::Tag TagFabricCount() { return TLV::ContextTag(2); }
-
-    chip::FabricIndex first_fabric = kUndefinedFabricIndex;
-    uint8_t fabric_count           = 0;
-
-    FabricList() = default;
-    FabricList(chip::FabricIndex first) : first_fabric(first), fabric_count(1) {}
-
-    CHIP_ERROR UpdateKey(StorageKeyName & key) override
-    {
-        key = DefaultStorageKeyAllocator::GroupFabricList();
-        return CHIP_NO_ERROR;
-    }
-
-    void Clear() override
-    {
-        first_fabric = kUndefinedFabricIndex;
-        fabric_count = 0;
-    }
-
-    CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
-    {
-        TLV::TLVType container;
-        ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
-
-        ReturnErrorOnFailure(writer.Put(TagFirstFabric(), static_cast<uint16_t>(first_fabric)));
-        ReturnErrorOnFailure(writer.Put(TagFabricCount(), static_cast<uint16_t>(fabric_count)));
-
-        return writer.EndContainer(container);
-    }
-
-    CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
-    {
-        ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
-        VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
-
-        TLV::TLVType container;
-        ReturnErrorOnFailure(reader.EnterContainer(container));
-
-        // first_fabric
-        ReturnErrorOnFailure(reader.Next(TagFirstFabric()));
-        ReturnErrorOnFailure(reader.Get(first_fabric));
-        // fabric_count
-        ReturnErrorOnFailure(reader.Next(TagFabricCount()));
-        ReturnErrorOnFailure(reader.Get(fabric_count));
-
-        return reader.ExitContainer(container);
-    }
-};
-
 struct FabricData : public PersistentData<kPersistentBufferMax>
 {
     static constexpr TLV::Tag TagFirstGroup() { return TLV::ContextTag(1); }
@@ -257,15 +145,15 @@
         if (CHIP_ERROR_NOT_FOUND == err)
         {
             // New fabric list
-            fabric_list.first_fabric = fabric_index;
-            fabric_list.fabric_count = 1;
+            fabric_list.first_entry = fabric_index;
+            fabric_list.entry_count = 1;
             return fabric_list.Save(storage);
         }
         ReturnErrorOnFailure(err);
 
         // Existing fabric list, search for existing entry
-        FabricData fabric(fabric_list.first_fabric);
-        for (size_t i = 0; i < fabric_list.fabric_count; i++)
+        FabricData fabric(fabric_list.first_entry);
+        for (size_t i = 0; i < fabric_list.entry_count; i++)
         {
             err = fabric.Load(storage);
             if (CHIP_NO_ERROR != err)
@@ -280,9 +168,9 @@
             fabric.fabric_index = fabric.next;
         }
         // Add this fabric to the fabric list
-        this->next               = fabric_list.first_fabric;
-        fabric_list.first_fabric = this->fabric_index;
-        fabric_list.fabric_count++;
+        this->next              = fabric_list.first_entry;
+        fabric_list.first_entry = this->fabric_index;
+        fabric_list.entry_count++;
         return fabric_list.Save(storage);
     }
 
@@ -294,10 +182,10 @@
         VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
 
         // Existing fabric list, search for existing entry
-        FabricData fabric(fabric_list.first_fabric);
+        FabricData fabric(fabric_list.first_entry);
         FabricData prev;
 
-        for (size_t i = 0; i < fabric_list.fabric_count; i++)
+        for (size_t i = 0; i < fabric_list.entry_count; i++)
         {
             err = fabric.Load(storage);
             if (CHIP_NO_ERROR != err)
@@ -310,7 +198,7 @@
                 if (i == 0)
                 {
                     // Remove first fabric
-                    fabric_list.first_fabric = this->next;
+                    fabric_list.first_entry = this->next;
                 }
                 else
                 {
@@ -318,8 +206,8 @@
                     prev.next = this->next;
                     ReturnErrorOnFailure(prev.Save(storage));
                 }
-                VerifyOrReturnError(fabric_list.fabric_count > 0, CHIP_ERROR_INTERNAL);
-                fabric_list.fabric_count--;
+                VerifyOrReturnError(fabric_list.entry_count > 0, CHIP_ERROR_INTERNAL);
+                fabric_list.entry_count--;
                 return fabric_list.Save(storage);
             }
             prev                = fabric;
@@ -336,9 +224,9 @@
         ReturnErrorOnFailure(fabric_list.Load(storage));
 
         // Existing fabric list, search for existing entry
-        FabricData fabric(fabric_list.first_fabric);
+        FabricData fabric(fabric_list.first_entry);
 
-        for (size_t i = 0; i < fabric_list.fabric_count; i++)
+        for (size_t i = 0; i < fabric_list.entry_count; i++)
         {
             ReturnErrorOnFailure(fabric.Load(storage));
             if (fabric.fabric_index == this->fabric_index)
@@ -1892,10 +1780,10 @@
 {
     FabricList fabric_list;
     ReturnOnFailure(fabric_list.Load(provider.mStorage));
-    mFirstFabric = fabric_list.first_fabric;
-    mFabric      = fabric_list.first_fabric;
+    mFirstFabric = fabric_list.first_entry;
+    mFabric      = fabric_list.first_entry;
     mFabricCount = 0;
-    mFabricTotal = fabric_list.fabric_count;
+    mFabricTotal = fabric_list.entry_count;
     mMapCount    = 0;
     mFirstMap    = true;
 }
diff --git a/src/lib/support/CommonIterator.h b/src/lib/support/CommonIterator.h
new file mode 100644
index 0000000..689625a
--- /dev/null
+++ b/src/lib/support/CommonIterator.h
@@ -0,0 +1,55 @@
+/*
+ *
+ *    Copyright (c) 2022 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *          Contains a standard iterator class.
+ */
+
+#pragma once
+
+namespace chip {
+
+/**
+ * Template used to generate a custom iterator
+ */
+template <typename T>
+class CommonIterator
+{
+public:
+    virtual ~CommonIterator() = default;
+    /**
+     *  @retval The number of entries in total that will be iterated.
+     */
+    virtual size_t Count() = 0;
+    /**
+     *   @param[out] item  Value associated with the next element in the iteration.
+     *  @retval true if the next entry is successfully retrieved.
+     *  @retval false if no more entries can be found.
+     */
+    virtual bool Next(T & item) = 0;
+    /**
+     * Release the memory allocated by this iterator.
+     * Must be called before the iterator goes out of scope in the iterator was dynamically allocated.
+     */
+    virtual void Release() = 0;
+
+protected:
+    CommonIterator() = default;
+};
+
+} // namespace chip
diff --git a/src/lib/support/CommonPersistentData.h b/src/lib/support/CommonPersistentData.h
new file mode 100644
index 0000000..24c3e9a
--- /dev/null
+++ b/src/lib/support/CommonPersistentData.h
@@ -0,0 +1,92 @@
+/*
+ *
+ *    Copyright (c) 2022 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *          Contains a class handling creation of linked list of stored persistent data.
+ */
+
+#include <lib/core/DataModelTypes.h>
+#include <lib/support/PersistentData.h>
+
+namespace chip {
+namespace CommonPersistentData {
+
+constexpr uint8_t kdefaultUndefinedEntry = 0;
+
+/// @brief Generic class to implement storage of a list persistently
+/// @tparam EntryType : Type of entry depends on the stored data
+/// @tparam kMaxSerializedSize : inherited from PersistentData class
+template <typename EntryType, size_t kMaxSerializedSize>
+struct StoredDataList : public PersistentData<kMaxSerializedSize>
+{
+    static constexpr TLV::Tag TagFirstEntry() { return TLV::ContextTag(1); }
+    static constexpr TLV::Tag TagEntryCount() { return TLV::ContextTag(2); }
+
+    EntryType first_entry = kdefaultUndefinedEntry;
+    uint16_t entry_count  = 0;
+
+    StoredDataList() = default;
+    StoredDataList(EntryType first) : first_entry(first), entry_count(1) {}
+
+    CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
+    {
+        TLV::TLVType container;
+        ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
+
+        ReturnErrorOnFailure(writer.Put(TagFirstEntry(), static_cast<uint16_t>(first_entry)));
+        ReturnErrorOnFailure(writer.Put(TagEntryCount(), static_cast<uint16_t>(entry_count)));
+
+        return writer.EndContainer(container);
+    }
+
+    CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
+    {
+        ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag()));
+        VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL);
+
+        TLV::TLVType container;
+        ReturnErrorOnFailure(reader.EnterContainer(container));
+
+        // first_entry
+        ReturnErrorOnFailure(reader.Next(TagFirstEntry()));
+        ReturnErrorOnFailure(reader.Get(first_entry));
+        // entry_count
+        ReturnErrorOnFailure(reader.Next(TagEntryCount()));
+        ReturnErrorOnFailure(reader.Get(entry_count));
+
+        return reader.ExitContainer(container);
+    }
+};
+
+constexpr size_t kPersistentFabricBufferMax = 32;
+struct FabricList : StoredDataList<FabricIndex, kPersistentFabricBufferMax>
+{
+    CHIP_ERROR UpdateKey(StorageKeyName & key) override
+    {
+        key = DefaultStorageKeyAllocator::FabricList();
+        return CHIP_NO_ERROR;
+    }
+
+    void Clear() override
+    {
+        first_entry = kUndefinedFabricIndex;
+        entry_count = 0;
+    }
+};
+} // namespace CommonPersistentData
+} // namespace chip
diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h
index 6a4b906..dd7098e 100644
--- a/src/lib/support/DefaultStorageKeyAllocator.h
+++ b/src/lib/support/DefaultStorageKeyAllocator.h
@@ -102,6 +102,9 @@
     static StorageKeyName FabricMetadata(FabricIndex fabric) { return StorageKeyName::Formatted("f/%x/m", fabric); }
     static StorageKeyName FabricOpKey(FabricIndex fabric) { return StorageKeyName::Formatted("f/%x/o", fabric); }
 
+    // Fabric List
+    static StorageKeyName FabricList() { return StorageKeyName::FromConst("g/fl"); }
+
     // Fail-safe handling
     static StorageKeyName FailSafeCommitMarkerKey() { return StorageKeyName::FromConst("g/fs/c"); }
     static StorageKeyName FailSafeNetworkConfig() { return StorageKeyName::FromConst("g/fs/n"); }
@@ -144,7 +147,6 @@
     // Group Data Provider
 
     // List of fabric indices that have endpoint-to-group associations defined.
-    static StorageKeyName GroupFabricList() { return StorageKeyName::FromConst("g/gfl"); }
     static StorageKeyName FabricGroups(chip::FabricIndex fabric) { return StorageKeyName::Formatted("f/%x/g", fabric); }
     static StorageKeyName FabricGroup(chip::FabricIndex fabric, chip::GroupId group)
     {
diff --git a/src/lib/support/PersistentData.h b/src/lib/support/PersistentData.h
new file mode 100644
index 0000000..b1eceaf
--- /dev/null
+++ b/src/lib/support/PersistentData.h
@@ -0,0 +1,91 @@
+/*
+ *
+ *    Copyright (c) 2022 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#pragma once
+
+#include <lib/core/CHIPPersistentStorageDelegate.h>
+#include <lib/core/CHIPTLV.h>
+#include <lib/support/DefaultStorageKeyAllocator.h>
+
+namespace chip {
+
+/// @brief Interface to Persistent Storage Delegate allowing storage of data of variable size such as TLV.
+/// @tparam kMaxSerializedSize size of the buffer necessary to retrieve an entry from the storage. Varies with the type of data
+/// stored. Will be allocated on the stack so the implementation needs to be aware of this when choosing this value.
+template <size_t kMaxSerializedSize>
+struct PersistentData
+{
+    virtual ~PersistentData() = default;
+
+    virtual CHIP_ERROR UpdateKey(StorageKeyName & key)          = 0;
+    virtual CHIP_ERROR Serialize(TLV::TLVWriter & writer) const = 0;
+    virtual CHIP_ERROR Deserialize(TLV::TLVReader & reader)     = 0;
+    virtual void Clear()                                        = 0;
+
+    virtual CHIP_ERROR Save(PersistentStorageDelegate * storage)
+    {
+        VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
+
+        uint8_t buffer[kMaxSerializedSize] = { 0 };
+        StorageKeyName key                 = StorageKeyName::Uninitialized();
+        ReturnErrorOnFailure(UpdateKey(key));
+
+        // Serialize the data
+        TLV::TLVWriter writer;
+        writer.Init(buffer, sizeof(buffer));
+        ReturnErrorOnFailure(Serialize(writer));
+
+        // Save serialized data
+        return storage->SyncSetKeyValue(key.KeyName(), buffer, static_cast<uint16_t>(writer.GetLengthWritten()));
+    }
+
+    CHIP_ERROR Load(PersistentStorageDelegate * storage)
+    {
+        VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
+
+        uint8_t buffer[kMaxSerializedSize] = { 0 };
+        StorageKeyName key                 = StorageKeyName::Uninitialized();
+
+        // Set data to defaults
+        Clear();
+
+        // Update storage key
+        ReturnErrorOnFailure(UpdateKey(key));
+
+        // Load the serialized data
+        uint16_t size  = static_cast<uint16_t>(sizeof(buffer));
+        CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), buffer, size);
+        VerifyOrReturnError(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND != err, CHIP_ERROR_NOT_FOUND);
+        ReturnErrorOnFailure(err);
+
+        // Decode serialized data
+        TLV::TLVReader reader;
+        reader.Init(buffer, size);
+        return Deserialize(reader);
+    }
+
+    virtual CHIP_ERROR Delete(PersistentStorageDelegate * storage)
+    {
+        VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
+
+        StorageKeyName key = StorageKeyName::Uninitialized();
+        ReturnErrorOnFailure(UpdateKey(key));
+
+        return storage->SyncDeleteKeyValue(key.KeyName());
+    }
+};
+
+} // namespace chip