Further FabricTable improvements: (#39164)

* Remove the need to allocate stack memory when removing an entry from persistence
* Remove internal dependency on TableEntry, instead referencing data & id directly
diff --git a/src/app/clusters/scenes-server/SceneTableImpl.cpp b/src/app/clusters/scenes-server/SceneTableImpl.cpp
index 91ee39e..a6286e0 100644
--- a/src/app/clusters/scenes-server/SceneTableImpl.cpp
+++ b/src/app/clusters/scenes-server/SceneTableImpl.cpp
@@ -131,12 +131,16 @@
 
 CHIP_ERROR DefaultSceneTableImpl::SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry)
 {
-    return this->SetTableEntry(fabric_index, entry);
+    return this->SetTableEntry(fabric_index, entry.mStorageId, entry.mStorageData);
 }
 
 CHIP_ERROR DefaultSceneTableImpl::GetSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id, SceneTableEntry & entry)
 {
-    return this->GetTableEntry(fabric_index, scene_id, entry);
+    // All data is copied to SceneTableEntry, buffer can be allocated on stack
+    PersistentStore<Serializer::kEntryMaxBytes()> store;
+    ReturnErrorOnFailure(this->GetTableEntry(fabric_index, scene_id, entry.mStorageData, store));
+    entry.mStorageId = scene_id;
+    return CHIP_NO_ERROR;
 }
 
 CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id)
@@ -198,7 +202,7 @@
         if (fabric.entry_map[i].mGroupId == group_id)
         {
             // Removing each scene from the nvm and clearing their entry in the scene map
-            ReturnErrorOnFailure(fabric.RemoveEntry(mStorage, fabric.entry_map[i]));
+            ReturnErrorOnFailure(fabric.RemoveEntry(*mStorage, fabric.entry_map[i]));
         }
     }
 
diff --git a/src/app/storage/FabricTableImpl.h b/src/app/storage/FabricTableImpl.h
index adf022e..43e384e 100644
--- a/src/app/storage/FabricTableImpl.h
+++ b/src/app/storage/FabricTableImpl.h
@@ -27,6 +27,45 @@
 namespace Storage {
 
 /**
+ * @brief Container which exposes various compile-time constants for specializations
+ * of FabricTableImpl.
+ */
+template <class StorageId, class StorageData>
+class DefaultSerializer
+{
+public:
+    // gcc bug prevents us from using a static variable; see:
+    // https://stackoverflow.com/questions/50638053/constexpr-static-data-member-without-initializer
+    // The number of bytes used by an entry (StorageData) + its metadata when persisting to storage
+    static constexpr size_t kEntryMaxBytes();
+    // The number of bytes used by FabricEntryData, which is dependent on the size of StorageId
+    static constexpr size_t kFabricMaxBytes();
+    // The max number of entries per fabric; this value directly affects memory usage
+    static constexpr uint16_t kMaxPerFabric();
+    // The max number of entries for the endpoint (programmatic limit)
+    static constexpr uint16_t kMaxPerEndpoint();
+
+    DefaultSerializer() {}
+    ~DefaultSerializer(){};
+
+    static CHIP_ERROR SerializeId(TLV::TLVWriter & writer, const StorageId & id);
+    static CHIP_ERROR DeserializeId(TLV::TLVReader & reader, StorageId & id);
+
+    static CHIP_ERROR SerializeData(TLV::TLVWriter & writer, const StorageData & data);
+    static CHIP_ERROR DeserializeData(TLV::TLVReader & reader, StorageData & data);
+
+    static StorageKeyName EndpointEntryCountKey(EndpointId endpoint);
+    // The key for persisting the data for a fabric
+    static StorageKeyName FabricEntryDataKey(FabricIndex fabric, EndpointId endpoint);
+    // The key for persisting the data for an entry in a fabric; FabricEntryDataKey should be a root prefix
+    // of this key, such that removing a fabric removes all its entries
+    static StorageKeyName FabricEntryKey(FabricIndex fabric, EndpointId endpoint, uint16_t idx);
+
+    // Clears the data to default values
+    static void Clear(StorageData & data) { data.Clear(); }
+}; // class DefaultSerializer
+
+/**
  * @brief Implementation of a storage accessor in nonvolatile storage of a templatized table that stores by fabric index.
  *        This class does not actually hold the entries, but rather acts as a wrapper/accessor around the storage layer,
  *        reading entries from the storage pointed to by calling SetEndpoint.
@@ -37,11 +76,13 @@
 template <class StorageId, class StorageData, size_t kIteratorsMax>
 class FabricTableImpl
 {
-    using TableEntry    = Data::TableEntry<StorageId, StorageData>;
-    using EntryIterator = CommonIterator<TableEntry>;
-    using EntryIndex    = Data::EntryIndex;
+    using TableEntry = Data::TableEntry<StorageId, StorageData>;
 
 public:
+    using EntryIterator = CommonIterator<TableEntry>;
+    using EntryIndex    = Data::EntryIndex;
+    using Serializer    = DefaultSerializer<StorageId, StorageData>;
+
     virtual ~FabricTableImpl() { Finish(); };
 
     CHIP_ERROR Init(PersistentStorageDelegate & storage);
@@ -65,9 +106,22 @@
 
     // Data
     CHIP_ERROR GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity);
-    CHIP_ERROR SetTableEntry(FabricIndex fabric_index, const TableEntry & entry);
-    CHIP_ERROR GetTableEntry(FabricIndex fabric_index, StorageId entry_id, TableEntry & entry);
-    CHIP_ERROR RemoveTableEntry(FabricIndex fabric_index, StorageId entry_id);
+    CHIP_ERROR SetTableEntry(FabricIndex fabric_index, const StorageId & entry_id, const StorageData & data);
+
+    /**
+     * @brief Loads the entry from persistent storage.
+     * @param fabric_index the fabric to load the entry from
+     * @param entry_id the unique entry identifier
+     * @param data the target for the loaded data
+     * @param buffer the buffer that will be used to load from persistence; some data types in the data argument, such as
+     * DecodableList, point directly into the buffer, and as such for those types of structures the lifetime of the buffer needs to
+     * be equal to or greater than data
+     */
+    template <size_t kEntryMaxBytes>
+    CHIP_ERROR GetTableEntry(FabricIndex fabric_index, StorageId & entry_id, StorageData & data,
+                             PersistentStore<kEntryMaxBytes> & buffer);
+    CHIP_ERROR FindTableEntry(FabricIndex fabric_index, const StorageId & entry_id, EntryIndex & idx);
+    CHIP_ERROR RemoveTableEntry(FabricIndex fabric_index, const StorageId & entry_id);
     CHIP_ERROR RemoveTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index, EntryIndex entry_idx);
 
     // Fabrics
@@ -124,38 +178,6 @@
     PersistentStorageDelegate * mStorage = nullptr;
 }; // class FabricTableImpl
 
-template <class StorageId, class StorageData>
-class DefaultSerializer
-{
-public:
-    // gcc bug prevents us from using a static variable; see:
-    // https://stackoverflow.com/questions/50638053/constexpr-static-data-member-without-initializer
-    // The number of bytes used by an entry (StorageData) + its metadata when persisting to storage
-    static constexpr size_t kEntryMaxBytes();
-    // The number of bytes used by FabricEntryData, which is dependent on the size of StorageId
-    static constexpr size_t kFabricMaxBytes();
-    // The max number of entries per fabric; this value directly affects memory usage
-    static constexpr uint16_t kMaxPerFabric();
-    // The max number of entries for the endpoint (programmatic limit)
-    static constexpr uint16_t kMaxPerEndpoint();
-
-    DefaultSerializer() {}
-    ~DefaultSerializer(){};
-
-    static CHIP_ERROR SerializeId(TLV::TLVWriter & writer, const StorageId & id);
-    static CHIP_ERROR DeserializeId(TLV::TLVReader & reader, StorageId & id);
-
-    static CHIP_ERROR SerializeData(TLV::TLVWriter & writer, const StorageData & data);
-    static CHIP_ERROR DeserializeData(TLV::TLVReader & reader, StorageData & data);
-
-    static StorageKeyName EndpointEntryCountKey(EndpointId endpoint);
-    // The key for persisting the data for a fabric
-    static StorageKeyName FabricEntryDataKey(FabricIndex fabric, EndpointId endpoint);
-    // The key for persisting the data for an entry in a fabric; FabricEntryDataKey should be a root prefix
-    // of this key, such that removing a fabric removes all its entries
-    static StorageKeyName FabricEntryKey(FabricIndex fabric, EndpointId endpoint, uint16_t idx);
-}; // class DefaultSerializer
-
 } // namespace Storage
 } // namespace app
 } // namespace chip
diff --git a/src/app/storage/FabricTableImpl.ipp b/src/app/storage/FabricTableImpl.ipp
index 8542c36..a5531b0 100644
--- a/src/app/storage/FabricTableImpl.ipp
+++ b/src/app/storage/FabricTableImpl.ipp
@@ -121,22 +121,25 @@
 
     inline const T * operator->() const { return &mRef; }
     inline T * operator->() { return &mRef; }
+
+    inline const T & operator*() const { return mRef; }
+    inline T & operator*() { return mRef; }
 };
 
 template <class StorageId, class StorageData>
 struct TableEntryData : DataAccessor
 {
     using Serializer = DefaultSerializer<StorageId, StorageData>;
-    using TableEntry = Data::TableEntry<StorageId, StorageData>;
 
     EndpointId endpoint_id   = kInvalidEndpointId;
     FabricIndex fabric_index = kUndefinedFabricIndex;
     EntryIndex index         = 0;
     bool first               = true;
-    ConstCorrectRef<Data::TableEntry<StorageId, StorageData>> mEntry;
+    ConstCorrectRef<StorageId> storage_id;
+    ConstCorrectRef<StorageData> storage_data;
 
-    TableEntryData(EndpointId endpoint, FabricIndex fabric, TableEntry & entry, EntryIndex idx = 0) :
-        endpoint_id(endpoint), fabric_index(fabric), index(idx), mEntry(entry)
+    TableEntryData(EndpointId endpoint, FabricIndex fabric, StorageId & id, StorageData & data, EntryIndex idx = 0) :
+        endpoint_id(endpoint), fabric_index(fabric), index(idx), storage_id(id), storage_data(data)
     {}
 
     CHIP_ERROR UpdateKey(StorageKeyName & key) const override
@@ -147,16 +150,16 @@
         return CHIP_NO_ERROR;
     }
 
-    void Clear() override { this->mEntry->mStorageData.Clear(); }
+    void Clear() override { Serializer::Clear(*storage_data); }
 
     CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
     {
         TLV::TLVType container;
         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
 
-        ReturnErrorOnFailure(Serializer::SerializeId(writer, this->mEntry->mStorageId));
+        ReturnErrorOnFailure(Serializer::SerializeId(writer, *storage_id));
 
-        ReturnErrorOnFailure(Serializer::SerializeData(writer, this->mEntry->mStorageData));
+        ReturnErrorOnFailure(Serializer::SerializeData(writer, *storage_data));
 
         return writer.EndContainer(container);
     }
@@ -168,9 +171,9 @@
         TLV::TLVType container;
         ReturnErrorOnFailure(reader.EnterContainer(container));
 
-        ReturnErrorOnFailure(Serializer::DeserializeId(reader, this->mEntry->mStorageId));
+        ReturnErrorOnFailure(Serializer::DeserializeId(reader, *storage_id));
 
-        ReturnErrorOnFailure(Serializer::DeserializeData(reader, this->mEntry->mStorageData));
+        ReturnErrorOnFailure(Serializer::DeserializeData(reader, *storage_data));
 
         return reader.ExitContainer(container);
     }
@@ -187,7 +190,6 @@
     using Serializer              = DefaultSerializer<StorageId, StorageData>;
     using TypedTableEntryData     = TableEntryData<StorageId, StorageData>;
     using Store                   = PersistentStore<kEntryMaxBytes>;
-    using TableEntry              = typename TypedTableEntryData::TableEntry;
     using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
 
     EndpointId endpoint_id;
@@ -257,7 +259,7 @@
     /// deleted so it can adjust the fabric and global entry count accordingly. Even if Deserialize fails, this value will return
     /// the number of entries deleted before the failure happened.
     /// @return CHIP_NO_ERROR on success, specific CHIP_ERROR otherwise
-    CHIP_ERROR Deserialize(TLV::TLVReader & reader, PersistentStorageDelegate * storage, uint8_t & deleted_entries_count)
+    CHIP_ERROR Deserialize(TLV::TLVReader & reader, PersistentStorageDelegate & storage, uint8_t & deleted_entries_count)
     {
         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
         TLV::TLVType fabricEntryContainer;
@@ -273,7 +275,6 @@
         CHIP_ERROR err;
         deleted_entries_count = 0;
 
-        TableEntry unused;
         Store persistentStore;
         while ((err = reader.Next(TLV::AnonymousTag())) == CHIP_NO_ERROR)
         {
@@ -286,11 +287,11 @@
             }
             else
             {
-                TypedTableEntryData entry(endpoint_id, fabric_index, unused, i);
+                StorageId unused;
                 ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
-                ReturnErrorOnFailure(Serializer::DeserializeId(reader, entry_map[i]));
+                ReturnErrorOnFailure(Serializer::DeserializeId(reader, unused));
                 ReturnErrorOnFailure(reader.ExitContainer(entryIdContainer));
-                ReturnErrorOnFailure(persistentStore.Delete(entry, storage));
+                ReturnErrorOnFailure(DeleteValue(storage, i));
                 deleted_entries_count++;
             }
 
@@ -302,13 +303,28 @@
         return reader.ExitContainer(fabricEntryContainer);
     }
 
+    /// @brief  Finds the id of the entry with the specified index
+    /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found
+    CHIP_ERROR FindByIndex(PersistentStorageDelegate & storage, EntryIndex index, StorageId & entry_id)
+    {
+        VerifyOrReturnError(entry_map[index].IsValid(), CHIP_ERROR_NOT_FOUND);
+        VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
+        VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
+        if (!storage.SyncDoesKeyExist(Serializer::FabricEntryKey(fabric_index, endpoint_id, index).KeyName()))
+        {
+            return CHIP_ERROR_NOT_FOUND;
+        }
+        entry_id = entry_map[index];
+        return CHIP_NO_ERROR;
+    }
+
     /// @brief  Finds the index where the current entry should be inserted by going through the endpoint's table and checking
     /// whether the entry is already there. If the target is not in the table, sets idx to the first empty space
     /// @param target_entry StorageId of entry to find
     /// @param idx Index where target or space is found
     /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found and space left
     ///         CHIP_ERROR_NO_MEMORY if target was not found and table is full
-    CHIP_ERROR Find(StorageId target_entry, EntryIndex & idx)
+    CHIP_ERROR Find(const StorageId & target_entry, EntryIndex & idx)
     {
         EntryIndex firstFreeIdx = Data::kUndefinedEntryIndex; // storage index if entry not found
         uint16_t index          = 0;
@@ -336,54 +352,55 @@
         return CHIP_ERROR_NO_MEMORY;
     }
 
-    CHIP_ERROR SaveEntry(PersistentStorageDelegate * storage, const TableEntry & src_entry)
+    CHIP_ERROR SaveEntry(PersistentStorageDelegate & storage, const StorageId & id, const StorageData & data)
     {
         CHIP_ERROR err = CHIP_NO_ERROR;
         // Look for empty storage space
 
         EntryIndex index;
-        err = this->Find(src_entry.mStorageId, index);
+        err = this->Find(id, index);
 
         Store persistentStore;
         // C++ doesn't have const constructors; variable is declared const
-        const TypedTableEntryData entry(endpoint_id, fabric_index, const_cast<TableEntry &>(src_entry), index);
+        const TypedTableEntryData entry(endpoint_id, fabric_index, const_cast<StorageId &>(id), const_cast<StorageData &>(data),
+                                        index);
 
         if (CHIP_NO_ERROR == err)
         {
-            return persistentStore.Save(entry, storage);
+            return persistentStore.Save(entry, &storage);
         }
 
         if (CHIP_ERROR_NOT_FOUND == err) // If not found, entry.index should be the first free index
         {
             // Update the global entry count
             TypedEndpointEntryCount endpoint_count(endpoint_id);
-            ReturnErrorOnFailure(endpoint_count.Load(storage));
+            ReturnErrorOnFailure(endpoint_count.Load(&storage));
             VerifyOrReturnError(endpoint_count.count_value < max_per_endpoint, CHIP_ERROR_NO_MEMORY);
             endpoint_count.count_value++;
-            ReturnErrorOnFailure(endpoint_count.Save(storage));
+            ReturnErrorOnFailure(endpoint_count.Save(&storage));
 
             entry_count++;
-            entry_map[entry.index] = src_entry.mStorageId;
+            entry_map[entry.index] = id;
 
-            err = this->Save(storage);
+            err = this->Save(&storage);
             if (CHIP_NO_ERROR != err)
             {
                 endpoint_count.count_value--;
-                ReturnErrorOnFailure(endpoint_count.Save(storage));
+                ReturnErrorOnFailure(endpoint_count.Save(&storage));
                 return err;
             }
 
-            err = persistentStore.Save(entry, storage);
+            err = persistentStore.Save(entry, &storage);
 
             // on failure to save the entry, undoes the changes to Fabric Entry Data
             if (CHIP_NO_ERROR != err)
             {
                 endpoint_count.count_value--;
-                ReturnErrorOnFailure(endpoint_count.Save(storage));
+                ReturnErrorOnFailure(endpoint_count.Save(&storage));
 
                 entry_count--;
                 entry_map[entry.index].Clear();
-                ReturnErrorOnFailure(this->Save(storage));
+                ReturnErrorOnFailure(this->Save(&storage));
                 return err;
             }
         }
@@ -397,47 +414,46 @@
     /// @param storage Storage delegate to access the entry
     /// @param entry_id Entry to remove
     /// @return CHIP_NO_ERROR if successful, specific CHIP_ERROR otherwise
-    CHIP_ERROR RemoveEntry(PersistentStorageDelegate * storage, const StorageId & entry_id)
+    CHIP_ERROR RemoveEntry(PersistentStorageDelegate & storage, const StorageId & entry_id)
     {
         CHIP_ERROR err = CHIP_NO_ERROR;
-        TableEntry id_holder(entry_id);
-        TypedTableEntryData entry(endpoint_id, fabric_index, id_holder);
+        EntryIndex entryIndex;
 
         // Empty Entry Fabric Data returns CHIP_NO_ERROR on remove
         if (entry_count > 0)
         {
             // If Find doesn't return CHIP_NO_ERROR, the entry wasn't found, which doesn't return an error
-            VerifyOrReturnValue(this->Find(entry_id, entry.index) == CHIP_NO_ERROR, CHIP_NO_ERROR);
+            VerifyOrReturnValue(this->Find(entry_id, entryIndex) == CHIP_NO_ERROR, CHIP_NO_ERROR);
 
             // Update the global entry count
             TypedEndpointEntryCount endpoint_entry_count(endpoint_id);
-            ReturnErrorOnFailure(endpoint_entry_count.Load(storage));
+            ReturnErrorOnFailure(endpoint_entry_count.Load(&storage));
             endpoint_entry_count.count_value--;
-            ReturnErrorOnFailure(endpoint_entry_count.Save(storage));
+            ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
 
             entry_count--;
-            entry_map[entry.index].Clear();
-            err = this->Save(storage);
+            entry_map[entryIndex].Clear();
+            err = this->Save(&storage);
 
             // On failure to update the entry map, undo the global count modification
             if (CHIP_NO_ERROR != err)
             {
                 endpoint_entry_count.count_value++;
-                ReturnErrorOnFailure(endpoint_entry_count.Save(storage));
+                ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
                 return err;
             }
 
-            err = Store().Delete(entry, storage);
+            err = DeleteValue(storage, entryIndex);
 
             // On failure to delete entry, undo the change to the Fabric Entry Data and the global entry count
             if (CHIP_NO_ERROR != err)
             {
                 endpoint_entry_count.count_value++;
-                ReturnErrorOnFailure(endpoint_entry_count.Save(storage));
+                ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
 
                 entry_count++;
-                entry_map[entry.index] = id_holder.mStorageId;
-                ReturnErrorOnFailure(this->Save(storage));
+                entry_map[entryIndex] = entry_id;
+                ReturnErrorOnFailure(this->Save(&storage));
                 return err;
             }
         }
@@ -468,7 +484,7 @@
         TLV::TLVReader reader;
         reader.Init(buffer, size);
 
-        err = Deserialize(reader, storage, deleted_entries_count);
+        err = Deserialize(reader, *storage, deleted_entries_count);
 
         // If Deserialize sets the "deleted_entries" variable, the table in flash memory held too many entries (can happen
         // if max_per_fabric was reduced during an OTA) and was adjusted during deserializing . The fabric data must then
@@ -484,12 +500,18 @@
 
         return err;
     }
+
+private:
+    CHIP_ERROR DeleteValue(PersistentStorageDelegate & storage, EntryIndex index)
+    {
+        StorageKeyName key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
+        return storage.SyncDeleteKeyValue(key.KeyName());
+    }
 };
 
 template <class StorageId, class StorageData, size_t kIteratorsMax>
 CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::Init(PersistentStorageDelegate & storage)
 {
-    using Serializer = DefaultSerializer<StorageId, StorageData>;
     // Verify the initialized parameter respects the maximum allowed values for entry capacity
     VerifyOrReturnError(mMaxPerFabric <= Serializer::kMaxPerFabric() && mMaxPerEndpoint <= Serializer::kMaxPerEndpoint(),
                         CHIP_ERROR_INVALID_INTEGER_VALUE);
@@ -505,7 +527,6 @@
 CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::GetFabricEntryCount(FabricIndex fabric_index,
                                                                                        uint8_t & entry_count)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -549,7 +570,6 @@
 CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::GetRemainingCapacity(FabricIndex fabric_index,
                                                                                         uint8_t & capacity)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -585,9 +605,9 @@
 }
 
 template <class StorageId, class StorageData, size_t kIteratorsMax>
-CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::SetTableEntry(FabricIndex fabric_index, const TableEntry & entry)
+CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::SetTableEntry(FabricIndex fabric_index, const StorageId & id,
+                                                                                 const StorageData & data)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -599,26 +619,27 @@
     CHIP_ERROR err = fabric.Load(mStorage);
     VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
 
-    err = fabric.SaveEntry(mStorage, entry);
+    err = fabric.SaveEntry(*mStorage, id, data);
     return err;
 }
 
 template <class StorageId, class StorageData, size_t kIteratorsMax>
-CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::GetTableEntry(FabricIndex fabric_index, StorageId entry_id,
-                                                                                 TableEntry & entry)
+template <size_t kEntryMaxBytes>
+CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::GetTableEntry(FabricIndex fabric_index, StorageId & entry_id,
+                                                                                 StorageData & data,
+                                                                                 PersistentStore<kEntryMaxBytes> & buffer)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
 
     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
-    TableEntryData<StorageId, StorageData> table_entry(mEndpointId, fabric_index, entry);
+    TableEntryData<StorageId, StorageData> table_entry(mEndpointId, fabric_index, entry_id, data);
 
     ReturnErrorOnFailure(fabric.Load(mStorage));
     VerifyOrReturnError(fabric.Find(entry_id, table_entry.index) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
 
-    CHIP_ERROR err = PersistentStore<Serializer::kEntryMaxBytes()>().Load(table_entry, mStorage);
+    CHIP_ERROR err = buffer.Load(table_entry, mStorage);
 
     // If entry.Load returns "buffer too small", the entry in memory is too big to be retrieved (this could happen if the
     // kEntryMaxBytes was reduced by OTA) and therefore must be deleted as is is no longer considered accessible.
@@ -632,9 +653,25 @@
 }
 
 template <class StorageId, class StorageData, size_t kIteratorsMax>
-CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::RemoveTableEntry(FabricIndex fabric_index, StorageId entry_id)
+CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::FindTableEntry(FabricIndex fabric_index,
+                                                                                  const StorageId & entry_id, EntryIndex & idx)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
+    using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
+                                                 Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
+    VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
+
+    TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
+
+    ReturnErrorOnFailure(fabric.Load(mStorage));
+    VerifyOrReturnError(fabric.Find(entry_id, idx) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
+
+    return CHIP_NO_ERROR;
+}
+
+template <class StorageId, class StorageData, size_t kIteratorsMax>
+CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::RemoveTableEntry(FabricIndex fabric_index,
+                                                                                    const StorageId & entry_id)
+{
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -643,7 +680,7 @@
 
     ReturnErrorOnFailure(fabric.Load(mStorage));
 
-    return fabric.RemoveEntry(mStorage, entry_id);
+    return fabric.RemoveEntry(*mStorage, entry_id);
 }
 
 /// @brief This function is meant to provide a way to empty the entry table without knowing any specific entry Id. Outside of this
@@ -656,29 +693,25 @@
                                                                                               FabricIndex fabric_index,
                                                                                               EntryIndex entry_idx)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
 
-    CHIP_ERROR err = CHIP_NO_ERROR;
     TypedFabricEntryData fabric(endpoint, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
-    TableEntry loadedEntry;
-    TableEntryData<StorageId, StorageData> entry(endpoint, fabric_index, loadedEntry, entry_idx);
 
     ReturnErrorOnFailure(fabric.Load(mStorage));
-    err = PersistentStore<Serializer::kEntryMaxBytes()>().Load(entry, mStorage);
+    StorageId entryId;
+    CHIP_ERROR err = fabric.FindByIndex(*mStorage, entry_idx, entryId);
     VerifyOrReturnValue(CHIP_ERROR_NOT_FOUND != err, CHIP_NO_ERROR);
     ReturnErrorOnFailure(err);
 
-    return fabric.RemoveEntry(mStorage, loadedEntry.mStorageId);
+    return fabric.RemoveEntry(*mStorage, entryId);
 }
 
 template <class StorageId, class StorageData, size_t kIteratorsMax>
 CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::RemoveFabric(FabricIndex fabric_index)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -717,7 +750,6 @@
 template <class StorageId, class StorageData, size_t kIteratorsMax>
 CHIP_ERROR FabricTableImpl<StorageId, StorageData, kIteratorsMax>::RemoveEndpoint()
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -757,8 +789,6 @@
 template <class StorageId, class StorageData, size_t kIteratorsMax>
 void FabricTableImpl<StorageId, StorageData, kIteratorsMax>::SetTableSize(uint16_t endpointTableSize, uint16_t maxPerFabric)
 {
-    using Serializer = DefaultSerializer<StorageId, StorageData>;
-
     // Verify the endpoint passed size respects the limits of the device configuration
     VerifyOrDie(Serializer::kMaxPerFabric() > 0);
     VerifyOrDie(Serializer::kMaxPerEndpoint() > 0);
@@ -772,7 +802,6 @@
     mProvider(provider),
     mFabric(fabricIdx), mEndpoint(endpoint), mMaxPerFabric(maxPerFabric), mMaxPerEndpoint(maxPerEndpoint)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -791,7 +820,6 @@
 template <class StorageId, class StorageData, size_t kIteratorsMax>
 bool FabricTableImpl<StorageId, StorageData, kIteratorsMax>::EntryIteratorImpl::Next(TableEntry & output)
 {
-    using Serializer           = DefaultSerializer<StorageId, StorageData>;
     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
 
@@ -805,7 +833,7 @@
     {
         if (fabric.entry_map[mEntryIndex].IsValid())
         {
-            TableEntryData<StorageId, StorageData> entry(mEndpoint, mFabric, output, mEntryIndex);
+            TableEntryData<StorageId, StorageData> entry(mEndpoint, mFabric, output.mStorageId, output.mStorageData, mEntryIndex);
             VerifyOrReturnError(persistentStore.Load(entry, mProvider.mStorage) == CHIP_NO_ERROR, false);
             mEntryIndex++;