Matter SDK Coverage Report
Current view: top level - app/data-model - DecodableList.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 0.0 % 52 0
Test Date: 2025-01-17 19:00:11 Functions: 0.0 % 26 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              : namespace detail {
      31              : 
      32              : /*
      33              :  * Base class of DecodableList to minimize template usage
      34              :  */
      35              : class DecodableListBase
      36              : {
      37              : public:
      38            0 :     DecodableListBase() { ClearReader(); }
      39              : 
      40              :     /*
      41              :      * @brief
      42              :      *
      43              :      * This call stores a TLV reader positioned on the list this class is to manage.
      44              :      *
      45              :      * Specifically, the passed-in reader should be pointing into the list just after
      46              :      * having called `OpenContainer` on the list element.
      47              :      */
      48            0 :     void SetReader(const TLV::TLVReader & reader) { mReader = reader; }
      49              : 
      50              :     /*
      51              :      * @brief
      52              :      *
      53              :      * This call clears the TLV reader managed by this class, so it can be reused.
      54              :      */
      55            0 :     void ClearReader() { mReader.Init(nullptr, 0); }
      56              : 
      57              :     /*
      58              :      * Compute the size of the list. This can fail if the TLV is malformed. If
      59              :      * this succeeds, that does not guarantee that the individual items can be
      60              :      * successfully decoded; consumers should check Iterator::GetStatus() when
      61              :      * actually decoding them. If there is no list then the size is considered
      62              :      * to be zero.
      63              :      */
      64              :     CHIP_ERROR ComputeSize(size_t * size) const
      65              :     {
      66              :         if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
      67              :         {
      68              :             *size = 0;
      69              :             return CHIP_NO_ERROR;
      70              :         }
      71              : 
      72              :         return mReader.CountRemainingInContainer(size);
      73              :     }
      74              : 
      75            0 :     CHIP_ERROR Decode(TLV::TLVReader & reader)
      76              :     {
      77            0 :         VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Array, CHIP_ERROR_SCHEMA_MISMATCH);
      78              :         TLV::TLVType type;
      79            0 :         ReturnErrorOnFailure(reader.EnterContainer(type));
      80            0 :         SetReader(reader);
      81            0 :         ReturnErrorOnFailure(reader.ExitContainer(type));
      82            0 :         return CHIP_NO_ERROR;
      83              :     }
      84              : 
      85              : protected:
      86              :     class Iterator
      87              :     {
      88              :     public:
      89            0 :         Iterator(const TLV::TLVReader & reader)
      90            0 :         {
      91            0 :             mStatus = CHIP_NO_ERROR;
      92            0 :             mReader.Init(reader);
      93            0 :         }
      94              : 
      95            0 :         bool Next()
      96              :         {
      97            0 :             if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
      98              :             {
      99            0 :                 return false;
     100              :             }
     101              : 
     102            0 :             if (mStatus == CHIP_NO_ERROR)
     103              :             {
     104            0 :                 mStatus = mReader.Next();
     105              :             }
     106              : 
     107            0 :             return (mStatus == CHIP_NO_ERROR);
     108              :         }
     109              : 
     110              :         /*
     111              :          * Returns the result of all previous operations on this iterator.
     112              :          *
     113              :          * Notably, if the end-of-list was encountered in a previous call to Next,
     114              :          * the status returned shall be CHIP_NO_ERROR.
     115              :          */
     116            0 :         CHIP_ERROR GetStatus() const
     117              :         {
     118            0 :             if (mStatus == CHIP_END_OF_TLV)
     119              :             {
     120            0 :                 return CHIP_NO_ERROR;
     121              :             }
     122              : 
     123            0 :             return mStatus;
     124              :         }
     125              : 
     126              :     protected:
     127              :         CHIP_ERROR mStatus;
     128              :         TLV::TLVReader mReader;
     129              :     };
     130              : 
     131              :     TLV::TLVReader mReader;
     132              : };
     133              : 
     134              : template <bool IsFabricScoped>
     135              : class FabricIndexListMemberMixin
     136              : {
     137              : };
     138              : 
     139              : template <>
     140              : class FabricIndexListMemberMixin<true>
     141              : {
     142              : public:
     143              :     void SetFabricIndex(FabricIndex fabricIndex) { mFabricIndex.SetValue(fabricIndex); }
     144              : 
     145              : protected:
     146              :     Optional<FabricIndex> mFabricIndex;
     147              : };
     148              : 
     149              : template <bool IsFabricScoped>
     150              : class FabricIndexIteratorMemberMixin
     151              : {
     152              : };
     153              : 
     154              : template <>
     155              : class FabricIndexIteratorMemberMixin<true>
     156              : {
     157              : public:
     158            0 :     FabricIndexIteratorMemberMixin(const Optional<FabricIndex> & fabricindex) : mFabricIndex(fabricindex) {}
     159              : 
     160              : protected:
     161              :     const Optional<FabricIndex> mFabricIndex;
     162              : };
     163              : 
     164              : template <bool IsFabricScoped>
     165              : class DecodableMaybeFabricScopedList : public DecodableListBase, public FabricIndexListMemberMixin<IsFabricScoped>
     166              : {
     167              : public:
     168              :     static constexpr bool kIsFabricScoped = IsFabricScoped;
     169              : 
     170              : protected:
     171              :     class Iterator : public DecodableListBase::Iterator, public FabricIndexIteratorMemberMixin<IsFabricScoped>
     172              :     {
     173              :     public:
     174              :         template <bool IsActuallyFabricScoped = IsFabricScoped, std::enable_if_t<IsActuallyFabricScoped, bool> = true>
     175            0 :         Iterator(const TLV::TLVReader & reader, const Optional<FabricIndex> & fabricIndex) :
     176            0 :             DecodableListBase::Iterator(reader), FabricIndexIteratorMemberMixin<IsFabricScoped>(fabricIndex)
     177            0 :         {}
     178              : 
     179              :         template <bool IsActuallyFabricScoped = IsFabricScoped, std::enable_if_t<!IsActuallyFabricScoped, bool> = true>
     180            0 :         Iterator(const TLV::TLVReader & reader) : DecodableListBase::Iterator(reader)
     181            0 :         {}
     182              :     };
     183              : };
     184              : 
     185              : } // namespace detail
     186              : 
     187              : /*
     188              :  * @brief
     189              :  *
     190              :  * This class provides an iteratable decoder of list items within TLV payloads
     191              :  * such that no memory has to be provided ahead of time to store the entirety of the decoded
     192              :  * list contents.
     193              :  *
     194              :  * Typical use of a DecodableList looks like this:
     195              :  *
     196              :  *    auto iter = list.begin();
     197              :  *    while (iter.Next()) {
     198              :  *        auto & entry = iter.GetValue();
     199              :  *        // Do whatever with entry
     200              :  *    }
     201              :  *    CHIP_ERROR err = iter.GetStatus();
     202              :  *    // If err is failure, decoding failed somewhere along the way.  Some valid
     203              :  *    // entries may have been processed already.
     204              :  *
     205              :  */
     206              : template <typename T>
     207              : class DecodableList : public detail::DecodableMaybeFabricScopedList<DataModel::IsFabricScoped<T>::value>
     208              : {
     209              : public:
     210            0 :     DecodableList() {}
     211              : 
     212              :     class Iterator : public detail::DecodableMaybeFabricScopedList<DataModel::IsFabricScoped<T>::value>::Iterator
     213              :     {
     214              :         using IteratorBase = typename detail::DecodableMaybeFabricScopedList<DataModel::IsFabricScoped<T>::value>::Iterator;
     215              : 
     216              :     public:
     217              :         /*
     218              :          * Initialize the iterator with a reference to a reader.
     219              :          *
     220              :          * This reader should be pointing into the list just after
     221              :          * having called `OpenContainer` on the list element, or should
     222              :          * have a `kTLVType_NotSpecified` container type if there is
     223              :          * no list.
     224              :          */
     225              :         template <typename T0 = T, std::enable_if_t<DataModel::IsFabricScoped<T0>::value, bool> = true>
     226            0 :         Iterator(const TLV::TLVReader & reader, Optional<FabricIndex> fabricIndex) : IteratorBase(reader, fabricIndex)
     227            0 :         {}
     228              : 
     229              :         template <typename T0 = T, std::enable_if_t<!DataModel::IsFabricScoped<T0>::value, bool> = true>
     230            0 :         Iterator(const TLV::TLVReader & reader) : IteratorBase(reader)
     231            0 :         {}
     232              : 
     233              :         /*
     234              :          * Increments the iterator to point to the next list element
     235              :          * if a valid one exists, and decodes the list element into
     236              :          * the internal value storage.
     237              :          *
     238              :          * If an element does exist and was successfully decoded, this
     239              :          * shall return true.
     240              :          *
     241              :          * Otherwise, if the end of list is reached, or there was no list,
     242              :          * this call shall return false.
     243              :          *
     244              :          * If an error was encountered at any point during the iteration or decode,
     245              :          * this shall return false as well. The caller is expected to invoke GetStatus()
     246              :          * to retrieve the status of the operation.
     247              :          */
     248              :         template <typename T0 = T, std::enable_if_t<!DataModel::IsFabricScoped<T0>::value, bool> = true>
     249            0 :         bool Next()
     250              :         {
     251            0 :             return IteratorBase::Next() && DecodeValue();
     252              :         }
     253              : 
     254              :         template <typename T0 = T, std::enable_if_t<DataModel::IsFabricScoped<T0>::value, bool> = true>
     255            0 :         bool Next()
     256              :         {
     257            0 :             bool hasNext = IteratorBase::Next() && DecodeValue();
     258              : 
     259            0 :             if (hasNext && this->mFabricIndex.HasValue())
     260              :             {
     261            0 :                 mValue.SetFabricIndex(this->mFabricIndex.Value());
     262              :             }
     263              : 
     264            0 :             return hasNext;
     265              :         }
     266              : 
     267              :         /*
     268              :          * Retrieves a reference to the decoded value, if one
     269              :          * was decoded on a previous call to Next().
     270              :          */
     271            0 :         const T & GetValue() const { return mValue; }
     272              : 
     273              :     private:
     274            0 :         bool DecodeValue()
     275              :         {
     276            0 :             if (this->mStatus == CHIP_NO_ERROR)
     277              :             {
     278              :                 //
     279              :                 // Re-construct mValue to reset its state back to cluster object defaults.
     280              :                 // This is especially important when decoding successive list elements
     281              :                 // that do not contain all of the fields for a given struct because
     282              :                 // they are marked optional/fabric-sensitive. Without this re-construction,
     283              :                 // data from previous decode attempts will continue to linger and give
     284              :                 // an incorrect view of the state as seen from a client.
     285              :                 //
     286            0 :                 mValue        = T();
     287            0 :                 this->mStatus = DataModel::Decode(this->mReader, mValue);
     288              :             }
     289              : 
     290            0 :             return (this->mStatus == CHIP_NO_ERROR);
     291              :         }
     292              : 
     293              :         T mValue;
     294              :     };
     295              : 
     296              :     // Need this->mReader and this->mFabricIndex for the name lookup to realize
     297              :     // those can be found in our superclasses.
     298              :     template <typename T0 = T, std::enable_if_t<DataModel::IsFabricScoped<T0>::value, bool> = true>
     299            0 :     Iterator begin() const
     300              :     {
     301            0 :         return Iterator(this->mReader, this->mFabricIndex);
     302              :     }
     303              : 
     304              :     template <typename T0 = T, std::enable_if_t<!DataModel::IsFabricScoped<T0>::value, bool> = true>
     305            0 :     Iterator begin() const
     306              :     {
     307            0 :         return Iterator(this->mReader);
     308              :     }
     309              : };
     310              : 
     311              : } // namespace DataModel
     312              : } // namespace app
     313              : } // namespace chip
        

Generated by: LCOV version 2.0-1