Matter SDK Coverage Report
Current view: top level - app/storage - FabricTableImpl.ipp (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 91.6 % 357 327
Test Date: 2026-01-31 08:14:20 Functions: 85.5 % 138 118

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

Generated by: LCOV version 2.0-1