From 59eb04e41d6193617073bb5b86d068ecc289be04 Mon Sep 17 00:00:00 2001
From: Andrei Litvin <andy314@gmail.com>
Date: Wed, 14 Aug 2024 14:30:11 -0400
Subject: [PATCH 1/3] Pull RAM usage reduction from write impl

---
 src/app/WriteHandler.cpp | 70 ++++++++++++++++++++++++----------------
 src/app/WriteHandler.h   | 50 +++++++++++++++++-----------
 2 files changed, 75 insertions(+), 45 deletions(-)

diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp
index 8d4a82d11630c4..2571385908df9a 100644
--- a/src/app/WriteHandler.cpp
+++ b/src/app/WriteHandler.cpp
@@ -16,19 +16,23 @@
  *    limitations under the License.
  */
 
-#include "messaging/ExchangeContext.h"
 #include <app/AppConfig.h>
 #include <app/AttributeAccessInterfaceRegistry.h>
+#include <app/AttributeValueDecoder.h>
 #include <app/InteractionModelEngine.h>
 #include <app/MessageDef/EventPathIB.h>
 #include <app/MessageDef/StatusIB.h>
 #include <app/StatusResponse.h>
 #include <app/WriteHandler.h>
+#include <app/data-model-provider/OperationTypes.h>
 #include <app/reporting/Engine.h>
 #include <app/util/MatterCallbacks.h>
 #include <app/util/ember-compatibility-functions.h>
 #include <credentials/GroupDataProvider.h>
+#include <lib/core/CHIPError.h>
+#include <lib/support/CodeUtils.h>
 #include <lib/support/TypeTraits.h>
+#include <messaging/ExchangeContext.h>
 #include <protocols/interaction_model/StatusCode.h>
 
 namespace chip {
@@ -62,7 +66,7 @@ void WriteHandler::Close()
     // successful.
     DeliverFinalListWriteEnd(false /* wasSuccessful */);
     mExchangeCtx.Release();
-    mSuppressResponse = false;
+    mStateFlags.Clear(StateBits::kSuppressResponse);
     MoveToState(State::Uninitialized);
 }
 
@@ -106,7 +110,7 @@ Status WriteHandler::OnWriteRequest(Messaging::ExchangeContext * apExchangeConte
     Status status = HandleWriteRequestMessage(apExchangeContext, std::move(aPayload), aIsTimedWrite);
 
     // The write transaction will be alive only when the message was handled successfully and there are more chunks.
-    if (!(status == Status::Success && mHasMoreChunks))
+    if (!(status == Status::Success && mStateFlags.Has(StateBits::kHasMoreChunks)))
     {
         Close();
     }
@@ -142,7 +146,7 @@ CHIP_ERROR WriteHandler::OnMessageReceived(Messaging::ExchangeContext * apExchan
     if (status == Status::Success)
     {
         // We have no more chunks, the write response has been sent in HandleWriteRequestMessage, so close directly.
-        if (!mHasMoreChunks)
+        if (!mStateFlags.Has(StateBits::kHasMoreChunks))
         {
             Close();
         }
@@ -184,8 +188,8 @@ CHIP_ERROR WriteHandler::SendWriteResponse(System::PacketBufferTLVWriter && aMes
     VerifyOrExit(mExchangeCtx, err = CHIP_ERROR_INCORRECT_STATE);
     mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
     err = mExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::WriteResponse, std::move(packet),
-                                    mHasMoreChunks ? Messaging::SendMessageFlags::kExpectResponse
-                                                   : Messaging::SendMessageFlags::kNone);
+                                    mStateFlags.Has(StateBits::kHasMoreChunks) ? Messaging::SendMessageFlags::kExpectResponse
+                                                                               : Messaging::SendMessageFlags::kNone);
     SuccessOrExit(err);
 
     MoveToState(State::Sending);
@@ -212,7 +216,7 @@ void WriteHandler::DeliverListWriteEnd(const ConcreteAttributePath & aPath, bool
 
 void WriteHandler::DeliverFinalListWriteEnd(bool writeWasSuccessful)
 {
-    if (mProcessingAttributePath.HasValue() && mProcessingAttributeIsList)
+    if (mProcessingAttributePath.HasValue() && mStateFlags.Has(StateBits::kProcessingAttributeIsList))
     {
         DeliverListWriteEnd(mProcessingAttributePath.Value(), writeWasSuccessful);
     }
@@ -221,7 +225,8 @@ void WriteHandler::DeliverFinalListWriteEnd(bool writeWasSuccessful)
 
 CHIP_ERROR WriteHandler::DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful)
 {
-    VerifyOrReturnError(mProcessingAttributePath.HasValue() && mProcessingAttributeIsList, CHIP_NO_ERROR);
+    VerifyOrReturnError(mProcessingAttributePath.HasValue() && mStateFlags.Has(StateBits::kProcessingAttributeIsList),
+                        CHIP_NO_ERROR);
 
     Credentials::GroupDataProvider::GroupEndpoint mapping;
     Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider();
@@ -321,18 +326,20 @@ CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeData
             continue;
         }
 
-        if (ShouldReportListWriteEnd(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath))
+        if (ShouldReportListWriteEnd(mProcessingAttributePath, mStateFlags.Has(StateBits::kProcessingAttributeIsList),
+                                     dataAttributePath))
         {
-            DeliverListWriteEnd(mProcessingAttributePath.Value(), mAttributeWriteSuccessful);
+            DeliverListWriteEnd(mProcessingAttributePath.Value(), mStateFlags.Has(StateBits::kAttributeWriteSuccessful));
         }
 
-        if (ShouldReportListWriteBegin(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath))
+        if (ShouldReportListWriteBegin(mProcessingAttributePath, mStateFlags.Has(StateBits::kProcessingAttributeIsList),
+                                       dataAttributePath))
         {
             DeliverListWriteBegin(dataAttributePath);
-            mAttributeWriteSuccessful = true;
+            mStateFlags.Set(StateBits::kAttributeWriteSuccessful);
         }
 
-        mProcessingAttributeIsList = dataAttributePath.IsListOperation();
+        mStateFlags.Set(StateBits::kProcessingAttributeIsList, dataAttributePath.IsListOperation());
         mProcessingAttributePath.SetValue(dataAttributePath);
 
         DataModelCallbacks::GetInstance()->AttributeOperation(DataModelCallbacks::OperationType::Write,
@@ -370,9 +377,9 @@ CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeData
 
     SuccessOrExit(err);
 
-    if (!mHasMoreChunks)
+    if (!mStateFlags.Has(StateBits::kHasMoreChunks))
     {
-        DeliverFinalListWriteEnd(mAttributeWriteSuccessful);
+        DeliverFinalListWriteEnd(mStateFlags.Has(StateBits::kAttributeWriteSuccessful));
     }
 
 exit:
@@ -426,8 +433,8 @@ CHIP_ERROR WriteHandler::ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttribut
         iterator = groupDataProvider->IterateEndpoints(fabric);
         VerifyOrExit(iterator != nullptr, err = CHIP_ERROR_NO_MEMORY);
 
-        bool shouldReportListWriteEnd =
-            ShouldReportListWriteEnd(mProcessingAttributePath, mProcessingAttributeIsList, dataAttributePath);
+        bool shouldReportListWriteEnd = ShouldReportListWriteEnd(
+            mProcessingAttributePath, mStateFlags.Has(StateBits::kProcessingAttributeIsList), dataAttributePath);
         bool shouldReportListWriteBegin = false; // This will be set below.
 
         const EmberAfAttributeMetadata * attributeMetadata = nullptr;
@@ -457,7 +464,8 @@ CHIP_ERROR WriteHandler::ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttribut
                                                                         dataAttributePath.mEndpointId, dataAttributePath.mListOp,
                                                                         dataAttributePath.mListIndex);
                 shouldReportListWriteBegin =
-                    ShouldReportListWriteBegin(mProcessingAttributePath, mProcessingAttributeIsList, pathForCheckingListWriteBegin);
+                    ShouldReportListWriteBegin(mProcessingAttributePath, mStateFlags.Has(StateBits::kProcessingAttributeIsList),
+                                               pathForCheckingListWriteBegin);
             }
 
             if (shouldReportListWriteEnd)
@@ -498,11 +506,10 @@ CHIP_ERROR WriteHandler::ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttribut
             DataModelCallbacks::GetInstance()->AttributeOperation(DataModelCallbacks::OperationType::Write,
                                                                   DataModelCallbacks::OperationOrder::Pre, dataAttributePath);
             err = WriteSingleClusterData(subjectDescriptor, dataAttributePath, tmpDataReader, this);
-
             if (err != CHIP_NO_ERROR)
             {
                 ChipLogError(DataManagement,
-                             "WriteSingleClusterData Endpoint=%u Cluster=" ChipLogFormatMEI " Attribute =" ChipLogFormatMEI
+                             "WriteClusterData Endpoint=%u Cluster=" ChipLogFormatMEI " Attribute =" ChipLogFormatMEI
                              " failed: %" CHIP_ERROR_FORMAT,
                              mapping.endpoint_id, ChipLogValueMEI(dataAttributePath.mClusterId),
                              ChipLogValueMEI(dataAttributePath.mAttributeId), err.Format());
@@ -512,7 +519,7 @@ CHIP_ERROR WriteHandler::ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttribut
         }
 
         dataAttributePath.mEndpointId = kInvalidEndpointId;
-        mProcessingAttributeIsList    = dataAttributePath.IsListOperation();
+        mStateFlags.Set(StateBits::kProcessingAttributeIsList, dataAttributePath.IsListOperation());
         mProcessingAttributePath.SetValue(dataAttributePath);
         iterator->Release();
     }
@@ -557,24 +564,33 @@ Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload,
 #if CHIP_CONFIG_IM_PRETTY_PRINT
     writeRequestParser.PrettyPrint();
 #endif
-    err = writeRequestParser.GetSuppressResponse(&mSuppressResponse);
+    bool boolValue;
+
+    boolValue = mStateFlags.Has(StateBits::kSuppressResponse);
+    err       = writeRequestParser.GetSuppressResponse(&boolValue);
+    mStateFlags.Set(StateBits::kSuppressResponse, boolValue);
     if (err == CHIP_END_OF_TLV)
     {
         err = CHIP_NO_ERROR;
     }
     SuccessOrExit(err);
 
-    err = writeRequestParser.GetTimedRequest(&mIsTimedRequest);
+    boolValue = mStateFlags.Has(StateBits::kIsTimedRequest);
+    err       = writeRequestParser.GetTimedRequest(&boolValue);
+    mStateFlags.Set(StateBits::kIsTimedRequest, boolValue);
     SuccessOrExit(err);
 
-    err = writeRequestParser.GetMoreChunkedMessages(&mHasMoreChunks);
+    boolValue = mStateFlags.Has(StateBits::kHasMoreChunks);
+    err       = writeRequestParser.GetMoreChunkedMessages(&boolValue);
+    mStateFlags.Set(StateBits::kHasMoreChunks, boolValue);
     if (err == CHIP_ERROR_END_OF_TLV)
     {
         err = CHIP_NO_ERROR;
     }
     SuccessOrExit(err);
 
-    if (mHasMoreChunks && (mExchangeCtx->IsGroupExchangeContext() || mIsTimedRequest))
+    if (mStateFlags.Has(StateBits::kHasMoreChunks) &&
+        (mExchangeCtx->IsGroupExchangeContext() || mStateFlags.Has(StateBits::kIsTimedRequest)))
     {
         // Sanity check: group exchange context should only have one chunk.
         // Also, timed requests should not have more than one chunk.
@@ -584,7 +600,7 @@ Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload,
     err = writeRequestParser.GetWriteRequests(&AttributeDataIBsParser);
     SuccessOrExit(err);
 
-    if (mIsTimedRequest != aIsTimedWrite)
+    if (mStateFlags.Has(StateBits::kIsTimedRequest) != aIsTimedWrite)
     {
         // The message thinks it should be part of a timed interaction but it's
         // not, or vice versa.
@@ -641,7 +657,7 @@ CHIP_ERROR WriteHandler::AddStatusInternal(const ConcreteDataAttributePath & aPa
 
     if (!aStatus.IsSuccess())
     {
-        mAttributeWriteSuccessful = false;
+        mStateFlags.Clear(StateBits::kAttributeWriteSuccessful);
     }
 
     ReturnErrorOnFailure(writeResponses.GetError());
diff --git a/src/app/WriteHandler.h b/src/app/WriteHandler.h
index 1884654f070710..0723a316738498 100644
--- a/src/app/WriteHandler.h
+++ b/src/app/WriteHandler.h
@@ -17,12 +17,15 @@
  */
 
 #pragma once
+
+#include <app/AppConfig.h>
 #include <app/AttributeAccessToken.h>
 #include <app/AttributePathParams.h>
 #include <app/InteractionModelDelegatePointers.h>
 #include <app/MessageDef/WriteResponseMessage.h>
 #include <lib/core/CHIPCore.h>
 #include <lib/core/TLVDebug.h>
+#include <lib/support/BitFlags.h>
 #include <lib/support/CodeUtils.h>
 #include <lib/support/DLLUtil.h>
 #include <lib/support/logging/CHIPLogging.h>
@@ -68,9 +71,11 @@ class WriteHandler : public Messaging::ExchangeDelegate
      *
      *  @param[in] apWriteHandlerDelegate  A Valid pointer to the WriteHandlerDelegate.
      *
+     *  @retval #CHIP_ERROR_INVALID_ARGUMENT on invalid pointers
      *  @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to
      *          kState_NotInitialized.
      *  @retval #CHIP_NO_ERROR On success.
+     *
      */
     CHIP_ERROR Init(WriteHandlerDelegate * apWriteHandlerDelegate);
 
@@ -115,7 +120,7 @@ class WriteHandler : public Messaging::ExchangeDelegate
     /**
      * Check whether the WriteRequest we are handling is a timed write.
      */
-    bool IsTimedWrite() const { return mIsTimedRequest; }
+    bool IsTimedWrite() const { return mStateFlags.Has(StateBits::kIsTimedRequest); }
 
     bool MatchesExchangeContext(Messaging::ExchangeContext * apExchangeContext) const
     {
@@ -136,7 +141,7 @@ class WriteHandler : public Messaging::ExchangeDelegate
 
 private:
     friend class TestWriteInteraction;
-    enum class State
+    enum class State : uint8_t
     {
         Uninitialized = 0, // The handler has not been initialized
         Initialized,       // The handler has been initialized and is ready
@@ -172,7 +177,6 @@ class WriteHandler : public Messaging::ExchangeDelegate
 
     CHIP_ERROR AddStatusInternal(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus);
 
-private:
     // ExchangeDelegate
     CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
                                  System::PacketBufferHandle && aPayload) override;
@@ -180,22 +184,7 @@ class WriteHandler : public Messaging::ExchangeDelegate
 
     Messaging::ExchangeHolder mExchangeCtx;
     WriteResponseMessage::Builder mWriteResponseBuilder;
-    State mState           = State::Uninitialized;
-    bool mIsTimedRequest   = false;
-    bool mSuppressResponse = false;
-    bool mHasMoreChunks    = false;
     Optional<ConcreteAttributePath> mProcessingAttributePath;
-    bool mProcessingAttributeIsList = false;
-    // We record the Status when AddStatus is called to determine whether all data of a list write is accepted.
-    // This value will be used by DeliverListWriteEnd and DeliverFinalListWriteEnd but it won't be used by group writes based on the
-    // fact that the errors that won't be delivered to AttributeAccessInterface are:
-    //  (1) Attribute not found
-    //  (2) Access control failed
-    //  (3) Write request to a read-only attribute
-    //  (4) Data version mismatch
-    //  (5) Not using timed write.
-    //  Where (1)-(3) will be consistent among the whole list write request, while (4) and (5) are not appliable to group writes.
-    bool mAttributeWriteSuccessful                = false;
     Optional<AttributeAccessToken> mACLCheckCache = NullOptional;
 
     // This may be a "fake" pointer or a real delegate pointer, depending
@@ -205,6 +194,31 @@ class WriteHandler : public Messaging::ExchangeDelegate
     // set to the global InteractionModelEngine and the size of this
     // member is 1 byte.
     InteractionModelDelegatePointer<WriteHandlerDelegate> mDelegate;
+
+    // bit level enums to save storage for this object. InteractionModelEngine maintains
+    // several of these objects, so every bit of storage multiplies storage usage.
+    enum class StateBits : uint8_t
+    {
+        kIsTimedRequest            = 0x01,
+        kSuppressResponse          = 0x02,
+        kHasMoreChunks             = 0x04,
+        kProcessingAttributeIsList = 0x08,
+        // We record the Status when AddStatus is called to determine whether all data of a list write is accepted.
+        // This value will be used by DeliverListWriteEnd and DeliverFinalListWriteEnd but it won't be used by group writes based on
+        // the fact that the errors that won't be delivered to AttributeAccessInterface are:
+        //  (1) Attribute not found
+        //  (2) Access control failed
+        //  (3) Write request to a read-only attribute
+        //  (4) Data version mismatch
+        //  (5) Not using timed write.
+        //  Where (1)-(3) will be consistent among the whole list write request, while (4) and (5) are not appliable to group
+        //  writes.
+        kAttributeWriteSuccessful = 0x10,
+    };
+
+    BitFlags<StateBits> mStateFlags;
+    State mState = State::Uninitialized;
 };
+
 } // namespace app
 } // namespace chip

From 9b1b9a974b3158a1cd33203c786b89e52b06eadc Mon Sep 17 00:00:00 2001
From: Andrei Litvin <andy314@gmail.com>
Date: Wed, 14 Aug 2024 14:31:12 -0400
Subject: [PATCH 2/3] remove some extra added includes

---
 src/app/WriteHandler.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp
index 2571385908df9a..906baac6c19592 100644
--- a/src/app/WriteHandler.cpp
+++ b/src/app/WriteHandler.cpp
@@ -18,21 +18,17 @@
 
 #include <app/AppConfig.h>
 #include <app/AttributeAccessInterfaceRegistry.h>
-#include <app/AttributeValueDecoder.h>
 #include <app/InteractionModelEngine.h>
 #include <app/MessageDef/EventPathIB.h>
 #include <app/MessageDef/StatusIB.h>
 #include <app/StatusResponse.h>
 #include <app/WriteHandler.h>
-#include <app/data-model-provider/OperationTypes.h>
 #include <app/reporting/Engine.h>
 #include <app/util/MatterCallbacks.h>
 #include <app/util/ember-compatibility-functions.h>
 #include <credentials/GroupDataProvider.h>
-#include <lib/core/CHIPError.h>
 #include <lib/support/CodeUtils.h>
 #include <lib/support/TypeTraits.h>
-#include <messaging/ExchangeContext.h>
 #include <protocols/interaction_model/StatusCode.h>
 
 namespace chip {

From 6a289f988935df05328449293d49b354e8d34925 Mon Sep 17 00:00:00 2001
From: Andrei Litvin <andy314@gmail.com>
Date: Wed, 14 Aug 2024 14:33:22 -0400
Subject: [PATCH 3/3] Apply review comment from previous PR

---
 src/app/WriteHandler.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp
index 906baac6c19592..15776eb545fc21 100644
--- a/src/app/WriteHandler.cpp
+++ b/src/app/WriteHandler.cpp
@@ -564,26 +564,26 @@ Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload,
 
     boolValue = mStateFlags.Has(StateBits::kSuppressResponse);
     err       = writeRequestParser.GetSuppressResponse(&boolValue);
-    mStateFlags.Set(StateBits::kSuppressResponse, boolValue);
     if (err == CHIP_END_OF_TLV)
     {
         err = CHIP_NO_ERROR;
     }
     SuccessOrExit(err);
+    mStateFlags.Set(StateBits::kSuppressResponse, boolValue);
 
     boolValue = mStateFlags.Has(StateBits::kIsTimedRequest);
     err       = writeRequestParser.GetTimedRequest(&boolValue);
-    mStateFlags.Set(StateBits::kIsTimedRequest, boolValue);
     SuccessOrExit(err);
+    mStateFlags.Set(StateBits::kIsTimedRequest, boolValue);
 
     boolValue = mStateFlags.Has(StateBits::kHasMoreChunks);
     err       = writeRequestParser.GetMoreChunkedMessages(&boolValue);
-    mStateFlags.Set(StateBits::kHasMoreChunks, boolValue);
     if (err == CHIP_ERROR_END_OF_TLV)
     {
         err = CHIP_NO_ERROR;
     }
     SuccessOrExit(err);
+    mStateFlags.Set(StateBits::kHasMoreChunks, boolValue);
 
     if (mStateFlags.Has(StateBits::kHasMoreChunks) &&
         (mExchangeCtx->IsGroupExchangeContext() || mStateFlags.Has(StateBits::kIsTimedRequest)))