Matter SDK Coverage Report
Current view: top level - app - BufferedReadCallback.cpp (source / functions) Coverage Total Hit
Test: SHA:704d97f9c619242ad76fcf75aeabc67802fa72d4 Lines: 96.6 % 89 86
Test Date: 2026-05-18 07:37:39 Functions: 100.0 % 7 7

            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 <app/BufferedReadCallback.h>
      20              : #include <app/InteractionModelEngine.h>
      21              : #include <lib/core/TLV.h>
      22              : #include <lib/core/TLVTags.h>
      23              : #include <lib/core/TLVTypes.h>
      24              : #include <lib/support/ScopedMemoryBuffer.h>
      25              : #include <protocols/interaction_model/Constants.h>
      26              : #include <system/SystemPacketBuffer.h>
      27              : #include <system/TLVPacketBufferBackingStore.h>
      28              : 
      29              : namespace chip {
      30              : namespace app {
      31              : 
      32          823 : void BufferedReadCallback::OnReportBegin()
      33              : {
      34          823 :     mCallback.OnReportBegin();
      35          823 : }
      36              : 
      37          823 : void BufferedReadCallback::OnReportEnd()
      38              : {
      39          823 :     CHIP_ERROR err = DispatchBufferedData(mBufferedPath, StatusIB(), true);
      40         1646 :     if (err != CHIP_NO_ERROR)
      41              :     {
      42            0 :         mCallback.OnError(err);
      43            0 :         return;
      44              :     }
      45              : 
      46          823 :     mCallback.OnReportEnd();
      47              : }
      48              : 
      49         1126 : CHIP_ERROR BufferedReadCallback::GenerateListTLV(TLV::ScopedBufferTLVReader & aReader)
      50              : {
      51              :     TLV::TLVType outerType;
      52         1126 :     Platform::ScopedMemoryBuffer<uint8_t> backingBuffer;
      53              : 
      54              :     //
      55              :     // To generate the final reconstituted list, we need to allocate a contiguous
      56              :     // buffer than can hold the entirety of its contents. To do so, we need to figure out
      57              :     // how big a buffer to allocate. This requires walking the buffered list items and computing their TLV sizes,
      58              :     // summing them all up and adding a bit of slop to account for the TLV array the list elements will go into.
      59              :     //
      60              :     // The alternative was to use a PacketBufferTLVWriter backed by chained packet buffers to
      61              :     // write out the list - this would have removed the need for this first pass. However,
      62              :     // we cannot actually back a TLVReader with a chained buffer since that violates the ability
      63              :     // for us to create readers off-of readers. Each reader would assume exclusive ownership of the chained
      64              :     // buffer and mutate the state within TLVPacketBufferBackingStore, preventing shared use.
      65              :     //
      66              :     // To avoid that, a single contiguous buffer is the best likely approach for now.
      67              :     //
      68         1126 :     size_t totalBufSize = 0;
      69         7524 :     for (const auto & packetBuffer : mBufferedList)
      70              :     {
      71         6398 :         totalBufSize += packetBuffer->TotalLength();
      72              :     }
      73              : 
      74              :     //
      75              :     // Size of the start container and end container are just 1 byte each, but, let's just be safe.
      76              :     //
      77         1126 :     totalBufSize += 4;
      78              : 
      79         1126 :     backingBuffer.Calloc(totalBufSize);
      80         1126 :     VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
      81              : 
      82         1126 :     TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
      83         1126 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, outerType));
      84              : 
      85         7524 :     for (auto & bufHandle : mBufferedList)
      86              :     {
      87         6398 :         System::PacketBufferTLVReader reader;
      88              : 
      89         6398 :         reader.Init(std::move(bufHandle));
      90              : 
      91         6398 :         ReturnErrorOnFailure(reader.Next());
      92         6398 :         ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
      93         6398 :     }
      94              : 
      95         1126 :     ReturnErrorOnFailure(writer.EndContainer(outerType));
      96              : 
      97         1126 :     TEMPORARY_RETURN_IGNORED writer.Finalize(backingBuffer);
      98              : 
      99         1126 :     aReader.Init(std::move(backingBuffer), totalBufSize);
     100              : 
     101         1126 :     return CHIP_NO_ERROR;
     102         1126 : }
     103              : 
     104         8012 : CHIP_ERROR BufferedReadCallback::BufferListItem(TLV::TLVReader & reader)
     105              : {
     106         8012 :     System::PacketBufferTLVWriter writer;
     107         8012 :     System::PacketBufferHandle handle;
     108              : 
     109              :     //
     110              :     // We conservatively allocate a packet buffer as big as an IPv6 MTU (since we're buffering
     111              :     // data received over the wire, which should always fit within that).
     112              :     //
     113              :     // We could have snapshotted the reader at its current position, advanced it past the current element
     114              :     // and computed the delta in its read point to figure out the size of the element before allocating
     115              :     // our target buffer. However, the reader's current position is already set past the control octet
     116              :     // and the tag. Consequently, the computed size is always going to omit the sizes of these two parts of the
     117              :     // TLV element. Since the tag can vary in size, for now, let's just do the safe thing. In the future, if this is a problem,
     118              :     // we can improve this.
     119              :     //
     120         8012 :     const size_t bufSize = mAllowLargePayload ? chip::app::kMaxLargeSecureSduLengthBytes : chip::app::kMaxSecureSduLengthBytes;
     121         8012 :     handle               = System::PacketBufferHandle::New(bufSize);
     122         8012 :     VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
     123              : 
     124         8012 :     writer.Init(std::move(handle), false);
     125              : 
     126         8012 :     ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
     127         8012 :     ReturnErrorOnFailure(writer.Finalize(&handle));
     128              : 
     129              :     // Compact the buffer down to a more reasonably sized packet buffer
     130              :     // if we can.
     131              :     //
     132         8012 :     handle.RightSize();
     133              : 
     134         8012 :     mBufferedList.push_back(std::move(handle));
     135              : 
     136         8012 :     return CHIP_NO_ERROR;
     137         8012 : }
     138              : 
     139         3562 : CHIP_ERROR BufferedReadCallback::BufferData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData)
     140              : {
     141              : 
     142         3562 :     if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll)
     143              :     {
     144         1272 :         VerifyOrReturnError(apData != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     145              :         TLV::TLVType outerContainer;
     146              : 
     147         1271 :         VerifyOrReturnError(apData->GetType() == TLV::kTLVType_Array, CHIP_ERROR_INVALID_TLV_ELEMENT);
     148         1271 :         mBufferedList.clear();
     149              : 
     150         1271 :         ReturnErrorOnFailure(apData->EnterContainer(outerContainer));
     151              : 
     152              :         CHIP_ERROR err;
     153              : 
     154        13992 :         while ((err = apData->Next()) == CHIP_NO_ERROR)
     155              :         {
     156         5725 :             ReturnErrorOnFailure(BufferListItem(*apData));
     157              :         }
     158              : 
     159         2542 :         if (err == CHIP_END_OF_TLV)
     160              :         {
     161         1271 :             err = CHIP_NO_ERROR;
     162              :         }
     163              : 
     164         1271 :         ReturnErrorOnFailure(err);
     165         1271 :         ReturnErrorOnFailure(apData->ExitContainer(outerContainer));
     166              :     }
     167         2290 :     else if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::AppendItem)
     168              :     {
     169         2288 :         VerifyOrReturnError(apData != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     170         2287 :         ReturnErrorOnFailure(BufferListItem(*apData));
     171              :     }
     172              : 
     173         3560 :     return CHIP_NO_ERROR;
     174              : }
     175              : 
     176         5773 : CHIP_ERROR BufferedReadCallback::DispatchBufferedData(const ConcreteAttributePath & aPath, const StatusIB & aStatusIB,
     177              :                                                       bool aEndOfReport)
     178              : {
     179         5773 :     if (aPath == mBufferedPath)
     180              :     {
     181              :         //
     182              :         // If we encountered the same list again and it's not the last DataIB, then
     183              :         // we need to continue to buffer up this list's data, so return immediately without dispatching
     184              :         // the existing buffered up contents.
     185              :         //
     186         3269 :         if (!aEndOfReport)
     187              :         {
     188         2446 :             return CHIP_NO_ERROR;
     189              :         }
     190              : 
     191              :         //
     192              :         // If we had previously buffered up data for this list and now we have encountered
     193              :         // an error for this list, that error takes precedence and the buffered data is now
     194              :         // rendered invalid. Return immediately without dispatching the existing buffered up contents.
     195              :         //
     196          823 :         if (aStatusIB.mStatus != Protocols::InteractionModel::Status::Success)
     197              :         {
     198            0 :             return CHIP_NO_ERROR;
     199              :         }
     200              :     }
     201              : 
     202         3327 :     if (!mBufferedPath.IsListOperation())
     203              :     {
     204         2201 :         return CHIP_NO_ERROR;
     205              :     }
     206              : 
     207         1126 :     StatusIB statusIB;
     208         1126 :     TLV::ScopedBufferTLVReader reader;
     209              : 
     210         1126 :     ReturnErrorOnFailure(GenerateListTLV(reader));
     211              : 
     212              :     //
     213              :     // Update the list operation to now reflect the delivery of the entire list
     214              :     // i.e a replace all operation.
     215              :     //
     216         1126 :     mBufferedPath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
     217              : 
     218              :     //
     219              :     // Advance the reader forward to the list itself
     220              :     //
     221         1126 :     ReturnErrorOnFailure(reader.Next());
     222              : 
     223         1126 :     mCallback.OnAttributeData(mBufferedPath, &reader, statusIB);
     224              : 
     225              :     //
     226              :     // Clear out our buffered contents to free up allocated buffers, and reset the buffered path.
     227              :     //
     228         1126 :     mBufferedList.clear();
     229         1126 :     mBufferedPath = ConcreteDataAttributePath();
     230         1126 :     return CHIP_NO_ERROR;
     231         1126 : }
     232              : 
     233         4950 : void BufferedReadCallback::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
     234              :                                            const StatusIB & aStatus)
     235              : {
     236              :     CHIP_ERROR err;
     237              : 
     238              :     //
     239              :     // First, let's dispatch to our registered callback any buffered up list data from previous calls.
     240              :     //
     241         4950 :     err = DispatchBufferedData(aPath, aStatus);
     242         4950 :     SuccessOrExit(err);
     243              : 
     244              :     //
     245              :     // We buffer up list data (only if the status was successful)
     246              :     //
     247         4950 :     if (aPath.IsListOperation() && aStatus.mStatus == Protocols::InteractionModel::Status::Success)
     248              :     {
     249         3562 :         err = BufferData(aPath, apData);
     250         3562 :         SuccessOrExit(err);
     251              :     }
     252              :     else
     253              :     {
     254         1388 :         mCallback.OnAttributeData(aPath, apData, aStatus);
     255              :     }
     256              : 
     257              :     //
     258              :     // Update our latched buffered path.
     259              :     //
     260         4948 :     mBufferedPath = aPath;
     261              : 
     262         4950 : exit:
     263         9900 :     if (err != CHIP_NO_ERROR)
     264              :     {
     265            2 :         mCallback.OnError(err);
     266              :     }
     267         4950 : }
     268              : 
     269              : } // namespace app
     270              : } // namespace chip
        

Generated by: LCOV version 2.0-1