Matter SDK Coverage Report
Current view: top level - app - AttributeValueEncoder.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 100.0 % 42 42
Test Date: 2025-01-17 19:00:11 Functions: 100.0 % 14 14

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2021-2024 Project CHIP Authors
       3              :  *    All rights reserved.
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : #pragma once
      18              : 
      19              : #include <access/SubjectDescriptor.h>
      20              : #include <app/AttributeEncodeState.h>
      21              : #include <app/AttributeReportBuilder.h>
      22              : #include <app/ConcreteAttributePath.h>
      23              : #include <app/MessageDef/AttributeReportIBs.h>
      24              : #include <app/data-model/FabricScoped.h>
      25              : #include <app/data-model/List.h>
      26              : 
      27              : #include <type_traits>
      28              : 
      29              : namespace chip {
      30              : namespace app {
      31              : 
      32              : /**
      33              :  * The AttributeValueEncoder is a helper class for filling report payloads into AttributeReportIBs.
      34              :  * The attribute value encoder can be initialized with a AttributeEncodeState for saving and recovering its state between encode
      35              :  * sessions (chunkings).
      36              :  *
      37              :  * When Encode returns recoverable errors (e.g. CHIP_ERROR_NO_MEMORY) the state can be used to initialize the AttributeValueEncoder
      38              :  * for future use on the same attribute path.
      39              :  */
      40              : class AttributeValueEncoder
      41              : {
      42              : public:
      43              :     class ListEncodeHelper
      44              :     {
      45              :     public:
      46         1307 :         ListEncodeHelper(AttributeValueEncoder & encoder) : mAttributeValueEncoder(encoder) {}
      47              : 
      48              :         template <typename T, std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true>
      49              :         CHIP_ERROR Encode(T && aArg) const
      50              :         {
      51              :             VerifyOrReturnError(aArg.GetFabricIndex() != kUndefinedFabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
      52              : 
      53              :             // If we are encoding for a fabric filtered attribute read and the fabric index does not match that present in the
      54              :             // request, skip encoding this list item.
      55              :             VerifyOrReturnError(!mAttributeValueEncoder.mIsFabricFiltered ||
      56              :                                     aArg.GetFabricIndex() == mAttributeValueEncoder.AccessingFabricIndex(),
      57              :                                 CHIP_NO_ERROR);
      58              :             return mAttributeValueEncoder.EncodeListItem(mAttributeValueEncoder.AccessingFabricIndex(), std::forward<T>(aArg));
      59              :         }
      60              : 
      61              :         template <typename T, std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true>
      62         2086 :         CHIP_ERROR Encode(T && aArg) const
      63              :         {
      64         2086 :             return mAttributeValueEncoder.EncodeListItem(std::forward<T>(aArg));
      65              :         }
      66              : 
      67              :     private:
      68              :         AttributeValueEncoder & mAttributeValueEncoder;
      69              :     };
      70              : 
      71         2471 :     AttributeValueEncoder(AttributeReportIBs::Builder & aAttributeReportIBsBuilder, Access::SubjectDescriptor subjectDescriptor,
      72              :                           const ConcreteAttributePath & aPath, DataVersion aDataVersion, bool aIsFabricFiltered = false,
      73         2471 :                           const AttributeEncodeState & aState = AttributeEncodeState()) :
      74         2471 :         mAttributeReportIBsBuilder(aAttributeReportIBsBuilder),
      75         2471 :         mSubjectDescriptor(subjectDescriptor), mPath(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId),
      76         2471 :         mDataVersion(aDataVersion), mIsFabricFiltered(aIsFabricFiltered), mEncodeState(aState)
      77         2471 :     {}
      78              : 
      79              :     /**
      80              :      * Encode a single value.  This value will not be chunked; it will either be
      81              :      * entirely encoded or fail to be encoded.  Consumers are allowed to make
      82              :      * either one call to Encode or one call to EncodeList to handle a read.
      83              :      */
      84              :     template <typename... Ts>
      85           36 :     CHIP_ERROR Encode(Ts &&... aArgs)
      86              :     {
      87           36 :         mTriedEncode = true;
      88           36 :         return EncodeAttributeReportIB(std::forward<Ts>(aArgs)...);
      89              :     }
      90              : 
      91              :     /**
      92              :      * Encode an explicit null value.
      93              :      */
      94              :     CHIP_ERROR EncodeNull()
      95              :     {
      96              :         // Doesn't matter what type Nullable we use here.
      97              :         return Encode(DataModel::Nullable<uint8_t>());
      98              :     }
      99              : 
     100              :     /**
     101              :      * Encode an explicit empty list.
     102              :      */
     103              :     CHIP_ERROR EncodeEmptyList()
     104              :     {
     105              :         // Doesn't matter what type List we use here.
     106              :         return Encode(DataModel::List<uint8_t>());
     107              :     }
     108              : 
     109              :     /**
     110              :      * aCallback is expected to take a const auto & argument and Encode() on it as many times as needed to encode all the list
     111              :      * elements one by one.  If any of those Encode() calls returns failure, aCallback must stop encoding and return failure.  When
     112              :      * all items are encoded aCallback is expected to return success.
     113              :      *
     114              :      * aCallback may not be called.  Consumers must not assume it will be called.
     115              :      *
     116              :      * When EncodeList returns an error, the consumers must abort the encoding, and return the exact error to the caller.
     117              :      *
     118              :      * TODO: Can we hold a error state in the AttributeValueEncoder itself?
     119              :      *
     120              :      * Consumers are allowed to make either one call to EncodeList or one call to Encode to handle a read.
     121              :      *
     122              :      */
     123              :     template <typename ListGenerator>
     124          716 :     CHIP_ERROR EncodeList(ListGenerator aCallback)
     125              :     {
     126          716 :         mTriedEncode = true;
     127              :         // Spec 10.5.4.3.1, 10.5.4.6 (Replace a list w/ Multiple IBs)
     128              :         // EmptyList acts as the beginning of the whole array type attribute report.
     129              :         // An empty list is encoded iff both mCurrentEncodingListIndex and mEncodeState.mCurrentEncodingListIndex are invalid
     130              :         // values. After encoding the empty list, mEncodeState.mCurrentEncodingListIndex and mCurrentEncodingListIndex are set to 0.
     131          716 :         ReturnErrorOnFailure(EnsureListStarted());
     132          666 :         CHIP_ERROR err = aCallback(ListEncodeHelper(*this));
     133              : 
     134              :         // Even if encoding list items failed, make sure we EnsureListEnded().
     135              :         // Since we encode list items atomically, in the case when we just
     136              :         // didn't fit the next item we want to make sure our list is properly
     137              :         // ended before the reporting engine starts chunking.
     138          666 :         EnsureListEnded();
     139          666 :         if (err == CHIP_NO_ERROR)
     140              :         {
     141              :             // The Encode procedure finished without any error, clear the state.
     142          622 :             mEncodeState.Reset();
     143              :         }
     144          666 :         return err;
     145              :     }
     146              : 
     147         2247 :     bool TriedEncode() const { return mTriedEncode; }
     148              : 
     149              :     const Access::SubjectDescriptor & GetSubjectDescriptor() const { return mSubjectDescriptor; }
     150              : 
     151              :     /**
     152              :      * The accessing fabric index for this read or subscribe interaction.
     153              :      */
     154              :     FabricIndex AccessingFabricIndex() const { return GetSubjectDescriptor().fabricIndex; }
     155              : 
     156              :     /**
     157              :      * AttributeValueEncoder is a short lived object, and the state is persisted by mEncodeState and restored by constructor.
     158              :      */
     159          228 :     const AttributeEncodeState & GetState() const { return mEncodeState; }
     160              : 
     161              : private:
     162              :     // We made EncodeListItem() private, and ListEncoderHelper will expose it by Encode()
     163              :     friend class ListEncodeHelper;
     164              :     friend class TestOnlyAttributeValueEncoderAccessor;
     165              : 
     166              :     template <typename... Ts>
     167         2086 :     CHIP_ERROR EncodeListItem(Ts &&... aArgs)
     168              :     {
     169              :         // EncodeListItem must be called after EnsureListStarted(), thus mCurrentEncodingListIndex and
     170              :         // mEncodeState.mCurrentEncodingListIndex are not invalid values.
     171         2086 :         if (mCurrentEncodingListIndex < mEncodeState.CurrentEncodingListIndex())
     172              :         {
     173              :             // We have encoded this element in previous chunks, skip it.
     174          196 :             mCurrentEncodingListIndex++;
     175          196 :             return CHIP_NO_ERROR;
     176              :         }
     177              : 
     178         1890 :         TLV::TLVWriter backup;
     179         1890 :         mAttributeReportIBsBuilder.Checkpoint(backup);
     180              : 
     181              :         CHIP_ERROR err;
     182         1890 :         if (mEncodingInitialList)
     183              :         {
     184              :             // Just encode a single item, with an anonymous tag.
     185              :             AttributeReportBuilder builder;
     186         1726 :             err = builder.EncodeValue(mAttributeReportIBsBuilder, TLV::AnonymousTag(), std::forward<Ts>(aArgs)...);
     187              :         }
     188              :         else
     189              :         {
     190          164 :             err = EncodeAttributeReportIB(std::forward<Ts>(aArgs)...);
     191              :         }
     192         1890 :         if (err != CHIP_NO_ERROR)
     193              :         {
     194              :             // For list chunking, ReportEngine should not rollback the buffer when CHIP_ERROR_NO_MEMORY or similar error occurred.
     195              :             // However, the error might be raised in the middle of encoding procedure, then the buffer may contain partial data,
     196              :             // unclosed containers etc. This line clears all possible partial data and makes EncodeListItem is atomic.
     197           44 :             mAttributeReportIBsBuilder.Rollback(backup);
     198           44 :             return err;
     199              :         }
     200              : 
     201         1846 :         mCurrentEncodingListIndex++;
     202         1846 :         mEncodeState.SetCurrentEncodingListIndex(mCurrentEncodingListIndex);
     203         1846 :         mEncodedAtLeastOneListItem = true;
     204         1846 :         return CHIP_NO_ERROR;
     205              :     }
     206              : 
     207              :     /**
     208              :      * Builds a single AttributeReportIB in AttributeReportIBs.  The caller is
     209              :      * responsible for setting up mPath correctly.
     210              :      *
     211              :      * In particular, when we are encoding a single element in the list, mPath
     212              :      * must indicate a null list index to represent an "append" operation.
     213              :      * operation.
     214              :      */
     215              :     template <typename... Ts>
     216          200 :     CHIP_ERROR EncodeAttributeReportIB(Ts &&... aArgs)
     217              :     {
     218              :         AttributeReportBuilder builder;
     219          200 :         ReturnErrorOnFailure(builder.PrepareAttribute(mAttributeReportIBsBuilder, mPath, mDataVersion));
     220          200 :         ReturnErrorOnFailure(builder.EncodeValue(mAttributeReportIBsBuilder, TLV::ContextTag(AttributeDataIB::Tag::kData),
     221              :                                                  std::forward<Ts>(aArgs)...));
     222              : 
     223          200 :         return builder.FinishAttribute(mAttributeReportIBsBuilder);
     224              :     }
     225              : 
     226              :     /**
     227              :      * EnsureListStarted sets our mCurrentEncodingListIndex to 0, and:
     228              :      *
     229              :      * * If we are just starting the list, gets us ready to encode list items.
     230              :      *
     231              :      * * If we are continuing a chunked list, guarantees that mPath.mListOp is
     232              :      *   AppendItem after it returns.
     233              :      */
     234              :     CHIP_ERROR EnsureListStarted();
     235              : 
     236              :     /**
     237              :      * EnsureListEnded writes out the end of the list and our attribute data IB,
     238              :      * if we were encoding our initial list
     239              :      */
     240              :     void EnsureListEnded();
     241              : 
     242              :     AttributeReportIBs::Builder & mAttributeReportIBsBuilder;
     243              :     const Access::SubjectDescriptor mSubjectDescriptor;
     244              :     ConcreteDataAttributePath mPath;
     245              :     DataVersion mDataVersion;
     246              :     bool mTriedEncode      = false;
     247              :     bool mIsFabricFiltered = false;
     248              :     // mEncodingInitialList is true if we're encoding a list and we have not
     249              :     // started chunking it yet, so we're encoding a single attribute report IB
     250              :     // for the whole list, not one per item.
     251              :     bool mEncodingInitialList = false;
     252              :     // mEncodedAtLeastOneListItem becomes true once we successfully encode a list item.
     253              :     bool mEncodedAtLeastOneListItem     = false;
     254              :     ListIndex mCurrentEncodingListIndex = kInvalidListIndex;
     255              :     AttributeEncodeState mEncodeState;
     256              : };
     257              : 
     258              : } // namespace app
     259              : } // namespace chip
        

Generated by: LCOV version 2.0-1