Matter SDK Coverage Report
Current view: top level - app/storage - FabricTableImpl.ipp (source / functions) Coverage Total Hit
Test: SHA:7c9b1260e3daa86aae0d41b894469b295eee70e8 Lines: 82.5 % 359 296
Test Date: 2025-09-07 07:12:04 Functions: 30.7 % 140 43

            Line data    Source code
       1              : /**
       2              :  *
       3              :  *    Copyright (c) 2025 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #pragma once
      19              : 
      20              : #include <app/storage/FabricTableImpl.h>
      21              : #include <app/util/endpoint-config-api.h>
      22              : #include <lib/support/DefaultStorageKeyAllocator.h>
      23              : #include <lib/support/TypeTraits.h>
      24              : #include <stdlib.h>
      25              : 
      26              : namespace chip {
      27              : namespace app {
      28              : namespace Storage {
      29              : 
      30              : using EntryIndex = Data::EntryIndex;
      31              : 
      32              : /// @brief Tags Used to serialize entries so they can be stored in flash memory.
      33              : /// kEndpointEntryCount: Number of entries in an endpoint
      34              : /// kEntryCount: Number of entries in a Fabric
      35              : /// kStorageIdArray: Array of StorageId struct
      36              : enum class TagEntry : uint8_t
      37              : {
      38              :     kEndpointEntryCount = 1,
      39              :     kEntryCount,
      40              :     kStorageIdArray,
      41              :     kFabricTableFirstSpecializationReservedTag,
      42              :     kFabricTableLastSpecializationReservedTag = 127,
      43              :     // Add new entries here; kFabricTableFirstSpecializationReservedTag through
      44              :     // kFabricTableLastSpecializationReservedTag are reserved for specializations
      45              : };
      46              : 
      47              : // Currently takes 5 Bytes to serialize Container and value in a TLV: 1 byte start struct, 2 bytes control + tag for the value, 1
      48              : // byte value, 1 byte end struct. 8 Bytes leaves space for potential increase in count_value size.
      49              : static constexpr size_t kPersistentBufferEntryCountBytes = 8;
      50              : 
      51              : struct BaseEntryCount : public PersistentData<kPersistentBufferEntryCountBytes>
      52              : {
      53              :     uint8_t count_value = 0;
      54          264 :     BaseEntryCount(uint8_t count = 0) : count_value(count) {}
      55              : 
      56          178 :     CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
      57              :     {
      58              :         TLV::TLVType container;
      59          178 :         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
      60          178 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEndpointEntryCount), count_value));
      61          178 :         return writer.EndContainer(container);
      62              :     }
      63              : 
      64          252 :     CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
      65              :     {
      66          252 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
      67              : 
      68              :         TLV::TLVType container;
      69          252 :         ReturnErrorOnFailure(reader.EnterContainer(container));
      70          252 :         ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEndpointEntryCount)));
      71          252 :         ReturnErrorOnFailure(reader.Get(count_value));
      72          252 :         return reader.ExitContainer(container);
      73              :     }
      74              : 
      75          264 :     CHIP_ERROR Load(PersistentStorageDelegate * storage) override
      76              :     {
      77          264 :         CHIP_ERROR err = PersistentData::Load(storage);
      78          264 :         VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
      79          264 :         if (CHIP_ERROR_NOT_FOUND == err)
      80              :         {
      81           12 :             count_value = 0;
      82              :         }
      83              : 
      84          264 :         return CHIP_NO_ERROR;
      85              :     }
      86              : };
      87              : 
      88              : template <class StorageId, class StorageData>
      89              : struct EndpointEntryCount : public BaseEntryCount
      90              : {
      91              :     using Serializer = DefaultSerializer<StorageId, StorageData>;
      92              : 
      93              :     EndpointId endpoint_id = kInvalidEndpointId;
      94              : 
      95          264 :     EndpointEntryCount(EndpointId endpoint, uint8_t count = 0) : BaseEntryCount(count), endpoint_id(endpoint) {}
      96          264 :     ~EndpointEntryCount() {}
      97              : 
      98          264 :     void Clear() override { count_value = 0; }
      99              : 
     100          442 :     CHIP_ERROR UpdateKey(StorageKeyName & key) const override
     101              :     {
     102          442 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     103          442 :         key = Serializer::EndpointEntryCountKey(endpoint_id);
     104          442 :         return CHIP_NO_ERROR;
     105              :     }
     106              : };
     107              : 
     108              : // Prevent mutations from happening in TableEntryData::Serialize
     109              : // If we just used a raw reference for TableEntryData::mEntry, C++ allows us
     110              : // to mutate mEntry.mStorageId & mEntry.mStorageData in TableEntryData::Serialize
     111              : // without having to do a const_cast; as an example, if we were to accidentally introduce
     112              : // the following code in TableEntryData::Serialize (a const method):
     113              : //
     114              : // this->mEntry->mStorageData = StorageData();
     115              : //
     116              : // If TableEntryData::mEntry is a reference, it allows this with no compilation error;
     117              : // But with ConstCorrectRef, we get a compile-time error that TableEntryData::mEntry->mStorageData
     118              : // cannot be modified because it is a const value
     119              : template <typename T>
     120              : class ConstCorrectRef
     121              : {
     122              :     T & mRef;
     123              : 
     124              : public:
     125          474 :     inline ConstCorrectRef(T & ref) : mRef(ref) {}
     126              : 
     127              :     inline const T * operator->() const { return &mRef; }
     128              :     inline T * operator->() { return &mRef; }
     129              : 
     130          200 :     inline const T & operator*() const { return mRef; }
     131          213 :     inline T & operator*() { return mRef; }
     132              : };
     133              : 
     134              : template <class StorageId, class StorageData>
     135              : struct TableEntryData : DataAccessor
     136              : {
     137              :     using Serializer = DefaultSerializer<StorageId, StorageData>;
     138              : 
     139              :     EndpointId endpoint_id   = kInvalidEndpointId;
     140              :     FabricIndex fabric_index = kUndefinedFabricIndex;
     141              :     EntryIndex index         = 0;
     142              :     bool first               = true;
     143              :     ConstCorrectRef<StorageId> storage_id;
     144              :     ConstCorrectRef<StorageData> storage_data;
     145              : 
     146          237 :     TableEntryData(EndpointId endpoint, FabricIndex fabric, StorageId & id, StorageData & data, EntryIndex idx = 0) :
     147          237 :         endpoint_id(endpoint), fabric_index(fabric), index(idx), storage_id(id), storage_data(data)
     148          237 :     {}
     149              : 
     150          171 :     CHIP_ERROR UpdateKey(StorageKeyName & key) const override
     151              :     {
     152          171 :         VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
     153          171 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     154          171 :         key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
     155          171 :         return CHIP_NO_ERROR;
     156              :     }
     157              : 
     158           71 :     void Clear() override { Serializer::Clear(*storage_data); }
     159              : 
     160          100 :     CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
     161              :     {
     162              :         TLV::TLVType container;
     163          100 :         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
     164              : 
     165          100 :         ReturnErrorOnFailure(Serializer::SerializeId(writer, *storage_id));
     166              : 
     167          100 :         ReturnErrorOnFailure(Serializer::SerializeData(writer, *storage_data));
     168              : 
     169          100 :         return writer.EndContainer(container);
     170              :     }
     171              : 
     172           71 :     CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
     173              :     {
     174           71 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
     175              : 
     176              :         TLV::TLVType container;
     177           71 :         ReturnErrorOnFailure(reader.EnterContainer(container));
     178              : 
     179           71 :         ReturnErrorOnFailure(Serializer::DeserializeId(reader, *storage_id));
     180              : 
     181           71 :         ReturnErrorOnFailure(Serializer::DeserializeData(reader, *storage_data));
     182              : 
     183           71 :         return reader.ExitContainer(container);
     184              :     }
     185              : };
     186              : 
     187              : /**
     188              :  * @brief Class that holds a map to all entries in a fabric for a specific endpoint
     189              :  *
     190              :  * FabricEntryData is an access to a linked list of entries
     191              :  */
     192              : template <class StorageId, class StorageData, size_t kEntryMaxBytes, size_t kFabricMaxBytes, uint16_t kMaxPerFabric>
     193              : struct FabricEntryData : public PersistentData<kFabricMaxBytes>
     194              : {
     195              :     using Serializer              = DefaultSerializer<StorageId, StorageData>;
     196              :     using TypedTableEntryData     = TableEntryData<StorageId, StorageData>;
     197              :     using Store                   = PersistentStore<kEntryMaxBytes>;
     198              :     using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
     199              : 
     200              :     EndpointId endpoint_id;
     201              :     FabricIndex fabric_index;
     202              :     uint8_t entry_count = 0;
     203              :     uint16_t max_per_fabric;
     204              :     uint16_t max_per_endpoint;
     205              :     StorageId entry_map[kMaxPerFabric];
     206              : 
     207          744 :     FabricEntryData(EndpointId endpoint = kInvalidEndpointId, FabricIndex fabric = kUndefinedFabricIndex,
     208              :                     uint16_t maxPerFabric = kMaxPerFabric, uint16_t maxPerEndpoint = Serializer::kMaxPerEndpoint()) :
     209          744 :         endpoint_id(endpoint),
     210         8184 :         fabric_index(fabric), max_per_fabric(maxPerFabric), max_per_endpoint(maxPerEndpoint)
     211          744 :     {}
     212              : 
     213          932 :     CHIP_ERROR UpdateKey(StorageKeyName & key) const override
     214              :     {
     215          932 :         VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
     216          932 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     217          932 :         key = Serializer::FabricEntryDataKey(fabric_index, endpoint_id);
     218          932 :         return CHIP_NO_ERROR;
     219              :     }
     220              : 
     221          744 :     void Clear() override
     222              :     {
     223          744 :         entry_count = 0;
     224         7602 :         for (uint16_t i = 0; i < max_per_fabric; i++)
     225              :         {
     226         6858 :             entry_map[i].Clear();
     227              :         }
     228          744 :     }
     229              : 
     230          178 :     CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
     231              :     {
     232              :         TLV::TLVType fabricEntryContainer;
     233          178 :         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, fabricEntryContainer));
     234          178 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEntryCount), entry_count));
     235              : 
     236              :         // Storing the entry map
     237              :         TLV::TLVType entryMapContainer;
     238          178 :         ReturnErrorOnFailure(
     239              :             writer.StartContainer(TLV::ContextTag(TagEntry::kStorageIdArray), TLV::kTLVType_Array, entryMapContainer));
     240         1450 :         for (uint16_t i = 0; i < max_per_fabric; i++)
     241              :         {
     242              :             TLV::TLVType entryIdContainer;
     243         1272 :             ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, entryIdContainer));
     244         1272 :             ReturnErrorOnFailure(Serializer::SerializeId(writer, entry_map[i]));
     245         1272 :             ReturnErrorOnFailure(writer.EndContainer(entryIdContainer));
     246              :         }
     247          178 :         ReturnErrorOnFailure(writer.EndContainer(entryMapContainer));
     248              : 
     249          178 :         return writer.EndContainer(fabricEntryContainer);
     250              :     }
     251              : 
     252              :     /// @brief This Deserialize method is implemented only to allow compilation. It is not used throughout the code.
     253              :     /// @param reader TLV reader
     254              :     /// @return CHIP_NO_ERROR
     255            0 :     CHIP_ERROR Deserialize(TLV::TLVReader & reader) override { return CHIP_ERROR_INCORRECT_STATE; }
     256              : 
     257              :     /// @brief This Deserialize method checks that the recovered entries from the deserialization fit in the current max and if
     258              :     /// there are too many entries in nvm, it deletes them. The method sets the deleted_entries output parameter to true if entries
     259              :     /// were deleted so that the load function can know it needs to save the Fabric entry data to update the entry_count and the
     260              :     /// entry map in stored memory.
     261              :     /// @param reade [in] TLV reader, must be big enough to hold the entry size
     262              :     /// @param storage [in] Persistent Storage Delegate, required to delete entries if the number of entries in storage is greater
     263              :     /// than the maximum allowed
     264              :     /// @param deleted_entries_count [out] uint8_t letting the caller (in this case the load method) know how many entries were
     265              :     /// deleted so it can adjust the fabric and global entry count accordingly. Even if Deserialize fails, this value will return
     266              :     /// the number of entries deleted before the failure happened.
     267              :     /// @return CHIP_NO_ERROR on success, specific CHIP_ERROR otherwise
     268          330 :     CHIP_ERROR Deserialize(TLV::TLVReader & reader, PersistentStorageDelegate & storage, uint8_t & deleted_entries_count)
     269              :     {
     270          330 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
     271              :         TLV::TLVType fabricEntryContainer;
     272          330 :         ReturnErrorOnFailure(reader.EnterContainer(fabricEntryContainer));
     273          330 :         ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEntryCount)));
     274          330 :         ReturnErrorOnFailure(reader.Get(entry_count));
     275          330 :         entry_count = std::min(entry_count, static_cast<uint8_t>(max_per_fabric));
     276          330 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::ContextTag(TagEntry::kStorageIdArray)));
     277              :         TLV::TLVType entryMapContainer;
     278          330 :         ReturnErrorOnFailure(reader.EnterContainer(entryMapContainer));
     279              : 
     280          330 :         uint16_t i = 0;
     281              :         CHIP_ERROR err;
     282          330 :         deleted_entries_count = 0;
     283              : 
     284         2665 :         while ((err = reader.Next(TLV::AnonymousTag())) == CHIP_NO_ERROR)
     285              :         {
     286              :             TLV::TLVType entryIdContainer;
     287         2335 :             if (i < max_per_fabric)
     288              :             {
     289         2325 :                 ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
     290         2325 :                 ReturnErrorOnFailure(Serializer::DeserializeId(reader, entry_map[i]));
     291         2325 :                 ReturnErrorOnFailure(reader.ExitContainer(entryIdContainer));
     292              :             }
     293              :             else
     294              :             {
     295           10 :                 StorageId unused;
     296           10 :                 ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
     297           10 :                 ReturnErrorOnFailure(Serializer::DeserializeId(reader, unused));
     298           10 :                 ReturnErrorOnFailure(reader.ExitContainer(entryIdContainer));
     299           10 :                 ReturnErrorOnFailure(DeleteValue(storage, i));
     300           10 :                 deleted_entries_count++;
     301              :             }
     302              : 
     303         2335 :             i++;
     304              :         }
     305              : 
     306          330 :         VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
     307          330 :         ReturnErrorOnFailure(reader.ExitContainer(entryMapContainer));
     308          330 :         return reader.ExitContainer(fabricEntryContainer);
     309              :     }
     310              : 
     311              :     /// @brief  Finds the id of the entry with the specified index
     312              :     /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found
     313           72 :     CHIP_ERROR FindByIndex(PersistentStorageDelegate & storage, EntryIndex index, StorageId & entry_id)
     314              :     {
     315           72 :         VerifyOrReturnError(entry_map[index].IsValid(), CHIP_ERROR_NOT_FOUND);
     316           59 :         VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
     317           59 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     318           59 :         if (!storage.SyncDoesKeyExist(Serializer::FabricEntryKey(fabric_index, endpoint_id, index).KeyName()))
     319              :         {
     320            0 :             return CHIP_ERROR_NOT_FOUND;
     321              :         }
     322           59 :         entry_id = entry_map[index];
     323           59 :         return CHIP_NO_ERROR;
     324              :     }
     325              : 
     326              :     /// @brief  Finds the index where the current entry should be inserted by going through the endpoint's table and checking
     327              :     /// whether the entry is already there. If the target is not in the table, sets idx to the first empty space
     328              :     /// @param target_entry StorageId of entry to find
     329              :     /// @param idx Index where target or space is found
     330              :     /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found and space left
     331              :     ///         CHIP_ERROR_NO_MEMORY if target was not found and table is full
     332          258 :     CHIP_ERROR Find(const StorageId & target_entry, EntryIndex & idx)
     333              :     {
     334          258 :         EntryIndex firstFreeIdx = Data::kUndefinedEntryIndex; // storage index if entry not found
     335          258 :         uint16_t index          = 0;
     336              : 
     337         1420 :         while (index < max_per_fabric)
     338              :         {
     339         1315 :             if (entry_map[index] == target_entry)
     340              :             {
     341          153 :                 idx = index;
     342          153 :                 return CHIP_NO_ERROR; // return entry at current index if entry found
     343              :             }
     344         1162 :             if (!entry_map[index].IsValid() && firstFreeIdx == Data::kUndefinedEntryIndex)
     345              :             {
     346          164 :                 firstFreeIdx = index;
     347              :             }
     348         1162 :             index++;
     349              :         }
     350              : 
     351          105 :         if (firstFreeIdx < max_per_fabric)
     352              :         {
     353          102 :             idx = firstFreeIdx;
     354          102 :             return CHIP_ERROR_NOT_FOUND;
     355              :         }
     356              : 
     357            3 :         return CHIP_ERROR_NO_MEMORY;
     358              :     }
     359              : 
     360          103 :     CHIP_ERROR SaveEntry(PersistentStorageDelegate & storage, const StorageId & id, const StorageData & data,
     361              :                          Store & persistentStore)
     362              :     {
     363          103 :         CHIP_ERROR err = CHIP_NO_ERROR;
     364              :         // Look for empty storage space
     365              : 
     366              :         EntryIndex index;
     367          103 :         err = this->Find(id, index);
     368              : 
     369              :         // C++ doesn't have const constructors; variable is declared const
     370          103 :         const TypedTableEntryData entry(endpoint_id, fabric_index, const_cast<StorageId &>(id), const_cast<StorageData &>(data),
     371              :                                         index);
     372              : 
     373          103 :         if (CHIP_NO_ERROR == err)
     374              :         {
     375            3 :             return persistentStore.Save(entry, &storage);
     376              :         }
     377              : 
     378          100 :         if (CHIP_ERROR_NOT_FOUND == err) // If not found, entry.index should be the first free index
     379              :         {
     380              :             // Update the global entry count
     381           99 :             TypedEndpointEntryCount endpoint_count(endpoint_id);
     382           99 :             ReturnErrorOnFailure(endpoint_count.Load(&storage));
     383           99 :             VerifyOrReturnError(endpoint_count.count_value < max_per_endpoint, CHIP_ERROR_NO_MEMORY);
     384           97 :             endpoint_count.count_value++;
     385           97 :             ReturnErrorOnFailure(endpoint_count.Save(&storage));
     386              : 
     387           97 :             entry_count++;
     388           97 :             entry_map[entry.index] = id;
     389              : 
     390           97 :             err = this->Save(&storage);
     391           97 :             if (CHIP_NO_ERROR != err)
     392              :             {
     393            0 :                 endpoint_count.count_value--;
     394            0 :                 ReturnErrorOnFailure(endpoint_count.Save(&storage));
     395            0 :                 return err;
     396              :             }
     397              : 
     398           97 :             err = persistentStore.Save(entry, &storage);
     399              : 
     400              :             // on failure to save the entry, undoes the changes to Fabric Entry Data
     401           97 :             if (CHIP_NO_ERROR != err)
     402              :             {
     403            0 :                 endpoint_count.count_value--;
     404            0 :                 ReturnErrorOnFailure(endpoint_count.Save(&storage));
     405              : 
     406            0 :                 entry_count--;
     407            0 :                 entry_map[entry.index].Clear();
     408            0 :                 ReturnErrorOnFailure(this->Save(&storage));
     409            0 :                 return err;
     410              :             }
     411           99 :         }
     412              : 
     413           98 :         return err;
     414          103 :     }
     415              : 
     416              :     /// @brief Removes an entry from the non-volatile memory and clears its index in the entry map. Decreases the number of entries
     417              :     /// in the global entry count and in the entry fabric data if successful. As the entry map size is not compressed upon removal,
     418              :     /// this only clears the entry corresponding to the entry from the entry map.
     419              :     /// @param storage Storage delegate to access the entry
     420              :     /// @param entry_id Entry to remove
     421              :     /// @return CHIP_NO_ERROR if successful, specific CHIP_ERROR otherwise
     422           80 :     CHIP_ERROR RemoveEntry(PersistentStorageDelegate & storage, const StorageId & entry_id)
     423              :     {
     424           80 :         CHIP_ERROR err = CHIP_NO_ERROR;
     425              :         EntryIndex entryIndex;
     426              : 
     427              :         // Empty Entry Fabric Data returns CHIP_NO_ERROR on remove
     428           80 :         if (entry_count > 0)
     429              :         {
     430              :             // If Find doesn't return CHIP_NO_ERROR, the entry wasn't found, which doesn't return an error
     431           80 :             VerifyOrReturnValue(this->Find(entry_id, entryIndex) == CHIP_NO_ERROR, CHIP_NO_ERROR);
     432              : 
     433              :             // Update the global entry count
     434           79 :             TypedEndpointEntryCount endpoint_entry_count(endpoint_id);
     435           79 :             ReturnErrorOnFailure(endpoint_entry_count.Load(&storage));
     436           79 :             endpoint_entry_count.count_value--;
     437           79 :             ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
     438              : 
     439           79 :             entry_count--;
     440           79 :             entry_map[entryIndex].Clear();
     441           79 :             err = this->Save(&storage);
     442              : 
     443              :             // On failure to update the entry map, undo the global count modification
     444           79 :             if (CHIP_NO_ERROR != err)
     445              :             {
     446            0 :                 endpoint_entry_count.count_value++;
     447            0 :                 ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
     448            0 :                 return err;
     449              :             }
     450              : 
     451           79 :             err = DeleteValue(storage, entryIndex);
     452              : 
     453              :             // On failure to delete entry, undo the change to the Fabric Entry Data and the global entry count
     454           79 :             if (CHIP_NO_ERROR != err)
     455              :             {
     456            0 :                 endpoint_entry_count.count_value++;
     457            0 :                 ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
     458              : 
     459            0 :                 entry_count++;
     460            0 :                 entry_map[entryIndex] = entry_id;
     461            0 :                 ReturnErrorOnFailure(this->Save(&storage));
     462            0 :                 return err;
     463              :             }
     464           79 :         }
     465           79 :         return err;
     466              :     }
     467              : 
     468          744 :     CHIP_ERROR Load(PersistentStorageDelegate * storage) override
     469              :     {
     470          744 :         VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
     471          744 :         uint8_t deleted_entries_count = 0;
     472              : 
     473          744 :         uint8_t buffer[kFabricMaxBytes] = { 0 };
     474          744 :         StorageKeyName key              = StorageKeyName::Uninitialized();
     475              : 
     476              :         // Set data to defaults
     477          744 :         Clear();
     478              : 
     479              :         // Update storage key
     480          744 :         ReturnErrorOnFailure(UpdateKey(key));
     481              : 
     482              :         // Load the serialized data
     483          744 :         uint16_t size  = static_cast<uint16_t>(sizeof(buffer));
     484          744 :         CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), buffer, size);
     485          744 :         VerifyOrReturnError(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND != err, CHIP_ERROR_NOT_FOUND);
     486          330 :         ReturnErrorOnFailure(err);
     487              : 
     488              :         // Decode serialized data
     489          330 :         TLV::TLVReader reader;
     490          330 :         reader.Init(buffer, size);
     491              : 
     492          330 :         err = Deserialize(reader, *storage, deleted_entries_count);
     493              : 
     494              :         // If Deserialize sets the "deleted_entries" variable, the table in flash memory held too many entries (can happen
     495              :         // if max_per_fabric was reduced during an OTA) and was adjusted during deserializing . The fabric data must then
     496              :         // be updated
     497          330 :         if (deleted_entries_count)
     498              :         {
     499            2 :             TypedEndpointEntryCount global_count(endpoint_id);
     500            2 :             ReturnErrorOnFailure(global_count.Load(storage));
     501            2 :             global_count.count_value = static_cast<uint8_t>(global_count.count_value - deleted_entries_count);
     502            2 :             ReturnErrorOnFailure(global_count.Save(storage));
     503            2 :             ReturnErrorOnFailure(this->Save(storage));
     504            2 :         }
     505              : 
     506          330 :         return err;
     507          744 :     }
     508              : 
     509              : private:
     510           89 :     CHIP_ERROR DeleteValue(PersistentStorageDelegate & storage, EntryIndex index)
     511              :     {
     512           89 :         StorageKeyName key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
     513           89 :         return storage.SyncDeleteKeyValue(key.KeyName());
     514           89 :     }
     515              : };
     516              : 
     517              : template <class StorageId, class StorageData>
     518            5 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::Init(PersistentStorageDelegate & storage)
     519              : {
     520              :     // Verify the initialized parameter respects the maximum allowed values for entry capacity
     521            5 :     VerifyOrReturnError(mMaxPerFabric <= Serializer::kMaxPerFabric() && mMaxPerEndpoint <= Serializer::kMaxPerEndpoint(),
     522              :                         CHIP_ERROR_INVALID_INTEGER_VALUE);
     523            3 :     this->mStorage = &storage;
     524            3 :     return CHIP_NO_ERROR;
     525              : }
     526              : 
     527              : template <class StorageId, class StorageData>
     528          128 : void FabricTableImpl<StorageId, StorageData>::Finish()
     529          128 : {}
     530              : 
     531              : template <class StorageId, class StorageData>
     532           33 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetFabricEntryCount(FabricIndex fabric_index, uint8_t & entry_count)
     533              : {
     534              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     535              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     536              : 
     537           33 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     538              : 
     539           33 :     TypedFabricEntryData fabric(mEndpointId, fabric_index);
     540           33 :     CHIP_ERROR err = fabric.Load(mStorage);
     541           33 :     VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     542              : 
     543           33 :     entry_count = (CHIP_ERROR_NOT_FOUND == err) ? 0 : fabric.entry_count;
     544              : 
     545           33 :     return CHIP_NO_ERROR;
     546           33 : }
     547              : 
     548              : template <class StorageId, class StorageData>
     549           84 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetEndpointEntryCount(uint8_t & entry_count)
     550              : {
     551              :     using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
     552              : 
     553           84 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     554              : 
     555           84 :     TypedEndpointEntryCount endpoint_entry_count(mEndpointId);
     556              : 
     557           84 :     ReturnErrorOnFailure(endpoint_entry_count.Load(mStorage));
     558           84 :     entry_count = endpoint_entry_count.count_value;
     559              : 
     560           84 :     return CHIP_NO_ERROR;
     561           84 : }
     562              : 
     563              : template <class StorageId, class StorageData>
     564            0 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetEndpointEntryCount(const uint8_t & entry_count)
     565              : {
     566              :     using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
     567            0 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     568              : 
     569            0 :     TypedEndpointEntryCount endpoint_entry_count(mEndpointId, entry_count);
     570            0 :     return endpoint_entry_count.Save(mStorage);
     571            0 : }
     572              : 
     573              : template <class StorageId, class StorageData>
     574           58 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity)
     575              : {
     576              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     577              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     578              : 
     579           58 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     580              : 
     581           58 :     uint8_t endpoint_entry_count = 0;
     582           58 :     ReturnErrorOnFailure(GetEndpointEntryCount(endpoint_entry_count));
     583              : 
     584              :     // If the global entry count is higher than the maximal Global entry capacity, this returns a capacity of 0 until enough entries
     585              :     // have been deleted to bring the global number of entries under the global maximum.
     586           58 :     if (endpoint_entry_count > mMaxPerEndpoint)
     587              :     {
     588            6 :         capacity = 0;
     589            6 :         return CHIP_NO_ERROR;
     590              :     }
     591           52 :     uint8_t remaining_capacity_global = static_cast<uint8_t>(mMaxPerEndpoint - endpoint_entry_count);
     592           52 :     uint8_t remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric);
     593              : 
     594           52 :     TypedFabricEntryData fabric(mEndpointId, fabric_index);
     595              : 
     596              :     // Load fabric data (defaults to zero)TypedFabricEntryData
     597           52 :     CHIP_ERROR err = fabric.Load(mStorage);
     598           52 :     VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     599              : 
     600           52 :     if (err == CHIP_NO_ERROR)
     601              :     {
     602           27 :         remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric - fabric.entry_count);
     603              :     }
     604              : 
     605           52 :     capacity = std::min(remaining_capacity_fabric, remaining_capacity_global);
     606              : 
     607           52 :     return CHIP_NO_ERROR;
     608           52 : }
     609              : 
     610              : template <class StorageId, class StorageData>
     611              : template <size_t kEntryMaxBytes>
     612          103 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetTableEntry(FabricIndex fabric_index, const StorageId & id,
     613              :                                                                   const StorageData & data,
     614              :                                                                   PersistentStore<kEntryMaxBytes> & writeBuffer)
     615              : {
     616              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     617              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     618              : 
     619          103 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     620              : 
     621          103 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     622              : 
     623              :     // Load fabric data (defaults to zero)
     624          103 :     CHIP_ERROR err = fabric.Load(mStorage);
     625          103 :     VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     626              : 
     627          103 :     err = fabric.SaveEntry(*mStorage, id, data, writeBuffer);
     628          103 :     return err;
     629          103 : }
     630              : 
     631              : template <class StorageId, class StorageData>
     632              : template <size_t kEntryMaxBytes>
     633          134 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetTableEntry(FabricIndex fabric_index, StorageId & entry_id,
     634              :                                                                   StorageData & data, PersistentStore<kEntryMaxBytes> & buffer)
     635              : {
     636              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     637              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     638          134 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     639              : 
     640          134 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     641          134 :     TableEntryData<StorageId, StorageData> table_entry(mEndpointId, fabric_index, entry_id, data);
     642              : 
     643          134 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     644           75 :     VerifyOrReturnError(fabric.Find(entry_id, table_entry.index) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
     645              : 
     646           71 :     CHIP_ERROR err = buffer.Load(table_entry, mStorage);
     647              : 
     648              :     // If entry.Load returns "buffer too small", the entry in memory is too big to be retrieved (this could happen if the
     649              :     // kEntryMaxBytes was reduced by OTA) and therefore must be deleted as is is no longer considered accessible.
     650           71 :     if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
     651              :     {
     652            0 :         ReturnErrorOnFailure(this->RemoveTableEntry(fabric_index, entry_id));
     653              :     }
     654           71 :     ReturnErrorOnFailure(err);
     655              : 
     656           71 :     return CHIP_NO_ERROR;
     657          134 : }
     658              : 
     659              : template <class StorageId, class StorageData>
     660            0 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::FindTableEntry(FabricIndex fabric_index, const StorageId & entry_id,
     661              :                                                                    EntryIndex & idx)
     662              : {
     663              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     664              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     665            0 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     666              : 
     667            0 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     668              : 
     669            0 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     670            0 :     VerifyOrReturnError(fabric.Find(entry_id, idx) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
     671              : 
     672            0 :     return CHIP_NO_ERROR;
     673            0 : }
     674              : 
     675              : template <class StorageId, class StorageData>
     676           15 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntry(FabricIndex fabric_index, const StorageId & entry_id)
     677              : {
     678              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     679              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     680              : 
     681           15 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     682           15 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     683              : 
     684           15 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     685              : 
     686           15 :     return fabric.RemoveEntry(*mStorage, entry_id);
     687           15 : }
     688              : 
     689              : /// @brief This function is meant to provide a way to empty the entry table without knowing any specific entry Id. Outside of this
     690              : /// specific use case, RemoveTableEntry should be used.
     691              : /// @param fabric_index Fabric in which the entry belongs
     692              : /// @param entry_idx Position in the Table
     693              : /// @return CHIP_NO_ERROR if removal was successful, errors if failed to remove the entry or to update the fabric after removing it
     694              : template <class StorageId, class StorageData>
     695           72 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index,
     696              :                                                                                EntryIndex entry_idx)
     697              : {
     698              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     699              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     700              : 
     701           72 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     702              : 
     703           72 :     TypedFabricEntryData fabric(endpoint, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     704              : 
     705           72 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     706           72 :     StorageId entryId;
     707           72 :     CHIP_ERROR err = fabric.FindByIndex(*mStorage, entry_idx, entryId);
     708           72 :     VerifyOrReturnValue(CHIP_ERROR_NOT_FOUND != err, CHIP_NO_ERROR);
     709           59 :     ReturnErrorOnFailure(err);
     710              : 
     711           59 :     return fabric.RemoveEntry(*mStorage, entryId);
     712           72 : }
     713              : 
     714              : template <class StorageId, class StorageData>
     715           17 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveFabric(FabricIndex fabric_index)
     716              : {
     717              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     718              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     719              : 
     720           17 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     721              : 
     722          153 :     for (uint16_t index = 0; index < emberAfEndpointCount(); index++)
     723              :     {
     724           68 :         if (!emberAfEndpointIndexIsEnabled(index))
     725              :         {
     726           60 :             continue;
     727              :         }
     728           68 :         EndpointId endpoint = emberAfEndpointFromIndex(index);
     729           68 :         TypedFabricEntryData fabric(endpoint, fabric_index);
     730           68 :         EntryIndex idx = 0;
     731           68 :         CHIP_ERROR err = fabric.Load(mStorage);
     732           68 :         VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     733           68 :         if (CHIP_ERROR_NOT_FOUND == err)
     734              :         {
     735           60 :             continue;
     736              :         }
     737              : 
     738           64 :         while (idx < mMaxPerFabric)
     739              :         {
     740           56 :             err = RemoveTableEntryAtPosition(endpoint, fabric_index, idx);
     741           56 :             VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     742           56 :             idx++;
     743              :         }
     744              : 
     745              :         // Remove fabric entries on endpoint
     746            8 :         ReturnErrorOnFailure(fabric.Delete(mStorage));
     747              :     }
     748              : 
     749           17 :     return CHIP_NO_ERROR;
     750              : }
     751              : 
     752              : template <class StorageId, class StorageData>
     753            1 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveEndpoint()
     754              : {
     755              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     756              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     757              : 
     758            1 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     759              : 
     760          507 :     for (FabricIndex fabric_index = kMinValidFabricIndex; fabric_index < kMaxValidFabricIndex; fabric_index++)
     761              :     {
     762          253 :         TypedFabricEntryData fabric(mEndpointId, fabric_index);
     763          253 :         CHIP_ERROR err = fabric.Load(mStorage);
     764          253 :         VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     765          253 :         if (CHIP_ERROR_NOT_FOUND == err)
     766              :         {
     767          251 :             continue;
     768              :         }
     769              : 
     770            2 :         EntryIndex idx = 0;
     771           16 :         while (idx < mMaxPerFabric)
     772              :         {
     773           14 :             err = RemoveTableEntryAtPosition(mEndpointId, fabric_index, idx);
     774           14 :             VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     775           14 :             idx++;
     776              :         };
     777              : 
     778              :         // Remove fabric entries on endpoint
     779            2 :         ReturnErrorOnFailure(fabric.Delete(mStorage));
     780              :     }
     781              : 
     782            1 :     return CHIP_NO_ERROR;
     783              : }
     784              : 
     785              : template <class StorageId, class StorageData>
     786           51 : void FabricTableImpl<StorageId, StorageData>::SetEndpoint(EndpointId endpoint)
     787              : {
     788           51 :     mEndpointId = endpoint;
     789           51 : }
     790              : 
     791              : template <class StorageId, class StorageData>
     792           49 : void FabricTableImpl<StorageId, StorageData>::SetTableSize(uint16_t endpointTableSize, uint16_t maxPerFabric)
     793              : {
     794              :     // Verify the endpoint passed size respects the limits of the device configuration
     795           49 :     VerifyOrDie(Serializer::kMaxPerFabric() > 0);
     796           49 :     VerifyOrDie(Serializer::kMaxPerEndpoint() > 0);
     797           49 :     mMaxPerEndpoint = std::min(Serializer::kMaxPerEndpoint(), endpointTableSize);
     798           49 :     mMaxPerFabric   = std::min(endpointTableSize, std::min(Serializer::kMaxPerFabric(), maxPerFabric));
     799           49 : }
     800              : 
     801              : template <class StorageId, class StorageData>
     802              : template <size_t kEntryMaxBytes, class UnaryFunc>
     803            0 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::IterateEntries(FabricIndex fabric, PersistentStore<kEntryMaxBytes> & store,
     804              :                                                                    UnaryFunc iterateFn)
     805              : {
     806            0 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     807              : 
     808            0 :     EntryIteratorImpl<kEntryMaxBytes> iterator(*this, fabric, mEndpointId, mMaxPerFabric, mMaxPerEndpoint, store);
     809            0 :     return iterateFn(iterator);
     810            0 : }
     811              : 
     812              : template <class StorageId, class StorageData>
     813              : template <size_t kEntryMaxBytes>
     814            0 : FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::EntryIteratorImpl(
     815              :     FabricTableImpl & provider, FabricIndex fabricIdx, EndpointId endpoint, uint16_t maxPerFabric, uint16_t maxPerEndpoint,
     816              :     PersistentStore<kEntryMaxBytes> & store) :
     817            0 :     mProvider(provider),
     818            0 :     mStore(store), mFabric(fabricIdx), mEndpoint(endpoint), mMaxPerFabric(maxPerFabric), mMaxPerEndpoint(maxPerEndpoint)
     819              : {
     820              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     821              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     822              : 
     823            0 :     TypedFabricEntryData fabric(mEndpoint, fabricIdx, mMaxPerFabric, mMaxPerEndpoint);
     824            0 :     ReturnOnFailure(fabric.Load(provider.mStorage));
     825            0 :     mTotalEntries = fabric.entry_count;
     826            0 :     mEntryIndex   = 0;
     827            0 : }
     828              : 
     829              : template <class StorageId, class StorageData>
     830              : template <size_t kEntryMaxBytes>
     831            0 : size_t FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Count()
     832              : {
     833            0 :     return mTotalEntries;
     834              : }
     835              : 
     836              : template <class StorageId, class StorageData>
     837              : template <size_t kEntryMaxBytes>
     838            0 : bool FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Next(TableEntry & output)
     839              : {
     840              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     841              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     842              : 
     843            0 :     TypedFabricEntryData fabric(mEndpoint, mFabric);
     844              : 
     845            0 :     VerifyOrReturnError(fabric.Load(mProvider.mStorage) == CHIP_NO_ERROR, false);
     846              : 
     847              :     // looks for next available entry
     848            0 :     while (mEntryIndex < mMaxPerFabric)
     849              :     {
     850            0 :         if (fabric.entry_map[mEntryIndex].IsValid())
     851              :         {
     852            0 :             TableEntryData<StorageId, StorageData> entry(mEndpoint, mFabric, output.mStorageId, output.mStorageData, mEntryIndex);
     853            0 :             VerifyOrReturnError(mStore.Load(entry, mProvider.mStorage) == CHIP_NO_ERROR, false);
     854            0 :             mEntryIndex++;
     855              : 
     856            0 :             return true;
     857            0 :         }
     858              : 
     859            0 :         mEntryIndex++;
     860              :     }
     861              : 
     862            0 :     return false;
     863            0 : }
     864              : 
     865              : template <class StorageId, class StorageData>
     866              : template <size_t kEntryMaxBytes>
     867            0 : void FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Release()
     868            0 : {}
     869              : } // namespace Storage
     870              : } // namespace app
     871              : } // namespace chip
        

Generated by: LCOV version 2.0-1