LCOV - code coverage report
Current view: top level - app - BufferedReadCallback.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 82 86 95.3 %
Date: 2024-02-15 08:20:41 Functions: 7 7 100.0 %

          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 :     uint32_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 1.14