LCOV - code coverage report
Current view: top level - app - AttributePathExpandIterator.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 86 86 100.0 %
Date: 2024-02-15 08:20:41 Functions: 6 6 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 <app/AttributePathExpandIterator.h>
      20             : 
      21             : #include <app/AttributePathParams.h>
      22             : #include <app/ConcreteAttributePath.h>
      23             : #include <app/EventManagement.h>
      24             : #include <app/GlobalAttributes.h>
      25             : #include <app/att-storage.h>
      26             : #include <lib/core/CHIPCore.h>
      27             : #include <lib/core/TLVDebug.h>
      28             : #include <lib/support/CodeUtils.h>
      29             : #include <lib/support/DLLUtil.h>
      30             : #include <lib/support/logging/CHIPLogging.h>
      31             : 
      32             : using namespace chip;
      33             : 
      34             : // TODO: Need to make it so that declarations of things that don't depend on generated files are not intermixed in af.h with
      35             : // dependencies on generated files, so we don't have to re-declare things here.
      36             : // Note: Some of the generated files that depended by af.h are gen_config.h and gen_tokens.h
      37             : typedef uint8_t EmberAfClusterMask;
      38             : 
      39             : extern uint16_t emberAfEndpointCount();
      40             : extern uint16_t emberAfIndexFromEndpoint(EndpointId endpoint);
      41             : extern uint8_t emberAfClusterCount(EndpointId endpoint, bool server);
      42             : extern uint16_t emberAfGetServerAttributeCount(chip::EndpointId endpoint, chip::ClusterId cluster);
      43             : extern uint16_t emberAfGetServerAttributeIndexByAttributeId(chip::EndpointId endpoint, chip::ClusterId cluster,
      44             :                                                             chip::AttributeId attributeId);
      45             : extern chip::EndpointId emberAfEndpointFromIndex(uint16_t index);
      46             : extern Optional<ClusterId> emberAfGetNthClusterId(chip::EndpointId endpoint, uint8_t n, bool server);
      47             : extern Optional<AttributeId> emberAfGetServerAttributeIdByIndex(chip::EndpointId endpoint, chip::ClusterId cluster,
      48             :                                                                 uint16_t attributeIndex);
      49             : extern uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask);
      50             : extern bool emberAfEndpointIndexIsEnabled(uint16_t index);
      51             : 
      52             : namespace chip {
      53             : namespace app {
      54             : 
      55        2847 : AttributePathExpandIterator::AttributePathExpandIterator(ObjectList<AttributePathParams> * aAttributePath)
      56             : {
      57        2847 :     mpAttributePath = aAttributePath;
      58             : 
      59             :     // Reset iterator state
      60        2847 :     mEndpointIndex  = UINT16_MAX;
      61        2847 :     mClusterIndex   = UINT8_MAX;
      62        2847 :     mAttributeIndex = UINT16_MAX;
      63             : 
      64             :     static_assert(std::numeric_limits<decltype(mGlobalAttributeIndex)>::max() >= ArraySize(GlobalAttributesNotInMetadata),
      65             :                   "Our index won't be able to hold the value we need to hold.");
      66             :     static_assert(std::is_same<decltype(mGlobalAttributeIndex), uint8_t>::value,
      67             :                   "If this changes audit all uses where we set to UINT8_MAX");
      68        2847 :     mGlobalAttributeIndex = UINT8_MAX;
      69             : 
      70             :     // Make the iterator ready to emit the first valid path in the list.
      71        2847 :     Next();
      72        2847 : }
      73             : 
      74         488 : void AttributePathExpandIterator::PrepareEndpointIndexRange(const AttributePathParams & aAttributePath)
      75             : {
      76         488 :     if (aAttributePath.HasWildcardEndpointId())
      77             :     {
      78          60 :         mEndpointIndex    = 0;
      79          60 :         mEndEndpointIndex = emberAfEndpointCount();
      80             :     }
      81             :     else
      82             :     {
      83         428 :         mEndpointIndex = emberAfIndexFromEndpoint(aAttributePath.mEndpointId);
      84             :         // If the given cluster id does not exist on the given endpoint, it will return uint16(0xFFFF), then endEndpointIndex
      85             :         // will be 0, means we should iterate a null endpoint set (skip it).
      86         428 :         mEndEndpointIndex = static_cast<uint16_t>(mEndpointIndex + 1);
      87             :     }
      88         488 : }
      89             : 
      90         574 : void AttributePathExpandIterator::PrepareClusterIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId)
      91             : {
      92         574 :     if (aAttributePath.HasWildcardClusterId())
      93             :     {
      94          88 :         mClusterIndex    = 0;
      95          88 :         mEndClusterIndex = emberAfClusterCount(aEndpointId, true /* server */);
      96             :     }
      97             :     else
      98             :     {
      99         486 :         mClusterIndex = emberAfClusterIndex(aEndpointId, aAttributePath.mClusterId, CLUSTER_MASK_SERVER);
     100             :         // If the given cluster id does not exist on the given endpoint, it will return uint8(0xFF), then endClusterIndex
     101             :         // will be 0, means we should iterate a null cluster set (skip it).
     102         486 :         mEndClusterIndex = static_cast<uint8_t>(mClusterIndex + 1);
     103             :     }
     104         574 : }
     105             : 
     106         641 : void AttributePathExpandIterator::PrepareAttributeIndexRange(const AttributePathParams & aAttributePath, EndpointId aEndpointId,
     107             :                                                              ClusterId aClusterId)
     108             : {
     109         641 :     if (aAttributePath.HasWildcardAttributeId())
     110             :     {
     111         594 :         mAttributeIndex          = 0;
     112         594 :         mEndAttributeIndex       = emberAfGetServerAttributeCount(aEndpointId, aClusterId);
     113         594 :         mGlobalAttributeIndex    = 0;
     114         594 :         mGlobalAttributeEndIndex = ArraySize(GlobalAttributesNotInMetadata);
     115             :     }
     116             :     else
     117             :     {
     118          47 :         mAttributeIndex = emberAfGetServerAttributeIndexByAttributeId(aEndpointId, aClusterId, aAttributePath.mAttributeId);
     119             :         // If the given attribute id does not exist on the given endpoint, it will return uint16(0xFFFF), then endAttributeIndex
     120             :         // will be 0, means we should iterate a null attribute set (skip it).
     121          47 :         mEndAttributeIndex = static_cast<uint16_t>(mAttributeIndex + 1);
     122          47 :         if (mAttributeIndex == UINT16_MAX)
     123             :         {
     124             :             // Check whether this is a non-metadata global attribute.
     125             :             //
     126             :             // Default to the max value, which will correspond (after we add 1
     127             :             // and overflow to 0 for the max index) to us not going through
     128             :             // non-metadata global attributes for this attribute.
     129          17 :             mGlobalAttributeIndex = UINT8_MAX;
     130             : 
     131             :             static_assert(ArraySize(GlobalAttributesNotInMetadata) <= UINT8_MAX, "Iterating over at most 256 array entries");
     132             : 
     133          17 :             const uint8_t arraySize = static_cast<uint8_t>(ArraySize(GlobalAttributesNotInMetadata));
     134          64 :             for (uint8_t idx = 0; idx < arraySize; ++idx)
     135             :             {
     136          51 :                 if (GlobalAttributesNotInMetadata[idx] == aAttributePath.mAttributeId)
     137             :                 {
     138           4 :                     mGlobalAttributeIndex = idx;
     139           4 :                     break;
     140             :                 }
     141             :             }
     142          17 :             mGlobalAttributeEndIndex = static_cast<uint8_t>(mGlobalAttributeIndex + 1);
     143             :         }
     144             :         else
     145             :         {
     146          30 :             mGlobalAttributeIndex    = UINT8_MAX;
     147          30 :             mGlobalAttributeEndIndex = 0;
     148             :         }
     149             :     }
     150         641 : }
     151             : 
     152         263 : void AttributePathExpandIterator::ResetCurrentCluster()
     153             : {
     154             :     // If this is a null iterator, or the attribute id of current cluster info is not a wildcard attribute id, then this function
     155             :     // will do nothing, since we won't be expanding the wildcard attribute ids under a cluster.
     156         263 :     VerifyOrReturn(mpAttributePath != nullptr && mpAttributePath->mValue.HasWildcardAttributeId());
     157             : 
     158             :     // Otherwise, we will reset the index for iterating the attributes, so we report the attributes for this cluster again. This
     159             :     // will ensure that the client sees a coherent view of the cluster from the reports generated by a single (wildcard) attribute
     160             :     // path in the request.
     161             :     //
     162             :     // Note that when Next() returns, we must be in one of the following states:
     163             :     // - This is not a wildcard path
     164             :     // - We just expanded some attribute id field
     165             :     // - We have exhausted all paths
     166             :     // Only the second case will happen here since the above check will fail for 1 and 3, so the following Next() call must result
     167             :     // in a valid path, which is the first attribute id we will emit for the current cluster.
     168           3 :     mAttributeIndex       = UINT16_MAX;
     169           3 :     mGlobalAttributeIndex = UINT8_MAX;
     170           3 :     Next();
     171             : }
     172             : 
     173        6754 : bool AttributePathExpandIterator::Next()
     174             : {
     175        7963 :     for (; mpAttributePath != nullptr; (mpAttributePath = mpAttributePath->mpNext, mEndpointIndex = UINT16_MAX))
     176             :     {
     177        6033 :         mOutputPath.mExpanded = mpAttributePath->mValue.IsWildcardPath();
     178             : 
     179        6033 :         if (mEndpointIndex == UINT16_MAX)
     180             :         {
     181             :             // Special case: If this is a concrete path, we just return its value as-is.
     182        2126 :             if (!mpAttributePath->mValue.IsWildcardPath())
     183             :             {
     184        1638 :                 mOutputPath.mEndpointId  = mpAttributePath->mValue.mEndpointId;
     185        1638 :                 mOutputPath.mClusterId   = mpAttributePath->mValue.mClusterId;
     186        1638 :                 mOutputPath.mAttributeId = mpAttributePath->mValue.mAttributeId;
     187             : 
     188             :                 // Prepare for next iteration
     189        1638 :                 mEndpointIndex = mEndEndpointIndex = 0;
     190        1638 :                 return true;
     191             :             }
     192             : 
     193         488 :             PrepareEndpointIndexRange(mpAttributePath->mValue);
     194         488 :             mClusterIndex = UINT8_MAX;
     195             :         }
     196             : 
     197        4766 :         for (; mEndpointIndex < mEndEndpointIndex;
     198         371 :              (mEndpointIndex++, mClusterIndex = UINT8_MAX, mAttributeIndex = UINT16_MAX, mGlobalAttributeIndex = UINT8_MAX))
     199             :         {
     200        3557 :             if (!emberAfEndpointIndexIsEnabled(mEndpointIndex))
     201             :             {
     202             :                 // Not an enabled endpoint; skip it.
     203          23 :                 continue;
     204             :             }
     205             : 
     206        3534 :             EndpointId endpointId = emberAfEndpointFromIndex(mEndpointIndex);
     207             : 
     208        3534 :             if (mClusterIndex == UINT8_MAX)
     209             :             {
     210         574 :                 PrepareClusterIndexRange(mpAttributePath->mValue, endpointId);
     211         574 :                 mAttributeIndex       = UINT16_MAX;
     212         574 :                 mGlobalAttributeIndex = UINT8_MAX;
     213             :             }
     214             : 
     215        3946 :             for (; mClusterIndex < mEndClusterIndex;
     216         412 :                  (mClusterIndex++, mAttributeIndex = UINT16_MAX, mGlobalAttributeIndex = UINT8_MAX))
     217             :             {
     218             :                 // emberAfGetNthClusterId must return a valid cluster id here since we have verified the mClusterIndex does
     219             :                 // not exceed the mEndClusterIndex.
     220        3598 :                 ClusterId clusterId = emberAfGetNthClusterId(endpointId, mClusterIndex, true /* server */).Value();
     221        3598 :                 if (mAttributeIndex == UINT16_MAX && mGlobalAttributeIndex == UINT8_MAX)
     222             :                 {
     223         641 :                     PrepareAttributeIndexRange(mpAttributePath->mValue, endpointId, clusterId);
     224             :                 }
     225             : 
     226        3598 :                 if (mAttributeIndex < mEndAttributeIndex)
     227             :                 {
     228             :                     // GetServerAttributeIdByIdex must return a valid attribute here since we have verified the mAttributeIndex does
     229             :                     // not exceed the mEndAttributeIndex.
     230        2060 :                     mOutputPath.mAttributeId = emberAfGetServerAttributeIdByIndex(endpointId, clusterId, mAttributeIndex).Value();
     231        2060 :                     mOutputPath.mClusterId   = clusterId;
     232        2060 :                     mOutputPath.mEndpointId  = endpointId;
     233        2060 :                     mAttributeIndex++;
     234             :                     // We found a valid attribute path, now return and increase the attribute index for next iteration.
     235             :                     // Return true will skip the increment of mClusterIndex, mEndpointIndex and mpAttributePath.
     236        2060 :                     return true;
     237             :                 }
     238        1538 :                 if (mGlobalAttributeIndex < mGlobalAttributeEndIndex)
     239             :                 {
     240             :                     // Return a path pointing to the next global attribute.
     241        1126 :                     mOutputPath.mAttributeId = GlobalAttributesNotInMetadata[mGlobalAttributeIndex];
     242        1126 :                     mOutputPath.mClusterId   = clusterId;
     243        1126 :                     mOutputPath.mEndpointId  = endpointId;
     244        1126 :                     mGlobalAttributeIndex++;
     245        1126 :                     return true;
     246             :                 }
     247             :                 // We have exhausted all attributes of this cluster, continue iterating over attributes of next cluster.
     248             :             }
     249             :             // We have exhausted all clusters of this endpoint, continue iterating over clusters of next endpoint.
     250             :         }
     251             :         // We have exhausted all endpoints in this cluster info, continue iterating over next cluster info item.
     252             :     }
     253             : 
     254             :     // Reset to default, invalid value.
     255        1930 :     mOutputPath = ConcreteReadAttributePath();
     256        1930 :     return false;
     257             : }
     258             : } // namespace app
     259             : } // namespace chip

Generated by: LCOV version 1.14