Matter SDK Coverage Report
Current view: top level - app - BufferedReadCallback.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 95.3 % 86 82
Test Date: 2025-01-17 19:00:11 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 "lib/core/TLV.h"
      20              : #include "lib/core/TLVTags.h"
      21              : #include "lib/core/TLVTypes.h"
      22              : #include "protocols/interaction_model/Constants.h"
      23              : #include "system/SystemPacketBuffer.h"
      24              : #include "system/TLVPacketBufferBackingStore.h"
      25              : #include <app/BufferedReadCallback.h>
      26              : #include <app/InteractionModelEngine.h>
      27              : #include <lib/support/ScopedBuffer.h>
      28              : 
      29              : namespace chip {
      30              : namespace app {
      31              : 
      32          758 : void BufferedReadCallback::OnReportBegin()
      33              : {
      34          758 :     mCallback.OnReportBegin();
      35          758 : }
      36              : 
      37          758 : void BufferedReadCallback::OnReportEnd()
      38              : {
      39          758 :     CHIP_ERROR err = DispatchBufferedData(mBufferedPath, StatusIB(), true);
      40          758 :     if (err != CHIP_NO_ERROR)
      41              :     {
      42            0 :         mCallback.OnError(err);
      43            0 :         return;
      44              :     }
      45              : 
      46          758 :     mCallback.OnReportEnd();
      47              : }
      48              : 
      49         1028 : CHIP_ERROR BufferedReadCallback::GenerateListTLV(TLV::ScopedBufferTLVReader & aReader)
      50              : {
      51              :     TLV::TLVType outerType;
      52         1028 :     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         1028 :     size_t totalBufSize = 0;
      69         7083 :     for (const auto & packetBuffer : mBufferedList)
      70              :     {
      71         6055 :         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         1028 :     totalBufSize += 4;
      78              : 
      79         1028 :     backingBuffer.Calloc(totalBufSize);
      80         1028 :     VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
      81              : 
      82         1028 :     TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
      83         1028 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, outerType));
      84              : 
      85         7083 :     for (auto & bufHandle : mBufferedList)
      86              :     {
      87         6055 :         System::PacketBufferTLVReader reader;
      88              : 
      89         6055 :         reader.Init(std::move(bufHandle));
      90              : 
      91         6055 :         ReturnErrorOnFailure(reader.Next());
      92         6055 :         ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
      93         6055 :     }
      94              : 
      95         1028 :     ReturnErrorOnFailure(writer.EndContainer(outerType));
      96              : 
      97         1028 :     writer.Finalize(backingBuffer);
      98              : 
      99         1028 :     aReader.Init(std::move(backingBuffer), totalBufSize);
     100              : 
     101         1028 :     return CHIP_NO_ERROR;
     102         1028 : }
     103              : 
     104         7669 : CHIP_ERROR BufferedReadCallback::BufferListItem(TLV::TLVReader & reader)
     105              : {
     106         7669 :     System::PacketBufferTLVWriter writer;
     107         7669 :     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         7669 :     handle = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes);
     121         7669 :     VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
     122              : 
     123         7669 :     writer.Init(std::move(handle), false);
     124              : 
     125         7669 :     ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
     126         7669 :     ReturnErrorOnFailure(writer.Finalize(&handle));
     127              : 
     128              :     // Compact the buffer down to a more reasonably sized packet buffer
     129              :     // if we can.
     130              :     //
     131         7669 :     handle.RightSize();
     132              : 
     133         7669 :     mBufferedList.push_back(std::move(handle));
     134              : 
     135         7669 :     return CHIP_NO_ERROR;
     136         7669 : }
     137              : 
     138         3460 : CHIP_ERROR BufferedReadCallback::BufferData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData)
     139              : {
     140              : 
     141         3460 :     if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll)
     142              :     {
     143              :         TLV::TLVType outerContainer;
     144              : 
     145         1173 :         VerifyOrReturnError(apData->GetType() == TLV::kTLVType_Array, CHIP_ERROR_INVALID_TLV_ELEMENT);
     146         1173 :         mBufferedList.clear();
     147              : 
     148         1173 :         ReturnErrorOnFailure(apData->EnterContainer(outerContainer));
     149              : 
     150              :         CHIP_ERROR err;
     151              : 
     152         6555 :         while ((err = apData->Next()) == CHIP_NO_ERROR)
     153              :         {
     154         5382 :             ReturnErrorOnFailure(BufferListItem(*apData));
     155              :         }
     156              : 
     157         1173 :         if (err == CHIP_END_OF_TLV)
     158              :         {
     159         1173 :             err = CHIP_NO_ERROR;
     160              :         }
     161              : 
     162         1173 :         ReturnErrorOnFailure(err);
     163         1173 :         ReturnErrorOnFailure(apData->ExitContainer(outerContainer));
     164              :     }
     165         2287 :     else if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::AppendItem)
     166              :     {
     167         2287 :         ReturnErrorOnFailure(BufferListItem(*apData));
     168              :     }
     169              : 
     170         3460 :     return CHIP_NO_ERROR;
     171              : }
     172              : 
     173         5601 : CHIP_ERROR BufferedReadCallback::DispatchBufferedData(const ConcreteAttributePath & aPath, const StatusIB & aStatusIB,
     174              :                                                       bool aEndOfReport)
     175              : {
     176         5601 :     if (aPath == mBufferedPath)
     177              :     {
     178              :         //
     179              :         // If we encountered the same list again and it's not the last DataIB, then
     180              :         // we need to continue to buffer up this list's data, so return immediately without dispatching
     181              :         // the existing buffered up contents.
     182              :         //
     183         3203 :         if (!aEndOfReport)
     184              :         {
     185         2445 :             return CHIP_NO_ERROR;
     186              :         }
     187              : 
     188              :         //
     189              :         // If we had previously buffered up data for this list and now we have encountered
     190              :         // an error for this list, that error takes precedence and the buffered data is now
     191              :         // rendered invalid. Return immediately without dispatching the existing buffered up contents.
     192              :         //
     193          758 :         if (aStatusIB.mStatus != Protocols::InteractionModel::Status::Success)
     194              :         {
     195            0 :             return CHIP_NO_ERROR;
     196              :         }
     197              :     }
     198              : 
     199         3156 :     if (!mBufferedPath.IsListOperation())
     200              :     {
     201         2128 :         return CHIP_NO_ERROR;
     202              :     }
     203              : 
     204         1028 :     StatusIB statusIB;
     205         1028 :     TLV::ScopedBufferTLVReader reader;
     206              : 
     207         1028 :     ReturnErrorOnFailure(GenerateListTLV(reader));
     208              : 
     209              :     //
     210              :     // Update the list operation to now reflect the delivery of the entire list
     211              :     // i.e a replace all operation.
     212              :     //
     213         1028 :     mBufferedPath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
     214              : 
     215              :     //
     216              :     // Advance the reader forward to the list itself
     217              :     //
     218         1028 :     ReturnErrorOnFailure(reader.Next());
     219              : 
     220         1028 :     mCallback.OnAttributeData(mBufferedPath, &reader, statusIB);
     221              : 
     222              :     //
     223              :     // Clear out our buffered contents to free up allocated buffers, and reset the buffered path.
     224              :     //
     225         1028 :     mBufferedList.clear();
     226         1028 :     mBufferedPath = ConcreteDataAttributePath();
     227         1028 :     return CHIP_NO_ERROR;
     228         1028 : }
     229              : 
     230         4843 : void BufferedReadCallback::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
     231              :                                            const StatusIB & aStatus)
     232              : {
     233              :     CHIP_ERROR err;
     234              : 
     235              :     //
     236              :     // First, let's dispatch to our registered callback any buffered up list data from previous calls.
     237              :     //
     238         4843 :     err = DispatchBufferedData(aPath, aStatus);
     239         4843 :     SuccessOrExit(err);
     240              : 
     241              :     //
     242              :     // We buffer up list data (only if the status was successful)
     243              :     //
     244         4843 :     if (aPath.IsListOperation() && aStatus.mStatus == Protocols::InteractionModel::Status::Success)
     245              :     {
     246         3460 :         err = BufferData(aPath, apData);
     247         3460 :         SuccessOrExit(err);
     248              :     }
     249              :     else
     250              :     {
     251         1383 :         mCallback.OnAttributeData(aPath, apData, aStatus);
     252              :     }
     253              : 
     254              :     //
     255              :     // Update our latched buffered path.
     256              :     //
     257         4843 :     mBufferedPath = aPath;
     258              : 
     259         4843 : exit:
     260         4843 :     if (err != CHIP_NO_ERROR)
     261              :     {
     262            0 :         mCallback.OnError(err);
     263              :     }
     264         4843 : }
     265              : 
     266              : } // namespace app
     267              : } // namespace chip
        

Generated by: LCOV version 2.0-1