Matter SDK Coverage Report
Current view: top level - app/storage - FabricTableImpl.ipp (source / functions) Coverage Total Hit
Test: SHA:4d2388ac7eed75b2fe5e05e20de377999c632502 Lines: 89.9 % 328 295
Test Date: 2025-07-27 07:17:09 Functions: 91.3 % 46 42

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

Generated by: LCOV version 2.0-1