LCOV - code coverage report
Current view: top level - app/data-model - DecodableList.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 0 35 0.0 %
Date: 2024-02-15 08:20:41 Functions: 0 21 0.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020-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             : #pragma once
      20             : 
      21             : #include <app/data-model/Decode.h>
      22             : #include <app/data-model/Encode.h>
      23             : #include <app/data-model/FabricScoped.h>
      24             : #include <lib/core/TLV.h>
      25             : 
      26             : namespace chip {
      27             : namespace app {
      28             : namespace DataModel {
      29             : 
      30             : /*
      31             :  * @brief
      32             :  *
      33             :  * This class provides an iteratable decoder of list items within TLV payloads
      34             :  * such that no memory has to be provided ahead of time to store the entirety of the decoded
      35             :  * list contents.
      36             :  *
      37             :  * Typical use of a DecodableList looks like this:
      38             :  *
      39             :  *    auto iter = list.begin();
      40             :  *    while (iter.Next()) {
      41             :  *        auto & entry = iter.GetValue();
      42             :  *        // Do whatever with entry
      43             :  *    }
      44             :  *    CHIP_ERROR err = iter.GetStatus();
      45             :  *    // If err is failure, decoding failed somewhere along the way.  Some valid
      46             :  *    // entries may have been processed already.
      47             :  *
      48             :  */
      49             : template <typename T>
      50             : class DecodableList
      51             : {
      52             : public:
      53           0 :     DecodableList() { ClearReader(); }
      54             : 
      55             :     static constexpr bool kIsFabricScoped = DataModel::IsFabricScoped<T>::value;
      56             : 
      57             :     /*
      58             :      * @brief
      59             :      *
      60             :      * This call stores a TLV reader positioned on the list this class is to manage.
      61             :      *
      62             :      * Specifically, the passed-in reader should be pointing into the list just after
      63             :      * having called `OpenContainer` on the list element.
      64             :      */
      65           0 :     void SetReader(const TLV::TLVReader & reader) { mReader = reader; }
      66             : 
      67             :     /*
      68             :      * @brief
      69             :      *
      70             :      * This call clears the TLV reader managed by this class, so it can be reused.
      71             :      */
      72           0 :     void ClearReader() { mReader.Init(nullptr, 0); }
      73             : 
      74             :     template <typename T0 = T, std::enable_if_t<DataModel::IsFabricScoped<T0>::value, bool> = true>
      75             :     void SetFabricIndex(FabricIndex fabricIndex)
      76             :     {
      77             :         mFabricIndex.SetValue(fabricIndex);
      78             :     }
      79             : 
      80             :     class Iterator
      81             :     {
      82             :     public:
      83             :         /*
      84             :          * Initialize the iterator with a reference to a reader.
      85             :          *
      86             :          * This reader should be pointing into the list just after
      87             :          * having called `OpenContainer` on the list element, or should
      88             :          * have a `kTLVType_NotSpecified` container type if there is
      89             :          * no list.
      90             :          */
      91           0 :         Iterator(const TLV::TLVReader & reader, Optional<FabricIndex> fabricIndex) : mFabricIndex(fabricIndex)
      92             :         {
      93           0 :             mStatus = CHIP_NO_ERROR;
      94           0 :             mReader.Init(reader);
      95           0 :         }
      96             : 
      97             :         /*
      98             :          * Increments the iterator to point to the next list element
      99             :          * if a valid one exists, and decodes the list element into
     100             :          * the internal value storage.
     101             :          *
     102             :          * If an element does exist and was successfully decoded, this
     103             :          * shall return true.
     104             :          *
     105             :          * Otherwise, if the end of list is reached, or there was no list,
     106             :          * this call shall return false.
     107             :          *
     108             :          * If an error was encountered at any point during the iteration or decode,
     109             :          * this shall return false as well. The caller is expected to invoke GetStatus()
     110             :          * to retrieve the status of the operation.
     111             :          */
     112             :         template <typename T0 = T, std::enable_if_t<!DataModel::IsFabricScoped<T0>::value, bool> = true>
     113           0 :         bool Next()
     114             :         {
     115           0 :             return DoNext();
     116             :         }
     117             : 
     118             :         template <typename T0 = T, std::enable_if_t<DataModel::IsFabricScoped<T0>::value, bool> = true>
     119           0 :         bool Next()
     120             :         {
     121           0 :             bool hasNext = DoNext();
     122             : 
     123           0 :             if (hasNext && mFabricIndex.HasValue())
     124             :             {
     125           0 :                 mValue.SetFabricIndex(mFabricIndex.Value());
     126             :             }
     127             : 
     128           0 :             return hasNext;
     129             :         }
     130             : 
     131             :         /*
     132             :          * Retrieves a reference to the decoded value, if one
     133             :          * was decoded on a previous call to Next().
     134             :          */
     135           0 :         const T & GetValue() const { return mValue; }
     136             : 
     137             :         /*
     138             :          * Returns the result of all previous operations on this iterator.
     139             :          *
     140             :          * Notably, if the end-of-list was encountered in a previous call to Next,
     141             :          * the status returned shall be CHIP_NO_ERROR.
     142             :          */
     143           0 :         CHIP_ERROR GetStatus() const
     144             :         {
     145           0 :             if (mStatus == CHIP_END_OF_TLV)
     146             :             {
     147           0 :                 return CHIP_NO_ERROR;
     148             :             }
     149             : 
     150           0 :             return mStatus;
     151             :         }
     152             : 
     153             :     private:
     154           0 :         bool DoNext()
     155             :         {
     156           0 :             if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
     157             :             {
     158           0 :                 return false;
     159             :             }
     160             : 
     161           0 :             if (mStatus == CHIP_NO_ERROR)
     162             :             {
     163           0 :                 mStatus = mReader.Next();
     164             :             }
     165             : 
     166           0 :             if (mStatus == CHIP_NO_ERROR)
     167             :             {
     168             :                 //
     169             :                 // Re-construct mValue to reset its state back to cluster object defaults.
     170             :                 // This is especially important when decoding successive list elements
     171             :                 // that do not contain all of the fields for a given struct because
     172             :                 // they are marked optional/fabric-sensitive. Without this re-construction,
     173             :                 // data from previous decode attempts will continue to linger and give
     174             :                 // an incorrect view of the state as seen from a client.
     175             :                 //
     176           0 :                 mValue  = T();
     177           0 :                 mStatus = DataModel::Decode(mReader, mValue);
     178             :             }
     179             : 
     180           0 :             return (mStatus == CHIP_NO_ERROR);
     181             :         }
     182             : 
     183             :         T mValue;
     184             :         CHIP_ERROR mStatus;
     185             :         TLV::TLVReader mReader;
     186             :         // TODO: Consider some setup where this field does not exist when T
     187             :         // is not a fabric scoped struct.
     188             :         const Optional<FabricIndex> mFabricIndex;
     189             :     };
     190             : 
     191           0 :     Iterator begin() const { return Iterator(mReader, mFabricIndex); }
     192             : 
     193             :     /*
     194             :      * Compute the size of the list. This can fail if the TLV is malformed. If
     195             :      * this succeeds, that does not guarantee that the individual items can be
     196             :      * successfully decoded; consumers should check Iterator::GetStatus() when
     197             :      * actually decoding them. If there is no list then the size is considered
     198             :      * to be zero.
     199             :      */
     200             :     CHIP_ERROR ComputeSize(size_t * size) const
     201             :     {
     202             :         if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
     203             :         {
     204             :             *size = 0;
     205             :             return CHIP_NO_ERROR;
     206             :         }
     207             : 
     208             :         return mReader.CountRemainingInContainer(size);
     209             :     }
     210             : 
     211           0 :     CHIP_ERROR Decode(TLV::TLVReader & reader)
     212             :     {
     213           0 :         VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Array, CHIP_ERROR_SCHEMA_MISMATCH);
     214             :         TLV::TLVType type;
     215           0 :         ReturnErrorOnFailure(reader.EnterContainer(type));
     216           0 :         SetReader(reader);
     217           0 :         ReturnErrorOnFailure(reader.ExitContainer(type));
     218           0 :         return CHIP_NO_ERROR;
     219             :     }
     220             : 
     221             : private:
     222             :     TLV::TLVReader mReader;
     223             :     chip::Optional<FabricIndex> mFabricIndex;
     224             : };
     225             : 
     226             : } // namespace DataModel
     227             : } // namespace app
     228             : } // namespace chip

Generated by: LCOV version 1.14