Matter SDK Coverage Report
Current view: top level - app - ClusterStateCache.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 89.3 % 290 259
Test Date: 2025-01-17 19:00:11 Functions: 46.9 % 49 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           36 : 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           36 :     uint32_t size = 5;
      38              : 
      39           36 :     if (aStatus.mClusterStatus.HasValue())
      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           36 :     return size;
      48              : }
      49              : 
      50              : } // anonymous namespace
      51              : 
      52              : template <bool CanEnableDataCaching>
      53           80 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize)
      54              : {
      55           80 :     Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
      56           80 :     TLV::TLVReader reader;
      57           80 :     reader.Init(*apData);
      58           80 :     size_t totalBufSize = reader.GetTotalLength();
      59           80 :     backingBuffer.Calloc(totalBufSize);
      60           80 :     VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
      61           80 :     TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
      62           80 :     ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
      63           80 :     aSize = writer.GetLengthWritten();
      64           80 :     ReturnErrorOnFailure(writer.Finalize(backingBuffer));
      65           80 :     return CHIP_NO_ERROR;
      66           80 : }
      67              : 
      68              : template <bool CanEnableDataCaching>
      69          115 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
      70              :                                                                  const StatusIB & aStatus)
      71              : {
      72          115 :     AttributeState state;
      73          115 :     bool endpointIsNew = false;
      74              : 
      75          115 :     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           15 :         endpointIsNew = true;
      83              :     }
      84              : 
      85          115 :     if (apData)
      86              :     {
      87           80 :         uint32_t elementSize = 0;
      88           80 :         ReturnErrorOnFailure(GetElementTLVSize(apData, elementSize));
      89              : 
      90              :         if constexpr (CanEnableDataCaching)
      91              :         {
      92           80 :             if (mCacheData)
      93              :             {
      94           74 :                 Platform::ScopedMemoryBufferWithSize<uint8_t> backingBuffer;
      95           74 :                 backingBuffer.Calloc(elementSize);
      96           74 :                 VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
      97           74 :                 TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), elementSize);
      98           74 :                 ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData));
      99           74 :                 ReturnErrorOnFailure(writer.Finalize(backingBuffer));
     100              : 
     101           74 :                 state.template Set<AttributeData>(std::move(backingBuffer));
     102           74 :             }
     103              :             else
     104              :             {
     105            6 :                 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           80 :         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           80 :         if (mLastReportDataPath.IsValidConcreteClusterPath() && mLastReportDataPath != aPath)
     121              :         {
     122           12 :             CommitPendingDataVersion();
     123              :         }
     124              : 
     125           80 :         bool foundEncompassingWildcardPath = false;
     126          102 :         for (const auto & path : mRequestPathSet)
     127              :         {
     128           79 :             if (path.IncludesAllAttributesInCluster(aPath))
     129              :             {
     130           57 :                 foundEncompassingWildcardPath = true;
     131           57 :                 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           80 :         if (foundEncompassingWildcardPath)
     137              :         {
     138           57 :             mCache[aPath.mEndpointId][aPath.mClusterId].mPendingDataVersion = aPath.mDataVersion;
     139              :         }
     140              : 
     141           80 :         mLastReportDataPath = aPath;
     142              :     }
     143              :     else
     144              :     {
     145              :         if constexpr (CanEnableDataCaching)
     146              :         {
     147           35 :             if (mCacheData)
     148              :             {
     149           32 :                 state.template Set<StatusIB>(aStatus);
     150              :             }
     151              :             else
     152              :             {
     153            3 :                 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          115 :     if (endpointIsNew)
     167              :     {
     168           15 :         mAddedEndpoints.push_back(aPath.mEndpointId);
     169              :     }
     170              : 
     171          115 :     mCache[aPath.mEndpointId][aPath.mClusterId].mAttributes[aPath.mAttributeId] = std::move(state);
     172              : 
     173          115 :     if (mCacheData)
     174              :     {
     175          106 :         mChangedAttributeSet.insert(aPath);
     176              :     }
     177              : 
     178          115 :     return CHIP_NO_ERROR;
     179          115 : }
     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           29 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportBegin()
     232              : {
     233           29 :     mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
     234           29 :     mChangedAttributeSet.clear();
     235           29 :     mAddedEndpoints.clear();
     236           29 :     mCallback.OnReportBegin();
     237           29 : }
     238              : 
     239              : template <bool CanEnableDataCaching>
     240           41 : void ClusterStateCacheT<CanEnableDataCaching>::CommitPendingDataVersion()
     241              : {
     242           41 :     if (!mLastReportDataPath.IsValidConcreteClusterPath())
     243              :     {
     244            7 :         return;
     245              :     }
     246              : 
     247           34 :     auto & lastClusterInfo = mCache[mLastReportDataPath.mEndpointId][mLastReportDataPath.mClusterId];
     248           34 :     if (lastClusterInfo.mPendingDataVersion.HasValue())
     249              :     {
     250           19 :         lastClusterInfo.mCommittedDataVersion = lastClusterInfo.mPendingDataVersion;
     251           19 :         lastClusterInfo.mPendingDataVersion.ClearValue();
     252              :     }
     253              : }
     254              : 
     255              : template <bool CanEnableDataCaching>
     256           29 : void ClusterStateCacheT<CanEnableDataCaching>::OnReportEnd()
     257              : {
     258           29 :     CommitPendingDataVersion();
     259           29 :     mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
     260           29 :     std::set<std::tuple<EndpointId, ClusterId>> changedClusters;
     261              : 
     262              :     //
     263              :     // Add the EndpointId and ClusterId into a set so that we only
     264              :     // convey unique combinations in the subsequent OnClusterChanged callback.
     265              :     //
     266          131 :     for (auto & path : mChangedAttributeSet)
     267              :     {
     268          102 :         mCallback.OnAttributeChanged(this, path);
     269          102 :         changedClusters.insert(std::make_tuple(path.mEndpointId, path.mClusterId));
     270              :     }
     271              : 
     272           62 :     for (auto & item : changedClusters)
     273              :     {
     274           33 :         mCallback.OnClusterChanged(this, std::get<0>(item), std::get<1>(item));
     275              :     }
     276              : 
     277           44 :     for (auto endpoint : mAddedEndpoints)
     278              :     {
     279           15 :         mCallback.OnEndpointAdded(this, endpoint);
     280              :     }
     281              : 
     282           29 :     mCallback.OnReportEnd();
     283           29 : }
     284              : 
     285              : template <>
     286          128 : CHIP_ERROR ClusterStateCacheT<true>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
     287              : {
     288              :     CHIP_ERROR err;
     289          128 :     auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
     290          128 :     ReturnErrorOnFailure(err);
     291              : 
     292           88 :     if (attributeState->template Is<StatusIB>())
     293              :     {
     294            3 :         return CHIP_ERROR_IM_STATUS_CODE_RECEIVED;
     295              :     }
     296              : 
     297           85 :     if (!attributeState->template Is<AttributeData>())
     298              :     {
     299            3 :         return CHIP_ERROR_KEY_NOT_FOUND;
     300              :     }
     301              : 
     302           82 :     reader.Init(attributeState->template Get<AttributeData>().Get(), attributeState->template Get<AttributeData>().AllocatedSize());
     303           82 :     return reader.Next();
     304              : }
     305              : 
     306              : template <>
     307            0 : CHIP_ERROR ClusterStateCacheT<false>::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const
     308              : {
     309            0 :     return CHIP_ERROR_KEY_NOT_FOUND;
     310              : }
     311              : 
     312              : template <bool CanEnableDataCaching>
     313           44 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::Get(EventNumber eventNumber, TLV::TLVReader & reader) const
     314              : {
     315              :     CHIP_ERROR err;
     316              : 
     317           44 :     auto eventData = GetEventData(eventNumber, err);
     318           44 :     ReturnErrorOnFailure(err);
     319              : 
     320           44 :     System::PacketBufferTLVReader bufReader;
     321              : 
     322           44 :     bufReader.Init(eventData->second.Retain());
     323           44 :     ReturnErrorOnFailure(bufReader.Next());
     324              : 
     325           44 :     reader.Init(bufReader);
     326           44 :     return CHIP_NO_ERROR;
     327           44 : }
     328              : 
     329              : template <bool CanEnableDataCaching>
     330              : const typename ClusterStateCacheT<CanEnableDataCaching>::EndpointState *
     331          168 : ClusterStateCacheT<CanEnableDataCaching>::GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const
     332              : {
     333          168 :     auto endpointIter = mCache.find(endpointId);
     334          168 :     if (endpointIter == mCache.end())
     335              :     {
     336           16 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     337           16 :         return nullptr;
     338              :     }
     339              : 
     340          152 :     err = CHIP_NO_ERROR;
     341          152 :     return &endpointIter->second;
     342              : }
     343              : 
     344              : template <bool CanEnableDataCaching>
     345              : const typename ClusterStateCacheT<CanEnableDataCaching>::ClusterState *
     346          168 : ClusterStateCacheT<CanEnableDataCaching>::GetClusterState(EndpointId endpointId, ClusterId clusterId, CHIP_ERROR & err) const
     347              : {
     348          168 :     auto endpointState = GetEndpointState(endpointId, err);
     349          168 :     if (err != CHIP_NO_ERROR)
     350              :     {
     351           16 :         return nullptr;
     352              :     }
     353              : 
     354          152 :     auto clusterState = endpointState->find(clusterId);
     355          152 :     if (clusterState == endpointState->end())
     356              :     {
     357           15 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     358           15 :         return nullptr;
     359              :     }
     360              : 
     361          137 :     err = CHIP_NO_ERROR;
     362          137 :     return &clusterState->second;
     363              : }
     364              : 
     365              : template <bool CanEnableDataCaching>
     366              : const typename ClusterStateCacheT<CanEnableDataCaching>::AttributeState *
     367          129 : ClusterStateCacheT<CanEnableDataCaching>::GetAttributeState(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId,
     368              :                                                             CHIP_ERROR & err) const
     369              : {
     370          129 :     auto clusterState = GetClusterState(endpointId, clusterId, err);
     371          129 :     if (err != CHIP_NO_ERROR)
     372              :     {
     373           31 :         return nullptr;
     374              :     }
     375              : 
     376           98 :     auto attributeState = clusterState->mAttributes.find(attributeId);
     377           98 :     if (attributeState == clusterState->mAttributes.end())
     378              :     {
     379            9 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     380            9 :         return nullptr;
     381              :     }
     382              : 
     383           89 :     err = CHIP_NO_ERROR;
     384           89 :     return &attributeState->second;
     385              : }
     386              : 
     387              : template <bool CanEnableDataCaching>
     388              : const typename ClusterStateCacheT<CanEnableDataCaching>::EventData *
     389           88 : ClusterStateCacheT<CanEnableDataCaching>::GetEventData(EventNumber eventNumber, CHIP_ERROR & err) const
     390              : {
     391           88 :     EventData compareKey;
     392              : 
     393           88 :     compareKey.first.mEventNumber = eventNumber;
     394           88 :     auto eventData                = mEventDataCache.find(std::move(compareKey));
     395           88 :     if (eventData == mEventDataCache.end())
     396              :     {
     397            0 :         err = CHIP_ERROR_KEY_NOT_FOUND;
     398            0 :         return nullptr;
     399              :     }
     400              : 
     401           88 :     err = CHIP_NO_ERROR;
     402           88 :     return &(*eventData);
     403           88 : }
     404              : 
     405              : template <bool CanEnableDataCaching>
     406          115 : void ClusterStateCacheT<CanEnableDataCaching>::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
     407              :                                                                const StatusIB & aStatus)
     408              : {
     409              :     //
     410              :     // Since the cache itself is a ReadClient::Callback, it may be incorrectly passed in directly when registering with the
     411              :     // ReadClient. This should be avoided, since that bypasses the built-in buffered reader adapter callback that is needed for
     412              :     // lists to work correctly.
     413              :     //
     414              :     // Instead, the right callback should be retrieved using GetBufferedCallback().
     415              :     //
     416              :     // To catch such errors, we validate that the provided concrete path never indicates a raw list item operation (which the
     417              :     // buffered reader will handle and convert for us).
     418              :     //
     419              :     //
     420          115 :     VerifyOrDie(!aPath.IsListItemOperation());
     421              : 
     422              :     // Copy the reader for forwarding
     423          115 :     TLV::TLVReader dataSnapshot;
     424          115 :     if (apData)
     425              :     {
     426           80 :         dataSnapshot.Init(*apData);
     427              :     }
     428              : 
     429          115 :     UpdateCache(aPath, apData, aStatus);
     430              : 
     431              :     //
     432              :     // Forward the call through.
     433              :     //
     434          115 :     mCallback.OnAttributeData(aPath, apData ? &dataSnapshot : nullptr, aStatus);
     435          115 : }
     436              : 
     437              : template <bool CanEnableDataCaching>
     438           28 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetVersion(const ConcreteClusterPath & aPath,
     439              :                                                                 Optional<DataVersion> & aVersion) const
     440              : {
     441           28 :     VerifyOrReturnError(aPath.IsValidConcreteClusterPath(), CHIP_ERROR_INVALID_ARGUMENT);
     442              :     CHIP_ERROR err;
     443           28 :     auto clusterState = GetClusterState(aPath.mEndpointId, aPath.mClusterId, err);
     444           28 :     ReturnErrorOnFailure(err);
     445           28 :     aVersion = clusterState->mCommittedDataVersion;
     446           28 :     return CHIP_NO_ERROR;
     447              : }
     448              : 
     449              : template <bool CanEnableDataCaching>
     450           32 : void ClusterStateCacheT<CanEnableDataCaching>::OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData,
     451              :                                                            const StatusIB * apStatus)
     452              : {
     453           32 :     VerifyOrDie(apData != nullptr || apStatus != nullptr);
     454              : 
     455           32 :     TLV::TLVReader dataSnapshot;
     456           32 :     if (apData)
     457              :     {
     458           32 :         dataSnapshot.Init(*apData);
     459              :     }
     460              : 
     461           32 :     UpdateEventCache(aEventHeader, apData, apStatus);
     462           32 :     mCallback.OnEventData(aEventHeader, apData ? &dataSnapshot : nullptr, apStatus);
     463           32 : }
     464              : 
     465              : template <>
     466            1 : CHIP_ERROR ClusterStateCacheT<true>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
     467              : {
     468              :     CHIP_ERROR err;
     469              : 
     470            1 :     auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err);
     471            1 :     ReturnErrorOnFailure(err);
     472              : 
     473            1 :     if (!attributeState->template Is<StatusIB>())
     474              :     {
     475            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     476              :     }
     477              : 
     478            1 :     status = attributeState->template Get<StatusIB>();
     479            1 :     return CHIP_NO_ERROR;
     480              : }
     481              : 
     482              : template <>
     483            0 : CHIP_ERROR ClusterStateCacheT<false>::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const
     484              : {
     485            0 :     return CHIP_ERROR_INVALID_ARGUMENT;
     486              : }
     487              : 
     488              : template <bool CanEnableDataCaching>
     489            0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetStatus(const ConcreteEventPath & path, StatusIB & status) const
     490              : {
     491            0 :     auto statusIter = mEventStatusCache.find(path);
     492            0 :     if (statusIter == mEventStatusCache.end())
     493              :     {
     494            0 :         return CHIP_ERROR_KEY_NOT_FOUND;
     495              :     }
     496              : 
     497            0 :     status = statusIter->second;
     498            0 :     return CHIP_NO_ERROR;
     499              : }
     500              : 
     501              : template <bool CanEnableDataCaching>
     502          545 : void ClusterStateCacheT<CanEnableDataCaching>::GetSortedFilters(std::vector<std::pair<DataVersionFilter, size_t>> & aVector) const
     503              : {
     504         1167 :     for (auto const & endpointIter : mCache)
     505              :     {
     506          622 :         EndpointId endpointId = endpointIter.first;
     507         1254 :         for (auto const & clusterIter : endpointIter.second)
     508              :         {
     509          632 :             if (!clusterIter.second.mCommittedDataVersion.HasValue())
     510              :             {
     511           65 :                 continue;
     512              :             }
     513          567 :             DataVersion dataVersion = clusterIter.second.mCommittedDataVersion.Value();
     514          567 :             size_t clusterSize      = 0;
     515          567 :             ClusterId clusterId     = clusterIter.first;
     516              : 
     517         1263 :             for (auto const & attributeIter : clusterIter.second.mAttributes)
     518              :             {
     519              :                 if constexpr (CanEnableDataCaching)
     520              :                 {
     521          696 :                     if (attributeIter.second.template Is<StatusIB>())
     522              :                     {
     523           33 :                         clusterSize += SizeOfStatusIB(attributeIter.second.template Get<StatusIB>());
     524              :                     }
     525          663 :                     else if (attributeIter.second.template Is<uint32_t>())
     526              :                     {
     527            0 :                         clusterSize += attributeIter.second.template Get<uint32_t>();
     528              :                     }
     529              :                     else
     530              :                     {
     531          663 :                         VerifyOrDie(attributeIter.second.template Is<AttributeData>());
     532          663 :                         TLV::TLVReader bufReader;
     533          663 :                         bufReader.Init(attributeIter.second.template Get<AttributeData>().Get(),
     534          663 :                                        attributeIter.second.template Get<AttributeData>().AllocatedSize());
     535          663 :                         ReturnOnFailure(bufReader.Next());
     536              :                         // Skip to the end of the element.
     537          663 :                         ReturnOnFailure(bufReader.Skip());
     538              : 
     539              :                         // Compute the amount of value data
     540          663 :                         clusterSize += bufReader.GetLengthRead();
     541              :                     }
     542              :                 }
     543              :                 else
     544              :                 {
     545            0 :                     clusterSize += attributeIter.second;
     546              :                 }
     547              :             }
     548              : 
     549          567 :             if (clusterSize == 0)
     550              :             {
     551              :                 // No data in this cluster, so no point in sending a dataVersion
     552              :                 // along at all.
     553            0 :                 continue;
     554              :             }
     555              : 
     556          567 :             DataVersionFilter filter(endpointId, clusterId, dataVersion);
     557              : 
     558          567 :             aVector.push_back(std::make_pair(filter, clusterSize));
     559              :         }
     560              :     }
     561              : 
     562          545 :     std::sort(aVector.begin(), aVector.end(),
     563          156 :               [](const std::pair<DataVersionFilter, size_t> & x, const std::pair<DataVersionFilter, size_t> & y) {
     564          156 :                   return x.second > y.second;
     565              :               });
     566            0 : }
     567              : 
     568              : template <bool CanEnableDataCaching>
     569          545 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::OnUpdateDataVersionFilterList(
     570              :     DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder, const Span<AttributePathParams> & aAttributePaths,
     571              :     bool & aEncodedDataVersionList)
     572              : {
     573          545 :     CHIP_ERROR err = CHIP_NO_ERROR;
     574          545 :     TLV::TLVWriter backup;
     575              : 
     576              :     // Only put paths into mRequestPathSet if they cover clusters in their entirety and no other path in our path list
     577              :     // points to a specific attribute from any of those clusters.
     578              :     // this would help for data-out-of-sync issue when handling store data version for the particular case on two paths: (E1, C1,
     579              :     // wildcard), (wildcard, C1, A1)
     580         1111 :     for (auto & attribute1 : aAttributePaths)
     581              :     {
     582          566 :         if (attribute1.HasWildcardAttributeId())
     583              :         {
     584          544 :             bool intersected = false;
     585         1104 :             for (auto & attribute2 : aAttributePaths)
     586              :             {
     587          561 :                 if (attribute2.HasWildcardAttributeId())
     588              :                 {
     589          555 :                     continue;
     590              :                 }
     591              : 
     592            6 :                 if (attribute1.Intersects(attribute2))
     593              :                 {
     594            1 :                     intersected = true;
     595            1 :                     break;
     596              :                 }
     597              :             }
     598              : 
     599          544 :             if (!intersected)
     600              :             {
     601          543 :                 mRequestPathSet.insert(attribute1);
     602              :             }
     603              :         }
     604              :     }
     605              : 
     606          545 :     std::vector<std::pair<DataVersionFilter, size_t>> filterVector;
     607          545 :     GetSortedFilters(filterVector);
     608              : 
     609          545 :     aEncodedDataVersionList = false;
     610          939 :     for (auto & filter : filterVector)
     611              :     {
     612          549 :         bool intersected = false;
     613          549 :         aDataVersionFilterIBsBuilder.Checkpoint(backup);
     614              : 
     615              :         // if the particular cached cluster does not intersect with user provided attribute paths, skip the cached one
     616          556 :         for (const auto & attributePath : aAttributePaths)
     617              :         {
     618          553 :             if (attributePath.IncludesAttributesInCluster(filter.first))
     619              :             {
     620          546 :                 intersected = true;
     621          546 :                 break;
     622              :             }
     623              :         }
     624          549 :         if (!intersected)
     625              :         {
     626            3 :             continue;
     627              :         }
     628              : 
     629          546 :         SuccessOrExit(err = aDataVersionFilterIBsBuilder.EncodeDataVersionFilterIB(filter.first));
     630          391 :         aEncodedDataVersionList = true;
     631              :     }
     632              : 
     633          545 : exit:
     634          545 :     if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     635              :     {
     636          155 :         ChipLogProgress(DataManagement, "OnUpdateDataVersionFilterList out of space; rolling back");
     637          155 :         aDataVersionFilterIBsBuilder.Rollback(backup);
     638          155 :         err = CHIP_NO_ERROR;
     639              :     }
     640          545 :     return err;
     641          545 : }
     642              : 
     643              : template <bool CanEnableDataCaching>
     644            9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(EndpointId endpointId)
     645              : {
     646            9 :     mCache.erase(endpointId);
     647            9 : }
     648              : 
     649              : template <bool CanEnableDataCaching>
     650            9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttributes(const ConcreteClusterPath & cluster)
     651              : {
     652              :     // Can't use GetEndpointState here, since that only handles const things.
     653            9 :     auto endpointIter = mCache.find(cluster.mEndpointId);
     654            9 :     if (endpointIter == mCache.end())
     655              :     {
     656            0 :         return;
     657              :     }
     658              : 
     659            9 :     auto & endpointState = endpointIter->second;
     660            9 :     endpointState.erase(cluster.mClusterId);
     661              : }
     662              : 
     663              : template <bool CanEnableDataCaching>
     664            9 : void ClusterStateCacheT<CanEnableDataCaching>::ClearAttribute(const ConcreteAttributePath & attribute)
     665              : {
     666              :     // Can't use GetClusterState here, since that only handles const things.
     667            9 :     auto endpointIter = mCache.find(attribute.mEndpointId);
     668            9 :     if (endpointIter == mCache.end())
     669              :     {
     670            0 :         return;
     671              :     }
     672              : 
     673            9 :     auto & endpointState = endpointIter->second;
     674            9 :     auto clusterIter     = endpointState.find(attribute.mClusterId);
     675            9 :     if (clusterIter == endpointState.end())
     676              :     {
     677            0 :         return;
     678              :     }
     679              : 
     680            9 :     auto & clusterState = clusterIter->second;
     681            9 :     clusterState.mAttributes.erase(attribute.mAttributeId);
     682              : }
     683              : 
     684              : template <bool CanEnableDataCaching>
     685            0 : CHIP_ERROR ClusterStateCacheT<CanEnableDataCaching>::GetLastReportDataPath(ConcreteClusterPath & aPath)
     686              : {
     687            0 :     if (mLastReportDataPath.IsValidConcreteClusterPath())
     688              :     {
     689            0 :         aPath = mLastReportDataPath;
     690            0 :         return CHIP_NO_ERROR;
     691              :     }
     692            0 :     return CHIP_ERROR_INCORRECT_STATE;
     693              : }
     694              : 
     695              : // Ensure that our out-of-line template methods actually get compiled.
     696              : template class ClusterStateCacheT<true>;
     697              : template class ClusterStateCacheT<false>;
     698              : 
     699              : } // namespace app
     700              : } // namespace chip
        

Generated by: LCOV version 2.0-1