LCOV - code coverage report
Current view: top level - lib/core - TLVCircularBuffer.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 76 77 98.7 %
Date: 2024-02-15 08:20:41 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020-2021 Project CHIP Authors
       4             :  *    Copyright (c) 2016-2017 Nest Labs, Inc.
       5             :  *    All rights reserved.
       6             :  *
       7             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       8             :  *    you may not use this file except in compliance with the License.
       9             :  *    You may obtain a copy of the License at
      10             :  *
      11             :  *        http://www.apache.org/licenses/LICENSE-2.0
      12             :  *
      13             :  *    Unless required by applicable law or agreed to in writing, software
      14             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      15             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16             :  *    See the License for the specific language governing permissions and
      17             :  *    limitations under the License.
      18             :  */
      19             : 
      20             : /**
      21             :  *    @file
      22             :  *      This file implements the circular buffer for TLV
      23             :  *      elements. When used as the backing store for the TLVReader and
      24             :  *      TLVWriter, those classes will work with the wraparound of data
      25             :  *      within the buffer.  Additionally, the TLVWriter will be able
      26             :  *      to continually add top-level TLV elements by evicting
      27             :  *      pre-existing elements.
      28             :  */
      29             : #include <lib/core/TLVCircularBuffer.h>
      30             : 
      31             : #ifndef __STDC_LIMIT_MACROS
      32             : #define __STDC_LIMIT_MACROS
      33             : #endif
      34             : 
      35             : #include <lib/core/CHIPError.h>
      36             : #include <lib/core/TLVReader.h>
      37             : #include <lib/core/TLVTags.h>
      38             : #include <lib/core/TLVWriter.h>
      39             : #include <lib/support/BufferWriter.h>
      40             : #include <lib/support/CodeUtils.h>
      41             : 
      42             : #include <stdint.h>
      43             : 
      44             : namespace chip {
      45             : namespace TLV {
      46             : 
      47             : using namespace chip::Encoding;
      48             : 
      49             : /**
      50             :  * @brief
      51             :  *   TLVCircularBuffer constructor
      52             :  *
      53             :  * @param[in] inBuffer       A pointer to the backing store for the queue
      54             :  *
      55             :  * @param[in] inBufferLength Length, in bytes, of the backing store
      56             :  *
      57             :  * @param[in] inHead         Initial point for the head.  The @a inHead pointer is must fall within the backing store for the
      58             :  * circular buffer, i.e. within @a inBuffer and &(@a inBuffer[@a inBufferLength])
      59             :  */
      60           1 : TLVCircularBuffer::TLVCircularBuffer(uint8_t * inBuffer, uint32_t inBufferLength, uint8_t * inHead)
      61             : {
      62           1 :     mQueue       = inBuffer;
      63           1 :     mQueueSize   = inBufferLength;
      64           1 :     mQueueLength = 0;
      65           1 :     mQueueHead   = inHead;
      66             : 
      67           1 :     mProcessEvictedElement = nullptr;
      68           1 :     mAppData               = nullptr;
      69             : 
      70             :     // use common as opposed to unspecified, s.t. the reader that
      71             :     // skips over the elements does not complain about implicit
      72             :     // profile tags.
      73           1 :     mImplicitProfileId = kCommonProfileId;
      74           1 : }
      75             : 
      76             : /**
      77             :  * @brief
      78             :  *   TLVCircularBuffer constructor
      79             :  *
      80             :  * @param[in] inBuffer       A pointer to the backing store for the queue
      81             :  *
      82             :  * @param[in] inBufferLength Length, in bytes, of the backing store
      83             :  */
      84        1072 : TLVCircularBuffer::TLVCircularBuffer(uint8_t * inBuffer, uint32_t inBufferLength)
      85             : {
      86        1072 :     Init(inBuffer, inBufferLength);
      87        1072 : }
      88             : 
      89             : /**
      90             :  * @brief
      91             :  *   TLVCircularBuffer Init function
      92             :  *
      93             :  * @param[in] inBuffer       A pointer to the backing store for the queue
      94             :  *
      95             :  * @param[in] inBufferLength Length, in bytes, of the backing store
      96             :  */
      97        1441 : void TLVCircularBuffer::Init(uint8_t * inBuffer, uint32_t inBufferLength)
      98             : {
      99        1441 :     mQueue       = inBuffer;
     100        1441 :     mQueueSize   = inBufferLength;
     101        1441 :     mQueueLength = 0;
     102        1441 :     mQueueHead   = mQueue;
     103             : 
     104        1441 :     mProcessEvictedElement = nullptr;
     105        1441 :     mAppData               = nullptr;
     106             : 
     107             :     // use common as opposed to unspecified, s.t. the reader that
     108             :     // skips over the elements does not complain about implicit
     109             :     // profile tags.
     110        1441 :     mImplicitProfileId = kCommonProfileId;
     111        1441 : }
     112             : 
     113             : /**
     114             :  * @brief
     115             :  *   Evicts the oldest top-level TLV element in the TLVCircularBuffer
     116             :  *
     117             :  * This function removes the oldest top level TLV element in the
     118             :  * buffer.  The function will call the callback registered at
     119             :  * #mProcessEvictedElement to process the element prior to removal.
     120             :  * If the callback returns anything but #CHIP_NO_ERROR, the element
     121             :  * is not removed.  Similarly, if any other error occurs -- no
     122             :  * elements within the buffer, etc -- the underlying
     123             :  * #TLVCircularBuffer remains unchanged.
     124             :  *
     125             :  *  @retval #CHIP_NO_ERROR On success.
     126             :  *
     127             :  *  @retval other          On any other error returned either by the callback
     128             :  *                         or by the TLVReader.
     129             :  *
     130             :  */
     131        1545 : CHIP_ERROR TLVCircularBuffer::EvictHead()
     132             : {
     133             :     CircularTLVReader reader;
     134             :     uint8_t * newHead;
     135             :     uint32_t newLen;
     136             : 
     137             :     // find the boundaries of an event to throw away
     138        1545 :     reader.Init(*this);
     139        1545 :     reader.ImplicitProfileId = mImplicitProfileId;
     140             : 
     141             :     // position the reader on the first element
     142        1545 :     ReturnErrorOnFailure(reader.Next());
     143             : 
     144             :     // skip to the next element
     145        1545 :     ReturnErrorOnFailure(reader.Skip());
     146             : 
     147             :     // record the state of the queue post-call
     148        1544 :     newLen  = mQueueLength - (reader.GetLengthRead());
     149        1544 :     newHead = const_cast<uint8_t *>(reader.GetReadPoint());
     150             : 
     151             :     // if a custom handler is installed, give it a chance to
     152             :     // process the element before we evict it from the buffer.
     153        1544 :     if (mProcessEvictedElement != nullptr)
     154             :     {
     155             :         // Reinitialize the reader
     156        1116 :         reader.Init(*this);
     157        1116 :         reader.ImplicitProfileId = mImplicitProfileId;
     158             : 
     159        1116 :         ReturnErrorOnFailure(mProcessEvictedElement(*this, mAppData, reader));
     160             :     }
     161             : 
     162             :     // update queue state
     163         809 :     mQueueLength = newLen;
     164         809 :     mQueueHead   = newHead;
     165             : 
     166         809 :     return CHIP_NO_ERROR;
     167             : }
     168             : 
     169             : /**
     170             :  * @brief
     171             :  *  Implements TLVBackingStore::OnInit(TLVWriter) for circular buffers.
     172             :  */
     173          10 : CHIP_ERROR TLVCircularBuffer::OnInit(TLVWriter & writer, uint8_t *& bufStart, uint32_t & bufLen)
     174             : {
     175          10 :     return GetNewBuffer(writer, bufStart, bufLen);
     176             : }
     177             : 
     178             : /**
     179             :  * @brief
     180             :  *   Get additional space for the TLVWriter.  In actuality, the
     181             :  *   function evicts an element from the circular buffer, and adjusts
     182             :  *   the head of this buffer queue
     183             :  *
     184             :  * @param[in,out] ioWriter  TLVWriter calling this function
     185             :  *
     186             :  * @param[out] outBufStart The pointer to the new buffer
     187             :  *
     188             :  * @param[out] outBufLen   The available length for writing
     189             :  *
     190             :  * @retval #CHIP_NO_ERROR On success.
     191             :  *
     192             :  * @retval other           If the function was unable to elide a complete
     193             :  *                         top-level TLV element.
     194             :  */
     195             : 
     196         508 : CHIP_ERROR TLVCircularBuffer::GetNewBuffer(TLVWriter & ioWriter, uint8_t *& outBufStart, uint32_t & outBufLen)
     197             : {
     198         508 :     if (mQueueLength >= mQueueSize)
     199             :     {
     200             :         // Queue is out of space, need to evict an element
     201          18 :         ReturnErrorOnFailure(EvictHead());
     202             :     }
     203             : 
     204         507 :     GetCurrentWritableBuffer(outBufStart, outBufLen);
     205         507 :     return CHIP_NO_ERROR;
     206             : }
     207             : 
     208        1591 : void TLVCircularBuffer::GetCurrentWritableBuffer(uint8_t *& outBufStart, uint32_t & outBufLen) const
     209             : {
     210        1591 :     uint8_t * tail = QueueTail();
     211             : 
     212             :     // set the output values, returned buffer must be contiguous
     213        1591 :     outBufStart = tail;
     214             : 
     215        1591 :     if (tail >= mQueueHead)
     216             :     {
     217         351 :         outBufLen = mQueueSize - static_cast<uint32_t>(tail - mQueue);
     218             :     }
     219             :     else
     220             :     {
     221        1240 :         outBufLen = static_cast<uint32_t>(mQueueHead - tail);
     222             :     }
     223        1591 : }
     224             : 
     225             : /**
     226             :  * @brief
     227             :  *   FinalizeBuffer adjust the `TLVCircularBuffer` state on
     228             :  *   completion of output from the TLVWriter.  This function affects
     229             :  *   the position of the queue tail.
     230             :  *
     231             :  * @param[in,out] ioWriter TLVWriter calling this function
     232             :  *
     233             :  * @param[in] inBufStart pointer to the start of data (from `TLVWriter`
     234             :  *                       perspective)
     235             :  *
     236             :  * @param[in] inBufLen   length of data in the buffer pointed to by
     237             :  *                       `inbufStart`
     238             :  *
     239             :  * @retval #CHIP_NO_ERROR Unconditionally.
     240             :  */
     241             : 
     242        1605 : CHIP_ERROR TLVCircularBuffer::FinalizeBuffer(TLVWriter & ioWriter, uint8_t * inBufStart, uint32_t inBufLen)
     243             : {
     244        1605 :     CHIP_ERROR err = CHIP_NO_ERROR;
     245        1605 :     uint8_t * tail = inBufStart + inBufLen;
     246        1605 :     if (inBufLen)
     247             :     {
     248        1605 :         if (tail <= mQueueHead)
     249             :         {
     250        1242 :             mQueueLength = mQueueSize - static_cast<uint32_t>(mQueueHead - tail);
     251             :         }
     252             :         else
     253             :         {
     254         363 :             mQueueLength = static_cast<uint32_t>(tail - mQueueHead);
     255             :         }
     256             :     }
     257        1605 :     return err;
     258             : }
     259             : 
     260             : /**
     261             :  * @brief
     262             :  *  Implements TLVBackingStore::OnInit(TVLReader) for circular buffers.
     263             :  */
     264        4041 : CHIP_ERROR TLVCircularBuffer::OnInit(TLVReader & reader, const uint8_t *& bufStart, uint32_t & bufLen)
     265             : {
     266        4041 :     return GetNextBuffer(reader, bufStart, bufLen);
     267             : }
     268             : 
     269             : /**
     270             :  * @brief
     271             :  *   Get additional space for the TLVReader.
     272             :  *
     273             :  *  The storage provided by the TLVCircularBuffer may be
     274             :  *  wraparound within the buffer.  This function provides us with an
     275             :  *  ability to match the buffering of the circular buffer to the
     276             :  *  TLVReader constraints.  The reader will read at most `mQueueSize`
     277             :  *  bytes from the buffer.
     278             :  *
     279             :  * @param[in] ioReader         TLVReader calling this function.
     280             :  *
     281             :  * @param[in,out] outBufStart  The reference to the data buffer.  On
     282             :  *                             return, it is set to a value within this
     283             :  *                             buffer.
     284             :  *
     285             :  * @param[out] outBufLen       On return, set to the number of continuous
     286             :  *                             bytes that could be read out of the buffer.
     287             :  *
     288             :  * @retval #CHIP_NO_ERROR      Succeeds unconditionally.
     289             :  */
     290        6141 : CHIP_ERROR TLVCircularBuffer::GetNextBuffer(TLVReader & ioReader, const uint8_t *& outBufStart, uint32_t & outBufLen)
     291             : {
     292        6141 :     CHIP_ERROR err              = CHIP_NO_ERROR;
     293        6141 :     uint8_t * tail              = QueueTail();
     294        6141 :     const uint8_t * readerStart = outBufStart;
     295             : 
     296        6141 :     if (readerStart == nullptr)
     297             :     {
     298        5855 :         outBufStart = mQueueHead;
     299             : 
     300        5855 :         if (outBufStart == mQueue + mQueueSize)
     301             :         {
     302          10 :             outBufStart = mQueue;
     303             :         }
     304             :     }
     305         286 :     else if (readerStart >= (mQueue + mQueueSize))
     306             :     {
     307         238 :         outBufStart = mQueue;
     308             :     }
     309             :     else
     310             :     {
     311          48 :         outBufLen = 0;
     312          48 :         return err;
     313             :     }
     314             : 
     315        6093 :     if ((mQueueLength != 0) && (tail <= outBufStart))
     316             :     {
     317             :         // the buffer is non-empty and data wraps around the end
     318             :         // point.  The returned buffer conceptually spans from
     319             :         // outBufStart until the end of the underlying storage buffer
     320             :         // (i.e. mQueue+mQueueSize).  This case tail == outBufStart
     321             :         // indicates that the buffer is completely full
     322        3077 :         outBufLen = mQueueSize - static_cast<uint32_t>(outBufStart - mQueue);
     323        3077 :         if ((tail == outBufStart) && (readerStart != nullptr))
     324           0 :             outBufLen = 0;
     325             :     }
     326             :     else
     327             :     {
     328             :         // the buffer length is the distance between head and tail;
     329             :         // tail is either strictly larger or the buffer is empty
     330        3016 :         outBufLen = static_cast<uint32_t>(tail - outBufStart);
     331             :     }
     332        6093 :     return err;
     333             : }
     334             : 
     335             : } // namespace TLV
     336             : } // namespace chip

Generated by: LCOV version 1.14