Matter SDK Coverage Report
Current view: top level - app/storage - FabricTableImpl.ipp (source / functions) Coverage Total Hit
Test: SHA:6c8f029dd2432dc900f1c9245c324e69bd79e40a Lines: 91.7 % 360 330
Test Date: 2026-08-08 07:40:09 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          607 :     BaseEntryCount(uint8_t count = 0) : count_value(count) {}
      59              : 
      60          607 :     void Clear() override { count_value = 0; }
      61              : 
      62          290 :     CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
      63              :     {
      64              :         TLV::TLVType container;
      65          290 :         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
      66          290 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEndpointEntryCount), count_value));
      67          290 :         return writer.EndContainer(container);
      68              :     }
      69              : 
      70          542 :     CHIP_ERROR Deserialize(TLV::TLVReader & reader) override
      71              :     {
      72          542 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
      73              : 
      74              :         TLV::TLVType container;
      75          542 :         ReturnErrorOnFailure(reader.EnterContainer(container));
      76          542 :         ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEndpointEntryCount)));
      77          542 :         ReturnErrorOnFailure(reader.Get(count_value));
      78          542 :         return reader.ExitContainer(container);
      79              :     }
      80              : 
      81          607 :     CHIP_ERROR Load(PersistentStorageDelegate * storage) // NOLINT(bugprone-derived-method-shadowing-base-method)
      82              :     {
      83          607 :         CHIP_ERROR err = PersistableData::Load(storage);
      84         1214 :         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          607 :     EndpointEntryCount(EndpointId endpoint, uint8_t count = 0) : BaseEntryCount(count), endpoint_id(endpoint) {}
      96          607 :     ~EndpointEntryCount() {}
      97              : 
      98          897 :     CHIP_ERROR UpdateKey(StorageKeyName & key) const override
      99              :     {
     100          897 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     101          897 :         key = Serializer::EndpointEntryCountKey(endpoint_id);
     102          897 :         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          806 :     inline ConstCorrectRef(T & ref) : mRef(ref) {}
     124              : 
     125              :     inline const T * operator->() const { return &mRef; }
     126              :     inline T * operator->() { return &mRef; }
     127              : 
     128          412 :     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          403 :     TableEntryData(EndpointId endpoint, FabricIndex fabric, StorageId & id, StorageData & data, EntryIndex idx = 0) :
     145          403 :         endpoint_id(endpoint), fabric_index(fabric), index(idx), storage_id(id), storage_data(data)
     146          403 :     {}
     147              : 
     148          320 :     CHIP_ERROR UpdateKey(StorageKeyName & key) const override
     149              :     {
     150          320 :         VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
     151          320 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     152          320 :         key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
     153          320 :         return CHIP_NO_ERROR;
     154              :     }
     155              : 
     156          114 :     void Clear() override { Serializer::Clear(*storage_data); }
     157              : 
     158          206 :     CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
     159              :     {
     160              :         TLV::TLVType container;
     161          206 :         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, container));
     162              : 
     163          206 :         ReturnErrorOnFailure(Serializer::SerializeId(writer, *storage_id));
     164              : 
     165          206 :         ReturnErrorOnFailure(Serializer::SerializeData(writer, *storage_data));
     166              : 
     167          206 :         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         1670 :     FabricEntryData(EndpointId endpoint = kInvalidEndpointId, FabricIndex fabric = kUndefinedFabricIndex,
     206              :                     uint16_t maxPerFabric = kMaxPerFabric, uint16_t maxPerEndpoint = Serializer::kMaxPerEndpoint()) :
     207         1670 :         endpoint_id(endpoint),
     208        17638 :         fabric_index(fabric), max_per_fabric(maxPerFabric), max_per_endpoint(maxPerEndpoint)
     209         1670 :     {}
     210              : 
     211         1978 :     CHIP_ERROR UpdateKey(StorageKeyName & key) const override
     212              :     {
     213         1978 :         VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
     214         1978 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     215         1978 :         key = Serializer::FabricEntryDataKey(fabric_index, endpoint_id);
     216         1978 :         return CHIP_NO_ERROR;
     217              :     }
     218              : 
     219         1670 :     void Clear() override
     220              :     {
     221         1670 :         entry_count = 0;
     222        17946 :         for (uint16_t i = 0; i < max_per_fabric; i++)
     223              :         {
     224        16276 :             entry_map[i].Clear();
     225              :         }
     226         1670 :     }
     227              : 
     228          290 :     CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override
     229              :     {
     230              :         TLV::TLVType fabricEntryContainer;
     231          290 :         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, fabricEntryContainer));
     232          290 :         ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagEntry::kEntryCount), entry_count));
     233              : 
     234              :         // Storing the entry map
     235              :         TLV::TLVType entryMapContainer;
     236          290 :         ReturnErrorOnFailure(
     237              :             writer.StartContainer(TLV::ContextTag(TagEntry::kStorageIdArray), TLV::kTLVType_Array, entryMapContainer));
     238         2702 :         for (uint16_t i = 0; i < max_per_fabric; i++)
     239              :         {
     240              :             TLV::TLVType entryIdContainer;
     241         2412 :             ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, entryIdContainer));
     242         2412 :             ReturnErrorOnFailure(Serializer::SerializeId(writer, entry_map[i]));
     243         2412 :             ReturnErrorOnFailure(writer.EndContainer(entryIdContainer));
     244              :         }
     245          290 :         ReturnErrorOnFailure(writer.EndContainer(entryMapContainer));
     246              : 
     247          290 :         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          841 :     CHIP_ERROR Deserialize(TLV::TLVReader & reader, PersistentStorageDelegate & storage, uint8_t & deleted_entries_count)
     267              :     {
     268          841 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
     269              :         TLV::TLVType fabricEntryContainer;
     270          841 :         ReturnErrorOnFailure(reader.EnterContainer(fabricEntryContainer));
     271          841 :         ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagEntry::kEntryCount)));
     272          841 :         ReturnErrorOnFailure(reader.Get(entry_count));
     273          841 :         entry_count = std::min(entry_count, static_cast<uint8_t>(max_per_fabric));
     274          841 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::ContextTag(TagEntry::kStorageIdArray)));
     275              :         TLV::TLVType entryMapContainer;
     276          841 :         ReturnErrorOnFailure(reader.EnterContainer(entryMapContainer));
     277              : 
     278          841 :         uint16_t i = 0;
     279              :         CHIP_ERROR err;
     280          841 :         deleted_entries_count = 0;
     281              : 
     282        16878 :         while ((err = reader.Next(TLV::AnonymousTag())) == CHIP_NO_ERROR)
     283              :         {
     284              :             TLV::TLVType entryIdContainer;
     285         7598 :             if (i < max_per_fabric)
     286              :             {
     287         7588 :                 ReturnErrorOnFailure(reader.EnterContainer(entryIdContainer));
     288         7588 :                 ReturnErrorOnFailure(Serializer::DeserializeId(reader, entry_map[i]));
     289         7588 :                 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         7598 :             i++;
     302              :         }
     303              : 
     304         1682 :         VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
     305          841 :         ReturnErrorOnFailure(reader.ExitContainer(entryMapContainer));
     306          841 :         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          127 :     CHIP_ERROR FindByIndex(PersistentStorageDelegate & storage, EntryIndex index, StorageId & entry_id)
     312              :     {
     313          127 :         VerifyOrReturnError(index < max_per_fabric, CHIP_ERROR_NOT_FOUND);
     314          126 :         VerifyOrReturnError(entry_map[index].IsValid(), CHIP_ERROR_NOT_FOUND);
     315           66 :         VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX);
     316           66 :         VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT);
     317           66 :         if (!storage.SyncDoesKeyExist(Serializer::FabricEntryKey(fabric_index, endpoint_id, index).KeyName()))
     318              :         {
     319            0 :             return CHIP_ERROR_NOT_FOUND;
     320              :         }
     321           66 :         entry_id = entry_map[index];
     322           66 :         return CHIP_NO_ERROR;
     323              :     }
     324              : 
     325              :     /// @brief  Finds the index where the current entry should be inserted by going through the endpoint's table and checking
     326              :     /// whether the entry is already there. If the target is not in the table, sets idx to the first empty space.
     327              :     /// If the target was not found and the table is full, sets idx to kUndefinedEntryIndex.
     328              :     /// @param[in] target_entry StorageId of entry to find
     329              :     /// @param[out] idx Index where target or space is found.
     330              :     /// @return CHIP_NO_ERROR if managed to find the target entry, CHIP_ERROR_NOT_FOUND if not found and space left
     331              :     ///         CHIP_ERROR_NO_MEMORY if target was not found and table is full
     332          429 :     CHIP_ERROR Find(const StorageId & target_entry, EntryIndex & idx)
     333              :     {
     334          429 :         EntryIndex firstFreeIdx = Data::kUndefinedEntryIndex; // storage index if entry not found
     335          429 :         uint16_t index          = 0;
     336              : 
     337         2756 :         while (index < max_per_fabric)
     338              :         {
     339         2541 :             if (entry_map[index] == target_entry)
     340              :             {
     341          214 :                 idx = index;
     342          214 :                 return CHIP_NO_ERROR; // return entry at current index if entry found
     343              :             }
     344         2327 :             if (!entry_map[index].IsValid() && firstFreeIdx == Data::kUndefinedEntryIndex)
     345              :             {
     346          275 :                 firstFreeIdx = index;
     347              :             }
     348         2327 :             index++;
     349              :         }
     350              : 
     351          215 :         if (firstFreeIdx < max_per_fabric)
     352              :         {
     353          209 :             idx = firstFreeIdx;
     354          209 :             return CHIP_ERROR_NOT_FOUND;
     355              :         }
     356            6 :         idx = Data::kUndefinedEntryIndex;
     357            6 :         return CHIP_ERROR_NO_MEMORY;
     358              :     }
     359              : 
     360          210 :     CHIP_ERROR SaveEntry(PersistentStorageDelegate & storage, const StorageId & id, const StorageData & data, Buffer & buffer)
     361              :     {
     362          210 :         CHIP_ERROR err = CHIP_NO_ERROR;
     363              :         // Look for empty storage space
     364              : 
     365          210 :         EntryIndex index = Data::kUndefinedEntryIndex;
     366          210 :         err              = this->Find(id, index);
     367              : 
     368              :         // C++ doesn't have const constructors; variable is declared const
     369          210 :         const TypedTableEntryData entry(endpoint_id, fabric_index, const_cast<StorageId &>(id), const_cast<StorageData &>(data),
     370              :                                         index);
     371              : 
     372          420 :         if (CHIP_NO_ERROR == err)
     373              :         {
     374           10 :             return entry.Save(&storage, buffer.BufferSpan());
     375              :         }
     376              : 
     377          400 :         if (CHIP_ERROR_NOT_FOUND == err) // If not found, entry.index should be the first free index
     378              :         {
     379              :             // Update the global entry count
     380          198 :             TypedEndpointEntryCount endpoint_count(endpoint_id);
     381          198 :             ReturnErrorOnFailure(endpoint_count.Load(&storage));
     382          198 :             VerifyOrReturnError(endpoint_count.count_value < max_per_endpoint, CHIP_ERROR_NO_MEMORY);
     383          196 :             endpoint_count.count_value++;
     384          196 :             ReturnErrorOnFailure(endpoint_count.Save(&storage));
     385              : 
     386          196 :             entry_count++;
     387          196 :             entry_map[entry.index] = id;
     388              : 
     389          196 :             err = this->Save(&storage);
     390          392 :             if (CHIP_NO_ERROR != err)
     391              :             {
     392            0 :                 endpoint_count.count_value--;
     393            0 :                 ReturnErrorOnFailure(endpoint_count.Save(&storage));
     394            0 :                 return err;
     395              :             }
     396              : 
     397          196 :             err = entry.Save(&storage, buffer.BufferSpan());
     398              : 
     399              :             // on failure to save the entry, undoes the changes to Fabric Entry Data
     400          392 :             if (CHIP_NO_ERROR != err)
     401              :             {
     402            0 :                 endpoint_count.count_value--;
     403            0 :                 ReturnErrorOnFailure(endpoint_count.Save(&storage));
     404              : 
     405            0 :                 entry_count--;
     406            0 :                 entry_map[entry.index].Clear();
     407            0 :                 ReturnErrorOnFailure(this->Save(&storage));
     408            0 :                 return err;
     409              :             }
     410          198 :         }
     411              : 
     412          198 :         return err;
     413          210 :     }
     414              : 
     415              :     /// @brief Removes an entry from the non-volatile memory and clears its index in the entry map. Decreases the number of entries
     416              :     /// in the global entry count and in the entry fabric data if successful. As the entry map size is not compressed upon removal,
     417              :     /// this only clears the entry corresponding to the entry from the entry map.
     418              :     /// @param storage Storage delegate to access the entry
     419              :     /// @param entry_id Entry to remove
     420              :     /// @return CHIP_NO_ERROR if successful, specific CHIP_ERROR otherwise
     421           93 :     CHIP_ERROR RemoveEntry(PersistentStorageDelegate & storage, const StorageId & entry_id)
     422              :     {
     423           93 :         CHIP_ERROR err = CHIP_NO_ERROR;
     424              :         EntryIndex entryIndex;
     425              : 
     426              :         // Empty Entry Fabric Data returns CHIP_NO_ERROR on remove
     427           93 :         if (entry_count > 0)
     428              :         {
     429              :             // If Find doesn't return CHIP_NO_ERROR, the entry wasn't found, which doesn't return an error
     430          186 :             VerifyOrReturnValue(this->Find(entry_id, entryIndex) == CHIP_NO_ERROR, CHIP_NO_ERROR);
     431              : 
     432              :             // Update the global entry count
     433           92 :             TypedEndpointEntryCount endpoint_entry_count(endpoint_id);
     434           92 :             ReturnErrorOnFailure(endpoint_entry_count.Load(&storage));
     435           92 :             endpoint_entry_count.count_value--;
     436           92 :             ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
     437              : 
     438           92 :             entry_count--;
     439           92 :             entry_map[entryIndex].Clear();
     440           92 :             err = this->Save(&storage);
     441              : 
     442              :             // On failure to update the entry map, undo the global count modification
     443          184 :             if (CHIP_NO_ERROR != err)
     444              :             {
     445            0 :                 endpoint_entry_count.count_value++;
     446            0 :                 ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
     447            0 :                 return err;
     448              :             }
     449              : 
     450           92 :             err = DeleteValue(storage, entryIndex);
     451              : 
     452              :             // On failure to delete entry, undo the change to the Fabric Entry Data and the global entry count
     453          184 :             if (CHIP_NO_ERROR != err)
     454              :             {
     455            0 :                 endpoint_entry_count.count_value++;
     456            0 :                 ReturnErrorOnFailure(endpoint_entry_count.Save(&storage));
     457              : 
     458            0 :                 entry_count++;
     459            0 :                 entry_map[entryIndex] = entry_id;
     460            0 :                 ReturnErrorOnFailure(this->Save(&storage));
     461            0 :                 return err;
     462              :             }
     463           92 :         }
     464           92 :         return err;
     465              :     }
     466              : 
     467         1670 :     CHIP_ERROR Load(PersistentStorageDelegate * storage) // NOLINT(bugprone-derived-method-shadowing-base-method)
     468              :     {
     469         1670 :         VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT);
     470         1670 :         uint8_t deleted_entries_count = 0;
     471              : 
     472         1670 :         uint8_t buffer[kFabricMaxBytes] = { 0 };
     473         1670 :         StorageKeyName key              = StorageKeyName::Uninitialized();
     474              : 
     475              :         // Set data to defaults
     476         1670 :         Clear();
     477              : 
     478              :         // Update storage key
     479         1670 :         ReturnErrorOnFailure(UpdateKey(key));
     480              : 
     481              :         // Load the serialized data
     482         1670 :         uint16_t size  = static_cast<uint16_t>(sizeof(buffer));
     483         1670 :         CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), buffer, size);
     484         3340 :         VerifyOrReturnError(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND != err, CHIP_ERROR_NOT_FOUND);
     485          841 :         ReturnErrorOnFailure(err);
     486              : 
     487              :         // Decode serialized data
     488          841 :         TLV::TLVReader reader;
     489          841 :         reader.Init(buffer, size);
     490              : 
     491          841 :         err = Deserialize(reader, *storage, deleted_entries_count);
     492              : 
     493              :         // If Deserialize sets the "deleted_entries" variable, the table in flash memory held too many entries (can happen
     494              :         // if max_per_fabric was reduced during an OTA) and was adjusted during deserializing . The fabric data must then
     495              :         // be updated
     496          841 :         if (deleted_entries_count)
     497              :         {
     498            2 :             TypedEndpointEntryCount global_count(endpoint_id);
     499            2 :             ReturnErrorOnFailure(global_count.Load(storage));
     500            2 :             global_count.count_value = static_cast<uint8_t>(global_count.count_value - deleted_entries_count);
     501            2 :             ReturnErrorOnFailure(global_count.Save(storage));
     502            2 :             ReturnErrorOnFailure(this->Save(storage));
     503            2 :         }
     504              : 
     505          841 :         return err;
     506         1670 :     }
     507              : 
     508              : private:
     509          102 :     CHIP_ERROR DeleteValue(PersistentStorageDelegate & storage, EntryIndex index)
     510              :     {
     511          102 :         StorageKeyName key = Serializer::FabricEntryKey(fabric_index, endpoint_id, index);
     512          102 :         return storage.SyncDeleteKeyValue(key.KeyName());
     513          102 :     }
     514              : };
     515              : 
     516              : template <class StorageId, class StorageData>
     517           89 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::Init(PersistentStorageDelegate & storage)
     518              : {
     519              :     // Verify the initialized parameter respects the maximum allowed values for entry capacity
     520           89 :     VerifyOrReturnError(mMaxPerFabric <= Serializer::kMaxPerFabric() && mMaxPerEndpoint <= Serializer::kMaxPerEndpoint(),
     521              :                         CHIP_ERROR_INVALID_INTEGER_VALUE);
     522           87 :     this->mStorage = &storage;
     523           87 :     return CHIP_NO_ERROR;
     524              : }
     525              : 
     526              : template <class StorageId, class StorageData>
     527          104 : void FabricTableImpl<StorageId, StorageData>::Finish()
     528          104 : {}
     529              : 
     530              : template <class StorageId, class StorageData>
     531          147 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetFabricEntryCount(FabricIndex fabric_index, uint8_t & entry_count)
     532              : {
     533              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     534              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     535              : 
     536          147 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     537              : 
     538          147 :     TypedFabricEntryData fabric(mEndpointId, fabric_index);
     539          147 :     CHIP_ERROR err = fabric.Load(mStorage);
     540          303 :     VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     541              : 
     542          294 :     entry_count = (CHIP_ERROR_NOT_FOUND == err) ? 0 : fabric.entry_count;
     543              : 
     544          147 :     return CHIP_NO_ERROR;
     545          147 : }
     546              : 
     547              : template <class StorageId, class StorageData>
     548          315 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetEndpointEntryCount(uint8_t & entry_count)
     549              : {
     550              :     using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
     551              : 
     552          315 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     553              : 
     554          315 :     TypedEndpointEntryCount endpoint_entry_count(mEndpointId);
     555              : 
     556          315 :     ReturnErrorOnFailure(endpoint_entry_count.Load(mStorage));
     557          315 :     entry_count = endpoint_entry_count.count_value;
     558              : 
     559          315 :     return CHIP_NO_ERROR;
     560          315 : }
     561              : 
     562              : template <class StorageId, class StorageData>
     563            0 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetEndpointEntryCount(const uint8_t & entry_count)
     564              : {
     565              :     using TypedEndpointEntryCount = EndpointEntryCount<StorageId, StorageData>;
     566            0 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     567              : 
     568            0 :     TypedEndpointEntryCount endpoint_entry_count(mEndpointId, entry_count);
     569            0 :     return endpoint_entry_count.Save(mStorage);
     570            0 : }
     571              : 
     572              : template <class StorageId, class StorageData>
     573          290 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity)
     574              : {
     575              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     576              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     577              : 
     578          290 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     579              : 
     580          290 :     uint8_t endpoint_entry_count = 0;
     581          290 :     ReturnErrorOnFailure(GetEndpointEntryCount(endpoint_entry_count));
     582              : 
     583              :     // If the global entry count is higher than the maximal Global entry capacity, this returns a capacity of 0 until enough entries
     584              :     // have been deleted to bring the global number of entries under the global maximum.
     585          290 :     if (endpoint_entry_count > mMaxPerEndpoint)
     586              :     {
     587            6 :         capacity = 0;
     588            6 :         return CHIP_NO_ERROR;
     589              :     }
     590          284 :     uint8_t remaining_capacity_global = static_cast<uint8_t>(mMaxPerEndpoint - endpoint_entry_count);
     591          284 :     uint8_t remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric);
     592              : 
     593          284 :     TypedFabricEntryData fabric(mEndpointId, fabric_index);
     594              : 
     595              :     // Load fabric data (defaults to zero)TypedFabricEntryData
     596          284 :     CHIP_ERROR err = fabric.Load(mStorage);
     597          624 :     VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     598              : 
     599          568 :     if (err == CHIP_NO_ERROR)
     600              :     {
     601          228 :         remaining_capacity_fabric = static_cast<uint8_t>(mMaxPerFabric - fabric.entry_count);
     602              :     }
     603              : 
     604          284 :     capacity = std::min(remaining_capacity_fabric, remaining_capacity_global);
     605              : 
     606          284 :     return CHIP_NO_ERROR;
     607          284 : }
     608              : 
     609              : template <class StorageId, class StorageData>
     610              : template <size_t kEntryMaxBytes>
     611          210 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::SetTableEntry(FabricIndex fabric_index, const StorageId & id,
     612              :                                                                   const StorageData & data,
     613              :                                                                   PersistenceBuffer<kEntryMaxBytes> & writeBuffer)
     614              : {
     615              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     616              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     617              : 
     618          210 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     619              : 
     620          210 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     621              : 
     622              :     // Load fabric data (defaults to zero)
     623          210 :     CHIP_ERROR err = fabric.Load(mStorage);
     624          469 :     VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     625              : 
     626          210 :     err = fabric.SaveEntry(*mStorage, id, data, writeBuffer);
     627          210 :     return err;
     628          210 : }
     629              : 
     630              : template <class StorageId, class StorageData>
     631              : template <size_t kEntryMaxBytes>
     632          189 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::GetTableEntry(FabricIndex fabric_index, StorageId & entry_id,
     633              :                                                                   StorageData & data, PersistenceBuffer<kEntryMaxBytes> & buffer)
     634              : {
     635              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     636              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     637          189 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     638              : 
     639          189 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     640          189 :     TableEntryData<StorageId, StorageData> table_entry(mEndpointId, fabric_index, entry_id, data);
     641              : 
     642          189 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     643          244 :     VerifyOrReturnError(fabric.Find(entry_id, table_entry.index) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
     644              : 
     645          110 :     CHIP_ERROR err = table_entry.Load(mStorage, buffer.BufferSpan());
     646              : 
     647              :     // If entry.Load returns "buffer too small", the entry in memory is too big to be retrieved (this could happen if the
     648              :     // kEntryMaxBytes was reduced by OTA) and therefore must be deleted as is is no longer considered accessible.
     649          220 :     if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
     650              :     {
     651            0 :         ReturnErrorOnFailure(this->RemoveTableEntry(fabric_index, entry_id));
     652              :     }
     653          110 :     ReturnErrorOnFailure(err);
     654              : 
     655          110 :     return CHIP_NO_ERROR;
     656          189 : }
     657              : 
     658              : template <class StorageId, class StorageData>
     659            4 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::FindTableEntry(FabricIndex fabric_index, const StorageId & entry_id,
     660              :                                                                    EntryIndex & idx)
     661              : {
     662              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     663              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     664            4 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     665              : 
     666            4 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     667              : 
     668            4 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     669            8 :     VerifyOrReturnError(fabric.Find(entry_id, idx) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND);
     670              : 
     671            2 :     return CHIP_NO_ERROR;
     672            4 : }
     673              : 
     674              : template <class StorageId, class StorageData>
     675           18 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntry(FabricIndex fabric_index, const StorageId & entry_id)
     676              : {
     677              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     678              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     679              : 
     680           18 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     681           18 :     TypedFabricEntryData fabric(mEndpointId, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     682              : 
     683           18 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     684              : 
     685           18 :     return fabric.RemoveEntry(*mStorage, entry_id);
     686           18 : }
     687              : 
     688              : /// @brief This function is meant to provide a way to empty the entry table without knowing any specific entry Id. Outside of this
     689              : /// specific use case, RemoveTableEntry should be used.
     690              : /// @param fabric_index Fabric in which the entry belongs
     691              : /// @param entry_idx Position in the Table
     692              : /// @return CHIP_NO_ERROR if removal was successful, errors if failed to remove the entry or to update the fabric after removing it
     693              : template <class StorageId, class StorageData>
     694          127 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index,
     695              :                                                                                EntryIndex entry_idx)
     696              : {
     697              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     698              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     699              : 
     700          127 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     701              : 
     702          127 :     TypedFabricEntryData fabric(endpoint, fabric_index, mMaxPerFabric, mMaxPerEndpoint);
     703              : 
     704          127 :     ReturnErrorOnFailure(fabric.Load(mStorage));
     705          127 :     StorageId entryId;
     706          127 :     CHIP_ERROR err = fabric.FindByIndex(*mStorage, entry_idx, entryId);
     707          254 :     VerifyOrReturnValue(CHIP_ERROR_NOT_FOUND != err, CHIP_NO_ERROR);
     708           66 :     ReturnErrorOnFailure(err);
     709              : 
     710           66 :     return fabric.RemoveEntry(*mStorage, entryId);
     711          127 : }
     712              : 
     713              : template <class StorageId, class StorageData>
     714           46 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveFabric(DataModel::ProviderMetadataTree & provider,
     715              :                                                                  FabricIndex fabric_index)
     716              : {
     717              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     718              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     719              : 
     720           46 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     721              : 
     722           46 :     ReadOnlyBufferBuilder<DataModel::EndpointEntry> endpointsBuilder;
     723           46 :     ReturnErrorOnFailure(provider.Endpoints(endpointsBuilder));
     724              : 
     725          364 :     for (const auto & ep : endpointsBuilder.TakeBuffer())
     726              :     {
     727          159 :         EndpointId endpoint = ep.id;
     728          159 :         TypedFabricEntryData fabric(endpoint, fabric_index);
     729          159 :         EntryIndex idx = 0;
     730          159 :         CHIP_ERROR err = fabric.Load(mStorage);
     731          462 :         VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     732          318 :         if (CHIP_ERROR_NOT_FOUND == err)
     733              :         {
     734          144 :             continue;
     735              :         }
     736              : 
     737          114 :         while (idx < mMaxPerFabric)
     738              :         {
     739           99 :             err = RemoveTableEntryAtPosition(endpoint, fabric_index, idx);
     740          198 :             VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     741           99 :             idx++;
     742              :         }
     743              : 
     744              :         // Remove fabric entries on endpoint
     745           15 :         ReturnErrorOnFailure(fabric.Delete(mStorage));
     746              :     }
     747              : 
     748           46 :     return CHIP_NO_ERROR;
     749           46 : }
     750              : 
     751              : template <class StorageId, class StorageData>
     752            2 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::RemoveEndpoint()
     753              : {
     754              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     755              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     756              : 
     757            2 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     758              : 
     759         1014 :     for (FabricIndex fabric_index = kMinValidFabricIndex; fabric_index < kMaxValidFabricIndex; fabric_index++)
     760              :     {
     761          506 :         TypedFabricEntryData fabric(mEndpointId, fabric_index);
     762          506 :         CHIP_ERROR err = fabric.Load(mStorage);
     763         1515 :         VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     764         1012 :         if (CHIP_ERROR_NOT_FOUND == err)
     765              :         {
     766          503 :             continue;
     767              :         }
     768              : 
     769            3 :         EntryIndex idx = 0;
     770           28 :         while (idx < mMaxPerFabric)
     771              :         {
     772           25 :             err = RemoveTableEntryAtPosition(mEndpointId, fabric_index, idx);
     773           50 :             VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
     774           25 :             idx++;
     775              :         };
     776              : 
     777              :         // Remove fabric entries on endpoint
     778            3 :         ReturnErrorOnFailure(fabric.Delete(mStorage));
     779              :     }
     780              : 
     781            2 :     return CHIP_NO_ERROR;
     782              : }
     783              : 
     784              : template <class StorageId, class StorageData>
     785           95 : void FabricTableImpl<StorageId, StorageData>::SetEndpoint(EndpointId endpoint)
     786              : {
     787           95 :     mEndpointId = endpoint;
     788           95 : }
     789              : 
     790              : template <class StorageId, class StorageData>
     791           52 : void FabricTableImpl<StorageId, StorageData>::SetTableSize(uint16_t endpointTableSize, uint16_t maxPerFabric)
     792              : {
     793              :     // Verify the endpoint passed size respects the limits of the device configuration
     794           52 :     VerifyOrDie(Serializer::kMaxPerFabric() > 0);
     795           52 :     VerifyOrDie(Serializer::kMaxPerEndpoint() > 0);
     796           52 :     mMaxPerEndpoint = std::min(Serializer::kMaxPerEndpoint(), endpointTableSize);
     797           52 :     mMaxPerFabric   = std::min(endpointTableSize, std::min(Serializer::kMaxPerFabric(), maxPerFabric));
     798           52 : }
     799              : 
     800              : template <class StorageId, class StorageData>
     801              : template <size_t kEntryMaxBytes, class UnaryFunc>
     802            2 : CHIP_ERROR FabricTableImpl<StorageId, StorageData>::IterateEntries(FabricIndex fabric, PersistenceBuffer<kEntryMaxBytes> & buffer,
     803              :                                                                    UnaryFunc iterateFn)
     804              : {
     805            2 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);
     806              : 
     807            2 :     EntryIteratorImpl<kEntryMaxBytes> iterator(*this, fabric, mEndpointId, mMaxPerFabric, mMaxPerEndpoint, buffer);
     808            2 :     return iterateFn(iterator);
     809            2 : }
     810              : 
     811              : template <class StorageId, class StorageData>
     812              : template <size_t kEntryMaxBytes>
     813            2 : FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::EntryIteratorImpl(
     814              :     FabricTableImpl & provider, FabricIndex fabricIdx, EndpointId endpoint, uint16_t maxPerFabric, uint16_t maxPerEndpoint,
     815              :     PersistenceBuffer<kEntryMaxBytes> & buffer) :
     816            2 :     mProvider(provider),
     817            2 :     mBuffer(buffer), mFabric(fabricIdx), mEndpoint(endpoint), mMaxPerFabric(maxPerFabric), mMaxPerEndpoint(maxPerEndpoint)
     818              : {
     819              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     820              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     821              : 
     822            2 :     TypedFabricEntryData fabric(mEndpoint, fabricIdx, mMaxPerFabric, mMaxPerEndpoint);
     823            2 :     ReturnOnFailure(fabric.Load(provider.mStorage));
     824            2 :     mTotalEntries = fabric.entry_count;
     825            2 :     mEntryIndex   = 0;
     826            2 : }
     827              : 
     828              : template <class StorageId, class StorageData>
     829              : template <size_t kEntryMaxBytes>
     830            0 : size_t FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Count()
     831              : {
     832            0 :     return mTotalEntries;
     833              : }
     834              : 
     835              : template <class StorageId, class StorageData>
     836              : template <size_t kEntryMaxBytes>
     837            6 : bool FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Next(TableEntry & output)
     838              : {
     839              :     using TypedFabricEntryData = FabricEntryData<StorageId, StorageData, Serializer::kEntryMaxBytes(),
     840              :                                                  Serializer::kFabricMaxBytes(), Serializer::kMaxPerFabric()>;
     841              : 
     842            6 :     TypedFabricEntryData fabric(mEndpoint, mFabric);
     843              : 
     844           12 :     VerifyOrReturnError(fabric.Load(mProvider.mStorage) == CHIP_NO_ERROR, false);
     845              : 
     846              :     // looks for next available entry
     847           12 :     while (mEntryIndex < mMaxPerFabric)
     848              :     {
     849           10 :         if (fabric.entry_map[mEntryIndex].IsValid())
     850              :         {
     851            4 :             TableEntryData<StorageId, StorageData> entry(mEndpoint, mFabric, output.mStorageId, output.mStorageData, mEntryIndex);
     852            8 :             VerifyOrReturnError(entry.Load(mProvider.mStorage, mBuffer.BufferSpan()) == CHIP_NO_ERROR, false);
     853            4 :             mEntryIndex++;
     854              : 
     855            4 :             return true;
     856            4 :         }
     857              : 
     858            6 :         mEntryIndex++;
     859              :     }
     860              : 
     861            2 :     return false;
     862            6 : }
     863              : 
     864              : template <class StorageId, class StorageData>
     865              : template <size_t kEntryMaxBytes>
     866            0 : void FabricTableImpl<StorageId, StorageData>::EntryIteratorImpl<kEntryMaxBytes>::Release()
     867            0 : {}
     868              : } // namespace Storage
     869              : } // namespace app
     870              : } // namespace chip
        

Generated by: LCOV version 2.0-1