Matter SDK Coverage Report
Current view: top level - app - ClusterStateCache.cpp (source / functions) Coverage Total Hit
Test: SHA:209dc18e4021e7d0dff8120ccc585909391dd862 Lines: 87.4 % 293 256
Test Date: 2026-06-16 07:34:53 Functions: 45.1 % 51 23

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 Project CHIP Authors
       4              :  *    All rights reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : #include "system/SystemPacketBuffer.h"
      20              : #include <app/ClusterStateCache.h>
      21              : #include <app/InteractionModelEngine.h>
      22              : #include <tuple>
      23              : 
      24              : namespace chip {
      25              : namespace app {
      26              : 
      27              : namespace {
      28              : 
      29              : // Determine how much space a StatusIB takes up on the wire.
      30            0 : uint32_t SizeOfStatusIB(const StatusIB & aStatus)
      31              : {
      32              :     // 1 byte: anonymous tag control byte for struct.
      33              :     // 1 byte: control byte for uint8 value.
      34              :     // 1 byte: context-specific tag for uint8 value.
      35              :     // 1 byte: the uint8 value.
      36              :     // 1 byte: end of container.
      37            0 :     uint32_t size = 5;
      38              : 
      39            0 :     if (aStatus.mClusterStatus.has_value())
      40              :     {
      41              :         // 1 byte: control byte for uint8 value.
      42              :         // 1 byte: context-specific tag for uint8 value.
      43              :         // 1 byte: the uint8 value.
      44            0 :         size += 3;
      45              :     }
      46              : 
      47            0 :     return size;
      48              : }
      49              : 
      50              : } // anonymous namespace
      51              : 
      52              : template <bool CanEnableDataCaching>
      53          157 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize)
      54              : {
      55          157 :     Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
      56          157 :     TLV::TLVReader reader;
      57          157 :     reader.Init(*apData);
      58          157 :     size_t totalBufSize = reader.GetTotalLength();
      59          157 :     backingBuffer.Calloc(totalBufSize);
      60          157 :     VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
      61          157 :     TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
      62          157 :     ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
      63          157 :     aSize = writer.GetLengthWritten();
      64          157 :     ReturnErrorOnFailure(writer.Finalize(backingBuffer));
      65          157 :     return CHIP_NO_ERROR;
      66          157 : }
      67              : 
      68              : template <bool CanEnableDataCaching>
      69          159 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
      70              :                                                                  const StatusIB & aStatus)
      71              : {
      72          159 :     AttributeState state;
      73          159 :     bool endpointIsNew = false;
      74              : 
      75          159 :     if (mCache.find(aPath.mEndpointId) == mCache.end())
      76              :     {
      77              :         //
      78              :         // Since we might potentially be creating a new entry at mCache[aPath.mEndpointId][aPath.mClusterId] that
      79              :         // wasn't there before, we need to check if an entry didn't exist there previously and remember that so that
      80              :         // we can appropriately notify our clients of the addition of a new endpoint.
      81              :         //
      82           33 :         endpointIsNew = true;
      83              :     }
      84              : 
      85          159 :     if (apData)
      86              :     {
      87          157 :         uint32_t elementSize = 0;
      88          157 :         ReturnErrorOnFailure(GetElementTLVSize(apData, elementSize));
      89              : 
      90              :         if constexpr (CanEnableDataCaching)
      91              :         {
      92          157 :             if (mCacheData)
      93              :             {
      94          148 :                 Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
      95          148 :                 backingBuffer.Calloc(elementSize);
      96          148 :                 VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
      97          148 :                 TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), elementSize);
      98          148 :                 ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
      99          148 :                 ReturnErrorOnFailure(writer.Finalize(backingBuffer));
     100              : 
     101          148 :                 state.template Set<AttributeData>(std::move(backingBuffer));
     102          148 :             }
     103              :             else
     104              :             {
     105            9 :                 state.template Set<uint32_t>(elementSize);
     106              :             }
     107              :         }
     108              :         else
     109              :         {
     110            0 :             state = elementSize;
     111              :         }
     112              : 
     113              :         //
     114              :         // Clear out the committed data version and only set it again once we have received all data for this cluster.
     115              :         // Otherwise, we may have incomplete data that looks like it's complete since it has a valid data version.
     116              :         //
     117          157 :         mCache[aPath.mEndpointId][aPath.mClusterId].mCommittedDataVersion.ClearValue();
     118              : 
     119              :         // This commits a pending data version if the last report path is valid and it is different from the current path.
     120          157 :         if (mLastReportDataPath.IsValidConcreteClusterPath() && mLastReportDataPath != aPath)
     121              :         {
     122           19 :             CommitPendingDataVersion();
     123              :         }
     124              : 
     125          157 :         bool foundEncompassingWildcardPath = false;
     126          194 :         for (const auto & path : mRequestPathSet)
     127              :         {
     128          124 :             if (path.IncludesAllAttributesInCluster(aPath))
     129              :             {
     130           87 :                 foundEncompassingWildcardPath = true;
     131           87 :                 break;
     132              :             }
     133              :         }
     134              : 
     135              :         // if this data item is encompassed by a wildcard path, let's go ahead and update its pending data version.
     136          157 :         if (foundEncompassingWildcardPath)
     137              :         {
     138           87 :             mCache[aPath.mEndpointId][aPath.mClusterId].mPendingDataVersion = aPath.mDataVersion;
     139              :         }
     140              : 
     141          157 :         mLastReportDataPath = aPath;
     142              :     }
     143              :     else
     144              :     {
     145              :         if constexpr (CanEnableDataCaching)
     146              :         {
     147            2 :             if (mCacheData)
     148              :             {
     149            2 :                 state.template Set<StatusIB>(aStatus);
     150              :             }
     151              :             else
     152              :             {
     153            0 :                 state.template Set<uint32_t>(SizeOfStatusIB(aStatus));
     154              :             }
     155              :         }
     156              :         else
     157              :         {
     158            0 :             state = SizeOfStatusIB(aStatus);
     159              :         }
     160              :     }
     161              : 
     162              :     //
     163              :     // if the endpoint didn't exist previously, let's track the insertion
     164              :     // so that we can inform our callback of a new endpoint being added appropriately.
     165              :     //
     166          159 :     if (endpointIsNew)
     167              :     {
     168           33 :         mAddedEndpoints.push_back(aPath.mEndpointId);
     169              :     }
     170              : 
     171          159 :     mCache[aPath.mEndpointId][aPath.mClusterId].mAttributes[aPath.mAttributeId] = std::move(state);
     172              : 
     173          159 :     if (mCacheData)
     174              :     {
     175          150 :         mChangedAttributeSet.insert(aPath);
     176              :     }
     177              : 
     178          159 :     return CHIP_NO_ERROR;
     179          159 : }
     180              : 
     181              : template <bool CanEnableDataCaching>
     182           32 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateEventCache(const EventHeader & aEventHeader, TLV::TLVReader * apData,
     183              :                                                                       const StatusIB * apStatus)
     184              : {
     185           32 :     if (apData)
     186              :     {
     187              :         //
     188              :         // If we've already seen this event before, there's no more work to be done.
     189              :         //
     190           32 :         if (mHighestReceivedEventNumber.HasValue() && aEventHeader.mEventNumber <= mHighestReceivedEventNumber.Value())
     191              :         {
     192            5 :             return CHIP_NO_ERROR;
     193              :         }
     194           27 :         if (mCacheData)
     195              :         {
     196           21 :             System::PacketBufferHandle handle = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
     197           21 :             VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
     198              : 
     199           21 :             System::PacketBufferTLVWriter writer;
     200           21 :             writer.Init(std::move(handle), false);
     201              : 
     202           21 :             ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
     203           21 :             ReturnErrorOnFailure(writer.Finalize(&handle));
     204              : 
     205              :             //
     206              :             // Compact the buffer down to a more reasonably sized packet buffer
     207              :             // if we can.
     208              :             //
     209           21 :             handle.RightSize();
     210              : 
     211           21 :             EventData eventData;
     212           21 :             eventData.first  = aEventHeader;
     213           21 :             eventData.second = std::move(handle);
     214              : 
     215           21 :             mEventDataCache.insert(std::move(eventData));
     216           21 :         }
     217           27 :         mHighestReceivedEventNumber.SetValue(aEventHeader.mEventNumber);
     218              :     }
     219            0 :     else if (apStatus)
     220              :     {
     221            0 :         if (mCacheData)
     222              :         {
     223            0 :             mEventStatusCache[aEventHeader.mPath] = *apStatus;
     224              :         }
     225              :     }
     226              : 
     227           27 :     return CHIP_NO_ERROR;
     228              : }
     229              : 
     230              : template <bool CanEnableDataCaching>
     231            6 : void ClusterStateCacheT<CanEnableDataCaching>::NotifySubscriptionStillActive(const ReadClient & aReadClient)
     232              : {
     233            6 :     mCallback.NotifySubscriptionStillActive(aReadClient);
     234            6 : }
     235              : 
     236              : template <bool CanEnableDataCaching>
     237           35 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportBegin()
     238              : {
     239           35 :     mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
     240           35 :     mChangedAttributeSet.clear();
     241           35 :     mAddedEndpoints.clear();
     242           35 :     mCallback.OnReportBegin();
     243           35 : }
     244              : 
     245              : template <bool CanEnableDataCaching>
     246           54 : void ClusterStateCacheT<CanEnableDataCaching>::CommitPendingDataVersion()
     247              : {
     248           54 :     if (!mLastReportDataPath.IsValidConcreteClusterPath())
     249              :     {
     250            7 :         return;
     251              :     }
     252              : 
     253           47 :     auto & lastClusterInfo = mCache[mLastReportDataPath.mEndpointId][mLastReportDataPath.mClusterId];
     254           47 :     if (lastClusterInfo.mPendingDataVersion.HasValue())
     255              :     {
     256           19 :         lastClusterInfo.mCommittedDataVersion = lastClusterInfo.mPendingDataVersion;
     257           19 :         lastClusterInfo.mPendingDataVersion.ClearValue();
     258              :     }
     259              : }
     260              : 
     261              : template <bool CanEnableDataCaching>
     262           35 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportEnd()
     263              : {
     264           35 :     CommitPendingDataVersion();
     265           35 :     mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
     266           35 :     std::set<std::tuple<EndpointId, ClusterId>> changedClusters;
     267              : 
     268              :     //
     269              :     // Add the EndpointId and ClusterId into a set so that we only
     270              :     // convey unique combinations in the subsequent OnClusterChanged callback.
     271              :     //
     272          143 :     for (auto & path : mChangedAttributeSet)
     273              :     {
     274          108 :         mCallback.OnAttributeChanged(this, path);
     275          108 :         changedClusters.insert(std::make_tuple(path.mEndpointId, path.mClusterId));
     276              :     }
     277              : 
     278           74 :     for (auto & item : changedClusters)
     279              :     {
     280           39 :         mCallback.OnClusterChanged(this, std::get<0>(item), std::get<1>(item));
     281              :     }
     282              : 
     283           52 :     for (auto endpoint : mAddedEndpoints)
     284              :     {
     285           17 :         mCallback.OnEndpointAdded(this, endpoint);
     286              :     }
     287              : 
     288           35 :     mCallback.OnReportEnd();
     289           35 : }
     290              : 
     291              : template <>
     292          156 : CHIP_ERROR ClusterStateCacheT<true>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
     293              : {
     294              :     CHIP_ERROR err;
     295          156 :     auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
     296          156 :     ReturnErrorOnFailure(err);
     297              : 
     298          116 :     if (attributeState->template Is<StatusIB>())
     299              :     {
     300            3 :         return CHIP_ERROR_IM_STATUS_CODE_RECEIVED;
     301              :     }
     302              : 
     303          113 :     if (!attributeState->template Is<AttributeData>())
     304              :     {
     305            3 :         return CHIP_ERROR_KEY_NOT_FOUND;
     306              :     }
     307              : 
     308          110 :     reader.Init(attributeState->template Get<AttributeData>().Get(), attributeState->template Get<AttributeData>().AllocatedSize());
     309          110 :     return reader.Next();
     310              : }
     311              : 
     312              : template <>
     313            0 : CHIP_ERROR ClusterStateCacheT<false>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
     314              : {
     315            0 :     return CHIP_ERROR_KEY_NOT_FOUND;
     316              : }
     317              : 
     318              : template <bool CanEnableDataCaching>
     319           44 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::Get(EventNumber eventNumber, TLV::TLVReader & reader) const
     320              : {
     321              :     CHIP_ERROR err;
     322              : 
     323           44 :     auto eventData = GetEventData(eventNumber, err);
     324           44 :     ReturnErrorOnFailure(err);
     325              : 
     326           44 :     System::PacketBufferTLVReader bufReader;
     327              : 
     328           44 :     bufReader.Init(eventData->second.Retain());
     329           44 :     ReturnErrorOnFailure(bufReader.Next());
     330              : 
     331           44 :     reader.Init(bufReader);
     332           44 :     return CHIP_NO_ERROR;
     333           44 : }
     334              : 
     335              : template <bool CanEnableDataCaching>
     336              : const typename ClusterStateCacheT<CanEnableDataCaching>::EndpointState *
     337          205 : ClusterStateCacheT<CanEnableDataCaching>::GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const
     338              : {
     339          205 :     auto endpointIter = mCache.find(endpointId);
     340          205 :     if (endpointIter == mCache.end())
     341              :     {
     342           16 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     343           16 :         return nullptr;
     344              :     }
     345              : 
     346          189 :     err = CHIP_NO_ERROR;
     347          189 :     return &endpointIter->second;
     348              : }
     349              : 
     350              : template <bool CanEnableDataCaching>
     351              : const typename ClusterStateCacheT<CanEnableDataCaching>::ClusterState *
     352          205 : ClusterStateCacheT<CanEnableDataCaching>::GetClusterState(EndpointId endpointId, ClusterId clusterId, CHIP_ERROR & err) const
     353              : {
     354          205 :     auto endpointState = GetEndpointState(endpointId, err);
     355          410 :     if (err != CHIP_NO_ERROR)
     356              :     {
     357           16 :         return nullptr;
     358              :     }
     359              : 
     360          189 :     auto clusterState = endpointState->find(clusterId);
     361          189 :     if (clusterState == endpointState->end())
     362              :     {
     363           15 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     364           15 :         return nullptr;
     365              :     }
     366              : 
     367          174 :     err = CHIP_NO_ERROR;
     368          174 :     return &clusterState->second;
     369              : }
     370              : 
     371              : template <bool CanEnableDataCaching>
     372              : const typename ClusterStateCacheT<CanEnableDataCaching>::AttributeState *
     373          157 : ClusterStateCacheT<CanEnableDataCaching>::GetAttributeState(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId,
     374              :                                                             CHIP_ERROR & err) const
     375              : {
     376          157 :     auto clusterState = GetClusterState(endpointId, clusterId, err);
     377          314 :     if (err != CHIP_NO_ERROR)
     378              :     {
     379           31 :         return nullptr;
     380              :     }
     381              : 
     382          126 :     auto attributeState = clusterState->mAttributes.find(attributeId);
     383          126 :     if (attributeState == clusterState->mAttributes.end())
     384              :     {
     385            9 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     386            9 :         return nullptr;
     387              :     }
     388              : 
     389          117 :     err = CHIP_NO_ERROR;
     390          117 :     return &attributeState->second;
     391              : }
     392              : 
     393              : template <bool CanEnableDataCaching>
     394              : const typename ClusterStateCacheT<CanEnableDataCaching>::EventData *
     395           88 : ClusterStateCacheT<CanEnableDataCaching>::GetEventData(EventNumber eventNumber, CHIP_ERROR & err) const
     396              : {
     397           88 :     EventData compareKey;
     398              : 
     399           88 :     compareKey.first.mEventNumber = eventNumber;
     400           88 :     auto eventData                = mEventDataCache.find(std::move(compareKey));
     401           88 :     if (eventData == mEventDataCache.end())
     402              :     {
     403            0 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     404            0 :         return nullptr;
     405              :     }
     406              : 
     407           88 :     err = CHIP_NO_ERROR;
     408           88 :     return &(*eventData);
     409           88 : }
     410              : 
     411              : template <bool CanEnableDataCaching>
     412          159 : void ClusterStateCacheT<CanEnableDataCaching>::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
     413              :                                                                const StatusIB & aStatus)
     414              : {
     415              :     //
     416              :     // Since the cache itself is a ReadClient::Callback, it may be incorrectly passed in directly when registering with the
     417              :     // ReadClient. This should be avoided, since that bypasses the built-in buffered reader adapter callback that is needed for
     418              :     // lists to work correctly.
     419              :     //
     420              :     // Instead, the right callback should be retrieved using GetBufferedCallback().
     421              :     //
     422              :     // To catch such errors, we validate that the provided concrete path never indicates a raw list item operation (which the
     423              :     // buffered reader will handle and convert for us).
     424              :     //
     425              :     //
     426          159 :     VerifyOrDie(!aPath.IsListItemOperation());
     427              : 
     428              :     // Copy the reader for forwarding
     429          159 :     TLV::TLVReader dataSnapshot;
     430          159 :     if (apData)
     431              :     {
     432          157 :         dataSnapshot.Init(*apData);
     433              :     }
     434              : 
     435          159 :     TEMPORARY_RETURN_IGNORED UpdateCache(aPath, apData, aStatus);
     436              : 
     437              :     //
     438              :     // Forward the call through.
     439              :     //
     440          159 :     mCallback.OnAttributeData(aPath, apData ? &dataSnapshot : nullptr, aStatus);
     441          159 : }
     442              : 
     443              : template <bool CanEnableDataCaching>
     444           28 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetVersion(const ConcreteClusterPath & aPath,
     445              :                                                                 Optional<DataVersion> & aVersion) const
     446              : {
     447           28 :     VerifyOrReturnError(aPath.IsValidConcreteClusterPath(), CHIP_ERROR_INVALID_ARGUMENT);
     448              :     CHIP_ERROR err;
     449           28 :     auto clusterState = GetClusterState(aPath.mEndpointId, aPath.mClusterId, err);
     450           28 :     ReturnErrorOnFailure(err);
     451           28 :     aVersion = clusterState->mCommittedDataVersion;
     452           28 :     return CHIP_NO_ERROR;
     453              : }
     454              : 
     455              : template <bool CanEnableDataCaching>
     456           32 : void ClusterStateCacheT<CanEnableDataCaching>::OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData,
     457              :                                                            const StatusIB * apStatus)
     458              : {
     459           32 :     VerifyOrDie(apData != nullptr || apStatus != nullptr);
     460              : 
     461           32 :     TLV::TLVReader dataSnapshot;
     462           32 :     if (apData)
     463              :     {
     464           32 :         dataSnapshot.Init(*apData);
     465              :     }
     466              : 
     467           32 :     TEMPORARY_RETURN_IGNORED UpdateEventCache(aEventHeader, apData, apStatus);
     468           32 :     mCallback.OnEventData(aEventHeader, apData ? &dataSnapshot : nullptr, apStatus);
     469           32 : }
     470              : 
     471              : template <>
     472            1 : CHIP_ERROR ClusterStateCacheT<true>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
     473              : {
     474              :     CHIP_ERROR err;
     475              : 
     476            1 :     auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
     477            1 :     ReturnErrorOnFailure(err);
     478              : 
     479            1 :     if (!attributeState->template Is<StatusIB>())
     480              :     {
     481            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     482              :     }
     483              : 
     484            1 :     status = attributeState->template Get<StatusIB>();
     485            1 :     return CHIP_NO_ERROR;
     486              : }
     487              : 
     488              : template <>
     489            0 : CHIP_ERROR ClusterStateCacheT<false>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
     490              : {
     491            0 :     return CHIP_ERROR_INVALID_ARGUMENT;
     492              : }
     493              : 
     494              : template <bool CanEnableDataCaching>
     495            0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetStatus(const ConcreteEventPath & path, StatusIB & status) const
     496              : {
     497            0 :     auto statusIter = mEventStatusCache.find(path);
     498            0 :     if (statusIter == mEventStatusCache.end())
     499              :     {
     500            0 :         return CHIP_ERROR_KEY_NOT_FOUND;
     501              :     }
     502              : 
     503            0 :     status = statusIter->second;
     504            0 :     return CHIP_NO_ERROR;
     505              : }
     506              : 
     507              : template <bool CanEnableDataCaching>
     508          547 : void ClusterStateCacheT<CanEnableDataCaching>::GetSortedFilters(std::vector<std::pair<DataVersionFilter, size_t>> & aVector) const
     509              : {
     510         1169 :     for (auto const & endpointIter : mCache)
     511              :     {
     512          622 :         EndpointId endpointId = endpointIter.first;
     513         1254 :         for (auto const & clusterIter : endpointIter.second)
     514              :         {
     515          632 :             if (!clusterIter.second.mCommittedDataVersion.HasValue())
     516              :             {
     517           65 :                 continue;
     518              :             }
     519          567 :             DataVersion dataVersion = clusterIter.second.mCommittedDataVersion.Value();
     520          567 :             size_t clusterSize      = 0;
     521          567 :             ClusterId clusterId     = clusterIter.first;
     522              : 
     523         1263 :             for (auto const & attributeIter : clusterIter.second.mAttributes)
     524              :             {
     525              :                 if constexpr (CanEnableDataCaching)
     526              :                 {
     527          696 :                     if (attributeIter.second.template Is<StatusIB>())
     528              :                     {
     529            0 :                         clusterSize += SizeOfStatusIB(attributeIter.second.template Get<StatusIB>());
     530              :                     }
     531          696 :                     else if (attributeIter.second.template Is<uint32_t>())
     532              :                     {
     533            0 :                         clusterSize += attributeIter.second.template Get<uint32_t>();
     534              :                     }
     535              :                     else
     536              :                     {
     537          696 :                         VerifyOrDie(attributeIter.second.template Is<AttributeData>());
     538          696 :                         TLV::TLVReader bufReader;
     539          696 :                         bufReader.Init(attributeIter.second.template Get<AttributeData>().Get(),
     540          696 :                                        attributeIter.second.template Get<AttributeData>().AllocatedSize());
     541          696 :                         ReturnOnFailure(bufReader.Next());
     542              :                         // Skip to the end of the element.
     543          696 :                         ReturnOnFailure(bufReader.Skip());
     544              : 
     545              :                         // Compute the amount of value data
     546          696 :                         clusterSize += bufReader.GetLengthRead();
     547              :                     }
     548              :                 }
     549              :                 else
     550              :                 {
     551            0 :                     clusterSize += attributeIter.second;
     552              :                 }
     553              :             }
     554              : 
     555          567 :             if (clusterSize == 0)
     556              :             {
     557              :                 // No data in this cluster, so no point in sending a dataVersion
     558              :                 // along at all.
     559            0 :                 continue;
     560              :             }
     561              : 
     562          567 :             DataVersionFilter filter(endpointId, clusterId, dataVersion);
     563              : 
     564          567 :             aVector.push_back(std::make_pair(filter, clusterSize));
     565              :         }
     566              :     }
     567              : 
     568          547 :     std::sort(aVector.begin(), aVector.end(),
     569          156 :               [](const std::pair<DataVersionFilter, size_t> & x, const std::pair<DataVersionFilter, size_t> & y) {
     570          156 :                   return x.second > y.second;
     571              :               });
     572            0 : }
     573              : 
     574              : template <bool CanEnableDataCaching>
     575          547 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::OnUpdateDataVersionFilterList(
     576              :     DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder, const Span<AttributePathParams> & aAttributePaths,
     577              :     bool & aEncodedDataVersionList)
     578              : {
     579          547 :     CHIP_ERROR err = CHIP_NO_ERROR;
     580          547 :     TLV::TLVWriter backup;
     581              : 
     582              :     // Only put paths into mRequestPathSet if they cover clusters in their entirety and no other path in our path list
     583              :     // points to a specific attribute from any of those clusters.
     584              :     // this would help for data-out-of-sync issue when handling store data version for the particular case on two paths: (E1, C1,
     585              :     // wildcard), (wildcard, C1, A1)
     586         1115 :     for (auto & attribute1 : aAttributePaths)
     587              :     {
     588          568 :         if (attribute1.HasWildcardAttributeId())
     589              :         {
     590          544 :             bool intersected = false;
     591         1104 :             for (auto & attribute2 : aAttributePaths)
     592              :             {
     593          561 :                 if (attribute2.HasWildcardAttributeId())
     594              :                 {
     595          555 :                     continue;
     596              :                 }
     597              : 
     598            6 :                 if (attribute1.Intersects(attribute2))
     599              :                 {
     600            1 :                     intersected = true;
     601            1 :                     break;
     602              :                 }
     603              :             }
     604              : 
     605          544 :             if (!intersected)
     606              :             {
     607          543 :                 mRequestPathSet.insert(attribute1);
     608              :             }
     609              :         }
     610              :     }
     611              : 
     612          547 :     std::vector<std::pair<DataVersionFilter, size_t>> filterVector;
     613          547 :     GetSortedFilters(filterVector);
     614              : 
     615          547 :     aEncodedDataVersionList = false;
     616          941 :     for (auto & filter : filterVector)
     617              :     {
     618          549 :         bool intersected = false;
     619          549 :         aDataVersionFilterIBsBuilder.Checkpoint(backup);
     620              : 
     621              :         // if the particular cached cluster does not intersect with user provided attribute paths, skip the cached one
     622          556 :         for (const auto & attributePath : aAttributePaths)
     623              :         {
     624          553 :             if (attributePath.IncludesAttributesInCluster(filter.first))
     625              :             {
     626          546 :                 intersected = true;
     627          546 :                 break;
     628              :             }
     629              :         }
     630          549 :         if (!intersected)
     631              :         {
     632            3 :             continue;
     633              :         }
     634              : 
     635          546 :         SuccessOrExit(err = aDataVersionFilterIBsBuilder.EncodeDataVersionFilterIB(filter.first));
     636          391 :         aEncodedDataVersionList = true;
     637              :     }
     638              : 
     639          547 : exit:
     640         1639 :     if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     641              :     {
     642          155 :         ChipLogProgress(DataManagement, "OnUpdateDataVersionFilterList out of space; rolling back");
     643          155 :         aDataVersionFilterIBsBuilder.Rollback(backup);
     644          155 :         err = CHIP_NO_ERROR;
     645              :     }
     646         1094 :     return err;
     647          547 : }
     648              : 
     649              : template <bool CanEnableDataCaching>
     650            9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(EndpointId endpointId)
     651              : {
     652            9 :     mCache.erase(endpointId);
     653            9 : }
     654              : 
     655              : template <bool CanEnableDataCaching>
     656            9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(const ConcreteClusterPath & cluster)
     657              : {
     658              :     // Can't use GetEndpointState here, since that only handles const things.
     659            9 :     auto endpointIter = mCache.find(cluster.mEndpointId);
     660            9 :     if (endpointIter == mCache.end())
     661              :     {
     662            0 :         return;
     663              :     }
     664              : 
     665            9 :     auto & endpointState = endpointIter->second;
     666            9 :     endpointState.erase(cluster.mClusterId);
     667              : }
     668              : 
     669              : template <bool CanEnableDataCaching>
     670            9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttribute(const ConcreteAttributePath & attribute)
     671              : {
     672              :     // Can't use GetClusterState here, since that only handles const things.
     673            9 :     auto endpointIter = mCache.find(attribute.mEndpointId);
     674            9 :     if (endpointIter == mCache.end())
     675              :     {
     676            0 :         return;
     677              :     }
     678              : 
     679            9 :     auto & endpointState = endpointIter->second;
     680            9 :     auto clusterIter     = endpointState.find(attribute.mClusterId);
     681            9 :     if (clusterIter == endpointState.end())
     682              :     {
     683            0 :         return;
     684              :     }
     685              : 
     686            9 :     auto & clusterState = clusterIter->second;
     687            9 :     clusterState.mAttributes.erase(attribute.mAttributeId);
     688              : }
     689              : 
     690              : template <bool CanEnableDataCaching>
     691            0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetLastReportDataPath(ConcreteClusterPath & aPath)
     692              : {
     693            0 :     if (mLastReportDataPath.IsValidConcreteClusterPath())
     694              :     {
     695            0 :         aPath = mLastReportDataPath;
     696            0 :         return CHIP_NO_ERROR;
     697              :     }
     698            0 :     return CHIP_ERROR_INCORRECT_STATE;
     699              : }
     700              : 
     701              : // Ensure that our out-of-line template methods actually get compiled.
     702              : template class ClusterStateCacheT<true>;
     703              : template class ClusterStateCacheT<false>;
     704              : 
     705              : } // namespace app
     706              : } // namespace chip
        

Generated by: LCOV version 2.0-1