From bf2cae9f07a116431b0e019fda3d7a1d0c628239 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 19 Apr 2024 00:11:14 -0400 Subject: [PATCH 1/3] Make ClusterStateCache use less memory when not storing attribute values. ClusterStateCache has a "no storing values" mode, but in that mode it still uses 24 bytes per attribute to store just the data size. For a typical device with a few hundred attributes, this adds up to multiple KB of RAM; across many devices it really starts to add up. Introduce a compile-time switch that lets us optimize the storage when we know we're not storing the data. This still keeps the old "enable storing data at compile time but turn it off at runtime" API, both for API compat and because it might reduce codesize for some consumers who care about that more than RAM. --- src/app/ClusterStateCache.cpp | 218 ++++++++++++++++++++++------------ src/app/ClusterStateCache.h | 41 +++++-- 2 files changed, 172 insertions(+), 87 deletions(-) diff --git a/src/app/ClusterStateCache.cpp b/src/app/ClusterStateCache.cpp index 7a6eed3de8576c..47d2edf277dab7 100644 --- a/src/app/ClusterStateCache.cpp +++ b/src/app/ClusterStateCache.cpp @@ -27,14 +27,14 @@ namespace app { namespace { // Determine how much space a StatusIB takes up on the wire. -size_t SizeOfStatusIB(const StatusIB & aStatus) +uint32_t SizeOfStatusIB(const StatusIB & aStatus) { // 1 byte: anonymous tag control byte for struct. // 1 byte: control byte for uint8 value. // 1 byte: context-specific tag for uint8 value. // 1 byte: the uint8 value. // 1 byte: end of container. - size_t size = 5; + uint32_t size = 5; if (aStatus.mClusterStatus.HasValue()) { @@ -49,7 +49,8 @@ size_t SizeOfStatusIB(const StatusIB & aStatus) } // anonymous namespace -CHIP_ERROR ClusterStateCache::GetElementTLVSize(TLV::TLVReader * apData, size_t & aSize) +template +CHIP_ERROR ClusterStateCacheT::GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize) { Platform::ScopedMemoryBufferWithSize backingBuffer; TLV::TLVReader reader; @@ -64,8 +65,9 @@ CHIP_ERROR ClusterStateCache::GetElementTLVSize(TLV::TLVReader * apData, size_t return CHIP_NO_ERROR; } -CHIP_ERROR ClusterStateCache::UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, - const StatusIB & aStatus) +template +CHIP_ERROR ClusterStateCacheT::UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, + const StatusIB & aStatus) { AttributeState state; bool endpointIsNew = false; @@ -82,24 +84,32 @@ CHIP_ERROR ClusterStateCache::UpdateCache(const ConcreteDataAttributePath & aPat if (apData) { - size_t elementSize = 0; + uint32_t elementSize = 0; ReturnErrorOnFailure(GetElementTLVSize(apData, elementSize)); - if (mCacheData) + if constexpr (CanEnableDataCaching) { - Platform::ScopedMemoryBufferWithSize backingBuffer; - backingBuffer.Calloc(elementSize); - VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY); - TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), elementSize); - ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData)); - ReturnErrorOnFailure(writer.Finalize(backingBuffer)); - - state.Set(std::move(backingBuffer)); + if (mCacheData) + { + Platform::ScopedMemoryBufferWithSize backingBuffer; + backingBuffer.Calloc(elementSize); + VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY); + TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), elementSize); + ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), *apData)); + ReturnErrorOnFailure(writer.Finalize(backingBuffer)); + + state.template Set(std::move(backingBuffer)); + } + else + { + state.template Set(elementSize); + } } else { - state.Set(elementSize); + state = elementSize; } + // // Clear out the committed data version and only set it again once we have received all data for this cluster. // Otherwise, we may have incomplete data that looks like it's complete since it has a valid data version. @@ -132,13 +142,20 @@ CHIP_ERROR ClusterStateCache::UpdateCache(const ConcreteDataAttributePath & aPat } else { - if (mCacheData) + if constexpr (CanEnableDataCaching) { - state.Set(aStatus); + if (mCacheData) + { + state.template Set(aStatus); + } + else + { + state.template Set(SizeOfStatusIB(aStatus)); + } } else { - state.Set(SizeOfStatusIB(aStatus)); + state = SizeOfStatusIB(aStatus); } } @@ -161,7 +178,9 @@ CHIP_ERROR ClusterStateCache::UpdateCache(const ConcreteDataAttributePath & aPat return CHIP_NO_ERROR; } -CHIP_ERROR ClusterStateCache::UpdateEventCache(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) +template +CHIP_ERROR ClusterStateCacheT::UpdateEventCache(const EventHeader & aEventHeader, TLV::TLVReader * apData, + const StatusIB * apStatus) { if (apData) { @@ -208,7 +227,8 @@ CHIP_ERROR ClusterStateCache::UpdateEventCache(const EventHeader & aEventHeader, return CHIP_NO_ERROR; } -void ClusterStateCache::OnReportBegin() +template +void ClusterStateCacheT::OnReportBegin() { mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId); mChangedAttributeSet.clear(); @@ -216,7 +236,8 @@ void ClusterStateCache::OnReportBegin() mCallback.OnReportBegin(); } -void ClusterStateCache::CommitPendingDataVersion() +template +void ClusterStateCacheT::CommitPendingDataVersion() { if (!mLastReportDataPath.IsValidConcreteClusterPath()) { @@ -231,7 +252,8 @@ void ClusterStateCache::CommitPendingDataVersion() } } -void ClusterStateCache::OnReportEnd() +template +void ClusterStateCacheT::OnReportEnd() { CommitPendingDataVersion(); mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId); @@ -260,26 +282,37 @@ void ClusterStateCache::OnReportEnd() mCallback.OnReportEnd(); } -CHIP_ERROR ClusterStateCache::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const +template +CHIP_ERROR ClusterStateCacheT::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const { - CHIP_ERROR err; - auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); - ReturnErrorOnFailure(err); - if (attributeState->Is()) + if constexpr (CanEnableDataCaching) { - return CHIP_ERROR_IM_STATUS_CODE_RECEIVED; - } + CHIP_ERROR err; + auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); + ReturnErrorOnFailure(err); + + if (attributeState->template Is()) + { + return CHIP_ERROR_IM_STATUS_CODE_RECEIVED; + } + + if (!attributeState->template Is()) + { + return CHIP_ERROR_KEY_NOT_FOUND; + } - if (!attributeState->Is()) + reader.Init(attributeState->template Get().Get(), + attributeState->template Get().AllocatedSize()); + return reader.Next(); + } + else { return CHIP_ERROR_KEY_NOT_FOUND; } - - reader.Init(attributeState->Get().Get(), attributeState->Get().AllocatedSize()); - return reader.Next(); } -CHIP_ERROR ClusterStateCache::Get(EventNumber eventNumber, TLV::TLVReader & reader) const +template +CHIP_ERROR ClusterStateCacheT::Get(EventNumber eventNumber, TLV::TLVReader & reader) const { CHIP_ERROR err; @@ -295,7 +328,9 @@ CHIP_ERROR ClusterStateCache::Get(EventNumber eventNumber, TLV::TLVReader & read return CHIP_NO_ERROR; } -const ClusterStateCache::EndpointState * ClusterStateCache::GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const +template +const typename ClusterStateCacheT::EndpointState * +ClusterStateCacheT::GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const { auto endpointIter = mCache.find(endpointId); if (endpointIter == mCache.end()) @@ -308,8 +343,9 @@ const ClusterStateCache::EndpointState * ClusterStateCache::GetEndpointState(End return &endpointIter->second; } -const ClusterStateCache::ClusterState * ClusterStateCache::GetClusterState(EndpointId endpointId, ClusterId clusterId, - CHIP_ERROR & err) const +template +const typename ClusterStateCacheT::ClusterState * +ClusterStateCacheT::GetClusterState(EndpointId endpointId, ClusterId clusterId, CHIP_ERROR & err) const { auto endpointState = GetEndpointState(endpointId, err); if (err != CHIP_NO_ERROR) @@ -328,8 +364,10 @@ const ClusterStateCache::ClusterState * ClusterStateCache::GetClusterState(Endpo return &clusterState->second; } -const ClusterStateCache::AttributeState * ClusterStateCache::GetAttributeState(EndpointId endpointId, ClusterId clusterId, - AttributeId attributeId, CHIP_ERROR & err) const +template +const typename ClusterStateCacheT::AttributeState * +ClusterStateCacheT::GetAttributeState(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, + CHIP_ERROR & err) const { auto clusterState = GetClusterState(endpointId, clusterId, err); if (err != CHIP_NO_ERROR) @@ -348,7 +386,9 @@ const ClusterStateCache::AttributeState * ClusterStateCache::GetAttributeState(E return &attributeState->second; } -const ClusterStateCache::EventData * ClusterStateCache::GetEventData(EventNumber eventNumber, CHIP_ERROR & err) const +template +const typename ClusterStateCacheT::EventData * +ClusterStateCacheT::GetEventData(EventNumber eventNumber, CHIP_ERROR & err) const { EventData compareKey; @@ -364,7 +404,9 @@ const ClusterStateCache::EventData * ClusterStateCache::GetEventData(EventNumber return &(*eventData); } -void ClusterStateCache::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) +template +void ClusterStateCacheT::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, + const StatusIB & aStatus) { // // Since the cache itself is a ReadClient::Callback, it may be incorrectly passed in directly when registering with the @@ -394,7 +436,9 @@ void ClusterStateCache::OnAttributeData(const ConcreteDataAttributePath & aPath, mCallback.OnAttributeData(aPath, apData ? &dataSnapshot : nullptr, aStatus); } -CHIP_ERROR ClusterStateCache::GetVersion(const ConcreteClusterPath & aPath, Optional & aVersion) const +template +CHIP_ERROR ClusterStateCacheT::GetVersion(const ConcreteClusterPath & aPath, + Optional & aVersion) const { VerifyOrReturnError(aPath.IsValidConcreteClusterPath(), CHIP_ERROR_INVALID_ARGUMENT); CHIP_ERROR err; @@ -404,7 +448,9 @@ CHIP_ERROR ClusterStateCache::GetVersion(const ConcreteClusterPath & aPath, Opti return CHIP_NO_ERROR; } -void ClusterStateCache::OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) +template +void ClusterStateCacheT::OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, + const StatusIB * apStatus) { VerifyOrDie(apData != nullptr || apStatus != nullptr); @@ -418,23 +464,32 @@ void ClusterStateCache::OnEventData(const EventHeader & aEventHeader, TLV::TLVRe mCallback.OnEventData(aEventHeader, apData ? &dataSnapshot : nullptr, apStatus); } -CHIP_ERROR ClusterStateCache::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const +template +CHIP_ERROR ClusterStateCacheT::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const { - CHIP_ERROR err; + if constexpr (CanEnableDataCaching) + { + CHIP_ERROR err; - auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); - ReturnErrorOnFailure(err); + auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); + ReturnErrorOnFailure(err); + + if (!attributeState->template Is()) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } - if (!attributeState->Is()) + status = attributeState->template Get(); + return CHIP_NO_ERROR; + } + else { return CHIP_ERROR_INVALID_ARGUMENT; } - - status = attributeState->Get(); - return CHIP_NO_ERROR; } -CHIP_ERROR ClusterStateCache::GetStatus(const ConcreteEventPath & path, StatusIB & status) const +template +CHIP_ERROR ClusterStateCacheT::GetStatus(const ConcreteEventPath & path, StatusIB & status) const { auto statusIter = mEventStatusCache.find(path); if (statusIter == mEventStatusCache.end()) @@ -446,7 +501,8 @@ CHIP_ERROR ClusterStateCache::GetStatus(const ConcreteEventPath & path, StatusIB return CHIP_NO_ERROR; } -void ClusterStateCache::GetSortedFilters(std::vector> & aVector) const +template +void ClusterStateCacheT::GetSortedFilters(std::vector> & aVector) const { for (auto const & endpointIter : mCache) { @@ -463,26 +519,33 @@ void ClusterStateCache::GetSortedFilters(std::vector()) - { - clusterSize += SizeOfStatusIB(attributeIter.second.Get()); - } - else if (attributeIter.second.Is()) + if constexpr (CanEnableDataCaching) { - clusterSize += attributeIter.second.Get(); + if (attributeIter.second.template Is()) + { + clusterSize += SizeOfStatusIB(attributeIter.second.template Get()); + } + else if (attributeIter.second.template Is()) + { + clusterSize += attributeIter.second.template Get(); + } + else + { + VerifyOrDie(attributeIter.second.template Is()); + TLV::TLVReader bufReader; + bufReader.Init(attributeIter.second.template Get().Get(), + attributeIter.second.template Get().AllocatedSize()); + ReturnOnFailure(bufReader.Next()); + // Skip to the end of the element. + ReturnOnFailure(bufReader.Skip()); + + // Compute the amount of value data + clusterSize += bufReader.GetLengthRead(); + } } else { - VerifyOrDie(attributeIter.second.Is()); - TLV::TLVReader bufReader; - bufReader.Init(attributeIter.second.Get().Get(), - attributeIter.second.Get().AllocatedSize()); - ReturnOnFailure(bufReader.Next()); - // Skip to the end of the element. - ReturnOnFailure(bufReader.Skip()); - - // Compute the amount of value data - clusterSize += bufReader.GetLengthRead(); + clusterSize += attributeIter.second; } } @@ -505,9 +568,10 @@ void ClusterStateCache::GetSortedFilters(std::vector & aAttributePaths, - bool & aEncodedDataVersionList) +template +CHIP_ERROR ClusterStateCacheT::OnUpdateDataVersionFilterList( + DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder, const Span & aAttributePaths, + bool & aEncodedDataVersionList) { CHIP_ERROR err = CHIP_NO_ERROR; TLV::TLVWriter backup; @@ -587,7 +651,8 @@ CHIP_ERROR ClusterStateCache::OnUpdateDataVersionFilterList(DataVersionFilterIBs return err; } -CHIP_ERROR ClusterStateCache::GetLastReportDataPath(ConcreteClusterPath & aPath) +template +CHIP_ERROR ClusterStateCacheT::GetLastReportDataPath(ConcreteClusterPath & aPath) { if (mLastReportDataPath.IsValidConcreteClusterPath()) { @@ -596,5 +661,10 @@ CHIP_ERROR ClusterStateCache::GetLastReportDataPath(ConcreteClusterPath & aPath) } return CHIP_ERROR_INCORRECT_STATE; } + +// Ensure that our out-of-line template methods actually get compiled. +template class ClusterStateCacheT; +template class ClusterStateCacheT; + } // namespace app } // namespace chip diff --git a/src/app/ClusterStateCache.h b/src/app/ClusterStateCache.h index 4c663a2a5fc2f9..7f7e9e96e855a9 100644 --- a/src/app/ClusterStateCache.h +++ b/src/app/ClusterStateCache.h @@ -66,7 +66,8 @@ namespace app { * 2. The same cache cannot be used by multiple subscribe/read interactions at the same time. * */ -class ClusterStateCache : protected ReadClient::Callback +template +class ClusterStateCacheT : protected ReadClient::Callback { public: class Callback : public ReadClient::Callback @@ -83,17 +84,17 @@ class ClusterStateCache : protected ReadClient::Callback /* * Called anytime an attribute value has changed in the cache */ - virtual void OnAttributeChanged(ClusterStateCache * cache, const ConcreteAttributePath & path){}; + virtual void OnAttributeChanged(ClusterStateCacheT * cache, const ConcreteAttributePath & path){}; /* * Called anytime any attribute in a cluster has changed in the cache */ - virtual void OnClusterChanged(ClusterStateCache * cache, EndpointId endpointId, ClusterId clusterId){}; + virtual void OnClusterChanged(ClusterStateCacheT * cache, EndpointId endpointId, ClusterId clusterId){}; /* * Called anytime an endpoint was added to the cache */ - virtual void OnEndpointAdded(ClusterStateCache * cache, EndpointId endpointId){}; + virtual void OnEndpointAdded(ClusterStateCacheT * cache, EndpointId endpointId){}; }; /** @@ -104,18 +105,25 @@ class ClusterStateCache : protected ReadClient::Callback * @param [in] cacheData boolean to decide whether this cache would store attribute/event data/status, * the default is true. */ - ClusterStateCache(Callback & callback, Optional highestReceivedEventNumber = Optional::Missing(), - bool cacheData = true) : + ClusterStateCacheT(Callback & callback, Optional highestReceivedEventNumber = Optional::Missing()) : + mCallback(callback), mBufferedReader(*this) + { + mHighestReceivedEventNumber = highestReceivedEventNumber; + } + + template = true> + ClusterStateCacheT(Callback & callback, Optional highestReceivedEventNumber = Optional::Missing(), + bool cacheData = true) : mCallback(callback), mBufferedReader(*this), mCacheData(cacheData) { mHighestReceivedEventNumber = highestReceivedEventNumber; } - ClusterStateCache(const ClusterStateCache &) = delete; - ClusterStateCache(ClusterStateCache &&) = delete; - ClusterStateCache & operator=(const ClusterStateCache &) = delete; - ClusterStateCache & operator=(ClusterStateCache &&) = delete; + ClusterStateCacheT(const ClusterStateCacheT &) = delete; + ClusterStateCacheT(ClusterStateCacheT &&) = delete; + ClusterStateCacheT & operator=(const ClusterStateCacheT &) = delete; + ClusterStateCacheT & operator=(ClusterStateCacheT &&) = delete; void SetHighestReceivedEventNumber(EventNumber highestReceivedEventNumber) { @@ -534,8 +542,12 @@ class ClusterStateCache : protected ReadClient::Callback // * If we got data for the attribute and we are not storing data // oureselves, the size of the data, so we can still prioritize sending // DataVersions correctly. + // + // The data for a single attribute is not going to be gigabytes in size, so + // using uint32_t for the size is fine; on 64-bit systems this can save + // quite a bit of space. using AttributeData = Platform::ScopedMemoryBufferWithSize; - using AttributeState = Variant; + using AttributeState = std::conditional_t, uint32_t>; // mPendingDataVersion represents a tentative data version for a cluster that we have gotten some reports for. // // mCurrentDataVersion represents a known data version for a cluster. In order for this to have a @@ -659,7 +671,7 @@ class ClusterStateCache : protected ReadClient::Callback // on the wire if not all filters can be applied. void GetSortedFilters(std::vector> & aVector) const; - CHIP_ERROR GetElementTLVSize(TLV::TLVReader * apData, size_t & aSize); + CHIP_ERROR GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize); Callback & mCallback; NodeState mCache; @@ -672,9 +684,12 @@ class ClusterStateCache : protected ReadClient::Callback std::map mEventStatusCache; BufferedReadCallback mBufferedReader; ConcreteClusterPath mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId); - const bool mCacheData = true; + const bool mCacheData = CanEnableDataCaching; }; +using ClusterStateCache = ClusterStateCacheT; +using ClusterStateCacheNoData = ClusterStateCacheT; + }; // namespace app }; // namespace chip #endif // CHIP_CONFIG_ENABLE_READ_CLIENT From 1abc1e17a331e0c91f50233409fa91c60237c6bb Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 19 Apr 2024 11:21:55 -0400 Subject: [PATCH 2/3] Address review comment. --- src/app/ClusterStateCache.cpp | 40 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/app/ClusterStateCache.cpp b/src/app/ClusterStateCache.cpp index 47d2edf277dab7..712c2a86aa62bf 100644 --- a/src/app/ClusterStateCache.cpp +++ b/src/app/ClusterStateCache.cpp @@ -282,33 +282,31 @@ void ClusterStateCacheT::OnReportEnd() mCallback.OnReportEnd(); } -template -CHIP_ERROR ClusterStateCacheT::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const +template <> +CHIP_ERROR ClusterStateCacheT::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const { - if constexpr (CanEnableDataCaching) - { - CHIP_ERROR err; - auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); - ReturnErrorOnFailure(err); - - if (attributeState->template Is()) - { - return CHIP_ERROR_IM_STATUS_CODE_RECEIVED; - } - - if (!attributeState->template Is()) - { - return CHIP_ERROR_KEY_NOT_FOUND; - } + CHIP_ERROR err; + auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); + ReturnErrorOnFailure(err); - reader.Init(attributeState->template Get().Get(), - attributeState->template Get().AllocatedSize()); - return reader.Next(); + if (attributeState->template Is()) + { + return CHIP_ERROR_IM_STATUS_CODE_RECEIVED; } - else + + if (!attributeState->template Is()) { return CHIP_ERROR_KEY_NOT_FOUND; } + + reader.Init(attributeState->template Get().Get(), attributeState->template Get().AllocatedSize()); + return reader.Next(); +} + +template <> +CHIP_ERROR ClusterStateCacheT::Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const +{ + return CHIP_ERROR_KEY_NOT_FOUND; } template From c980863b090e0cc90619f5bc458db1251623ad21 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 19 Apr 2024 14:28:21 -0400 Subject: [PATCH 3/3] Actually fix the review comment. --- src/app/ClusterStateCache.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/app/ClusterStateCache.cpp b/src/app/ClusterStateCache.cpp index 712c2a86aa62bf..e3dd3dd8d84ca4 100644 --- a/src/app/ClusterStateCache.cpp +++ b/src/app/ClusterStateCache.cpp @@ -462,28 +462,27 @@ void ClusterStateCacheT::OnEventData(const EventHeader & a mCallback.OnEventData(aEventHeader, apData ? &dataSnapshot : nullptr, apStatus); } -template -CHIP_ERROR ClusterStateCacheT::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const +template <> +CHIP_ERROR ClusterStateCacheT::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const { - if constexpr (CanEnableDataCaching) - { - CHIP_ERROR err; - - auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); - ReturnErrorOnFailure(err); + CHIP_ERROR err; - if (!attributeState->template Is()) - { - return CHIP_ERROR_INVALID_ARGUMENT; - } + auto attributeState = GetAttributeState(path.mEndpointId, path.mClusterId, path.mAttributeId, err); + ReturnErrorOnFailure(err); - status = attributeState->template Get(); - return CHIP_NO_ERROR; - } - else + if (!attributeState->template Is()) { return CHIP_ERROR_INVALID_ARGUMENT; } + + status = attributeState->template Get(); + return CHIP_NO_ERROR; +} + +template <> +CHIP_ERROR ClusterStateCacheT::GetStatus(const ConcreteAttributePath & path, StatusIB & status) const +{ + return CHIP_ERROR_INVALID_ARGUMENT; } template