LCOV - code coverage report
Current view: top level - lib/core - TLVUtilities.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 70 74 94.6 %
Date: 2024-02-15 08:20:41 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020 Project CHIP Authors
       4             :  *    Copyright (c) 2015-2017 Nest Labs, Inc.
       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             : /**
      20             :  *    @file
      21             :  *      This file implements utility interfaces for managing and
      22             :  *      working with CHIP TLV.
      23             :  *
      24             :  */
      25             : 
      26             : #include <lib/core/TLVUtilities.h>
      27             : 
      28             : #include <lib/core/CHIPError.h>
      29             : #include <lib/core/TLVReader.h>
      30             : #include <lib/core/TLVTags.h>
      31             : #include <lib/core/TLVTypes.h>
      32             : #include <lib/support/CodeUtils.h>
      33             : 
      34             : namespace chip {
      35             : 
      36             : namespace TLV {
      37             : 
      38             : namespace Utilities {
      39             : 
      40             : namespace {
      41             : 
      42             : // Sets up a limit on recursion depth, to avoid any stack overflows
      43             : // on very deep TLV structures. Embedded has limited stack space.
      44             : constexpr size_t kMaxRecursionDepth = 10;
      45             : 
      46             : } // namespace
      47             : 
      48             : struct FindContext
      49             : {
      50             :     const Tag & mTag;
      51             :     TLVReader & mReader;
      52             : };
      53             : 
      54             : /**
      55             :  *  Iterate through the TLV data referenced by @a aReader and invoke @a aHandler
      56             :  *  for each visited TLV element in the context of @a aContext.
      57             :  *  The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR
      58             :  *
      59             :  *  @param[in]     aReader      A reference to the TLV reader containing the TLV
      60             :  *                              data to iterate.
      61             :  *  @param[in]     aDepth       The current depth into the TLV data.
      62             :  *  @param[in]     aHandler     A callback to invoke for the current TLV element
      63             :  *                              being visited.
      64             :  *  @param[in,out] aContext     An optional pointer to caller-provided context data.
      65             :  *  @param[in]     aRecurse     A Boolean indicating whether (true) or not (false)
      66             :  *                              any encountered arrays or structures should be
      67             :  *                              descended into.
      68             :  *
      69             :  *  @retval  #CHIP_END_OF_TLV   On a successful iteration to the end of a TLV encoding,
      70             :  *                              or to the end of a TLV container.
      71             :  *
      72             :  *  @retval  The last value returned by @a aHandler, if different than #CHIP_NO_ERROR
      73             :  */
      74        9075 : static CHIP_ERROR Iterate(TLVReader & aReader, size_t aDepth, IterateHandler aHandler, void * aContext, bool aRecurse)
      75             : {
      76        9075 :     CHIP_ERROR retval = CHIP_NO_ERROR;
      77             : 
      78        9075 :     if (aDepth >= kMaxRecursionDepth)
      79             :     {
      80           0 :         return CHIP_ERROR_RECURSION_DEPTH_LIMIT;
      81             :     }
      82             : 
      83        9075 :     if (aReader.GetType() == kTLVType_NotSpecified)
      84             :     {
      85        9030 :         ReturnErrorOnFailure(aReader.Next());
      86             :     }
      87             : 
      88             :     do
      89             :     {
      90       39060 :         const TLVType theType = aReader.GetType();
      91             : 
      92       39060 :         ReturnErrorOnFailure((aHandler) (aReader, aDepth, aContext));
      93             : 
      94       38073 :         if (aRecurse && TLVTypeIsContainer(theType))
      95             :         {
      96             :             TLVType containerType;
      97             : 
      98         529 :             ReturnErrorOnFailure(aReader.EnterContainer(containerType));
      99             : 
     100         529 :             retval = Iterate(aReader, aDepth + 1, aHandler, aContext, aRecurse);
     101         529 :             if ((retval != CHIP_END_OF_TLV) && (retval != CHIP_NO_ERROR))
     102             :             {
     103           4 :                 return retval;
     104             :             }
     105             : 
     106         525 :             ReturnErrorOnFailure(aReader.ExitContainer(containerType));
     107             :         }
     108       38067 :     } while ((retval = aReader.Next()) == CHIP_NO_ERROR);
     109             : 
     110        8065 :     return retval;
     111             : }
     112             : 
     113             : /**
     114             :  *  Iterate through the TLV data referenced by @a aReader and invoke @a aHandler
     115             :  *  for each visited TLV element in the context of @a aContext.
     116             :  *  The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR
     117             :  *
     118             :  *  @param[in]     aReader      A reference to the TLV reader containing the TLV
     119             :  *                              data to iterate.
     120             :  *  @param[in]     aHandler     A callback to invoke for the current TLV element
     121             :  *                              being visited.
     122             :  *  @param[in,out] aContext     An optional pointer to caller-provided context data.
     123             :  *
     124             :  *  @retval  #CHIP_END_OF_TLV  On a successful iteration to the end of a TLV encoding,
     125             :  *                              or to the end of a TLV container.
     126             :  *
     127             :  *  @retval  #CHIP_ERROR_INVALID_ARGUMENT  If @a aHandler is NULL.
     128             :  *
     129             :  *  @retval  The last value returned by @a aHandler, if different than #CHIP_NO_ERROR
     130             :  *
     131             :  */
     132          76 : CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext)
     133             : {
     134          76 :     const bool recurse = true;
     135             :     CHIP_ERROR retval;
     136             : 
     137          76 :     retval = Iterate(aReader, aHandler, aContext, recurse);
     138             : 
     139          76 :     return retval;
     140             : }
     141             : 
     142             : /**
     143             :  *  Iterate through the TLV data referenced by @a aReader and invoke @a aHandler
     144             :  *  for each visited TLV element in the context of @a aContext.
     145             :  *  The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR
     146             :  *
     147             :  *  @param[in]     aReader      A reference to the TLV reader containing the TLV
     148             :  *                              data to iterate.
     149             :  *  @param[in]     aHandler     A callback to invoke for the current TLV element
     150             :  *                              being visited.
     151             :  *  @param[in,out] aContext     An optional pointer to caller-provided context data.
     152             :  *  @param[in]     aRecurse     A Boolean indicating whether (true) or not (false)
     153             :  *                              any encountered arrays or structures should be
     154             :  *                              descended into.
     155             :  *
     156             :  *  @retval  #CHIP_END_OF_TLV  On a successful iteration to the end of a TLV encoding,
     157             :  *                              or to the end of a TLV container.
     158             :  *
     159             :  *  @retval  #CHIP_ERROR_INVALID_ARGUMENT  If @a aHandler is NULL.
     160             :  *
     161             :  *  @retval  The last value returned by @a aHandler, if different than #CHIP_NO_ERROR
     162             :  *
     163             :  */
     164        8546 : CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext, const bool aRecurse)
     165             : {
     166        8546 :     VerifyOrReturnError(aHandler != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     167             : 
     168             :     TLVReader temp;
     169        8546 :     temp.Init(aReader);
     170             : 
     171        8546 :     constexpr size_t depth = 0;
     172        8546 :     return Iterate(temp, depth, aHandler, aContext, aRecurse);
     173             : }
     174             : 
     175             : /**
     176             :  *  Increment the counter when iterating through the TLV data.
     177             :  *
     178             :  *  @param[in]     aReader      A reference to the TLV reader containing the TLV
     179             :  *                              data to count the number of TLV elements.
     180             :  *  @param[in]     aDepth       The current depth into the TLV data.
     181             :  *  @param[in,out] aContext     A pointer to the handler-specific context which
     182             :  *                              is a pointer to storage for the count value.
     183             :  *
     184             :  *  @retval  #CHIP_NO_ERROR                On success.
     185             :  *
     186             :  *  @retval  #CHIP_ERROR_INVALID_ARGUMENT  If @a aContext is NULL.
     187             :  *
     188             :  */
     189        2435 : static CHIP_ERROR CountHandler(const TLVReader & aReader, size_t aDepth, void * aContext)
     190             : {
     191        2435 :     VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     192             : 
     193        2435 :     *static_cast<size_t *>(aContext) += 1;
     194             : 
     195        2435 :     return CHIP_NO_ERROR;
     196             : }
     197             : 
     198             : /**
     199             :  *  Count the number of TLV elements within the specified TLV reader,
     200             :  *  descending into arrays or structures.
     201             :  *
     202             :  *  @param[in]     aReader      A read-only reference to the TLV reader for
     203             :  *                              which to count the number of TLV elements.
     204             :  *  @param[in,out] aCount       A reference to storage for the returned count.
     205             :  *                              This is initialized to zero (0) prior to counting
     206             :  *                              and is set to the number of elements counted on
     207             :  *                              success.
     208             :  *
     209             :  *  @retval  #CHIP_NO_ERROR    On success.
     210             :  *
     211             :  */
     212           2 : CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount)
     213             : {
     214           2 :     constexpr bool recurse = true;
     215           2 :     return Count(aReader, aCount, recurse);
     216             : }
     217             : 
     218             : /**
     219             :  *  Count the number of TLV elements within the specified TLV reader,
     220             :  *  optionally descending into arrays or structures.
     221             :  *
     222             :  *  @param[in]     aReader      A read-only reference to the TLV reader for
     223             :  *                              which to count the number of TLV elements.
     224             :  *  @param[in,out] aCount       A reference to storage for the returned count.
     225             :  *                              This is initialized to zero (0) prior to counting
     226             :  *                              and is set to the number of elements counted on
     227             :  *                              success.
     228             :  *  @param[in]     aRecurse     A Boolean indicating whether (true) or not (false)
     229             :  *                              any encountered arrays or structures should be
     230             :  *                              descended into.
     231             :  *
     232             :  *  @retval  #CHIP_NO_ERROR    On success.
     233             :  *
     234             :  */
     235        1094 : CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount, const bool aRecurse)
     236             : {
     237        1094 :     aCount = 0;
     238             : 
     239        1094 :     CHIP_ERROR retval = Iterate(aReader, CountHandler, &aCount, aRecurse);
     240             : 
     241        1094 :     if (retval == CHIP_END_OF_TLV)
     242        1094 :         retval = CHIP_NO_ERROR;
     243             : 
     244        1094 :     return retval;
     245             : }
     246             : 
     247             : /**
     248             :  *  Search for the specified tag within the provided TLV reader.
     249             :  *
     250             :  *  @param[in]     aReader      A read-only reference to the TLV reader in
     251             :  *                              which to find the specified tag.
     252             :  *  @param[in]     aDepth       The current depth into the TLV data.
     253             :  *  @param[in,out] aContext     A pointer to the handler-specific context.
     254             :  *
     255             :  *  @retval  #CHIP_NO_ERROR                On success.
     256             :  *
     257             :  *  @retval  #CHIP_ERROR_INVALID_ARGUMENT  If @a aContext is NULL.
     258             :  *
     259             :  *  @retval  #CHIP_ERROR_SENTINEL          If the specified tag is found.
     260             :  *
     261             :  */
     262          55 : static CHIP_ERROR FindHandler(const TLVReader & aReader, size_t aDepth, void * aContext)
     263             : {
     264          55 :     VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     265          55 :     const FindContext * const theContext = static_cast<const FindContext *>(aContext);
     266             : 
     267          55 :     if (theContext->mTag == aReader.GetTag())
     268             :     {
     269           8 :         theContext->mReader.Init(aReader);
     270             :         // terminate the iteration when the specified tag is found
     271           8 :         return CHIP_ERROR_SENTINEL;
     272             :     }
     273             : 
     274          47 :     return CHIP_NO_ERROR;
     275             : }
     276             : 
     277             : /**
     278             :  *  Search for the specified tag within the provided TLV reader.
     279             :  *
     280             :  *  @param[in]   aReader        A read-only reference to the TLV reader in
     281             :  *                              which to find the specified tag.
     282             :  *  @param[in]   aTag           A read-only reference to the TLV tag to find.
     283             :  *  @param[out]  aResult        A reference to storage to a TLV reader which
     284             :  *                              will be positioned at the specified tag
     285             :  *                              on success.
     286             :  *
     287             :  *  @retval  #CHIP_NO_ERROR                    On success.
     288             :  *
     289             :  *  @retval  #CHIP_ERROR_TLV_TAG_NOT_FOUND     If the specified tag @a aTag was not found.
     290             :  *
     291             :  */
     292           6 : CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult)
     293             : {
     294           6 :     constexpr bool recurse = true;
     295           6 :     return Find(aReader, aTag, aResult, recurse);
     296             : }
     297             : 
     298             : /**
     299             :  *  Search for the specified tag within the provided TLV reader,
     300             :  *  optionally descending into arrays or structures.
     301             :  *
     302             :  *  @param[in]   aReader        A read-only reference to the TLV reader in
     303             :  *                              which to find the specified tag.
     304             :  *  @param[in]   aTag           A read-only reference to the TLV tag to find.
     305             :  *  @param[out]  aResult        A reference to storage to a TLV reader which
     306             :  *                              will be positioned at the specified tag
     307             :  *                              on success.
     308             :  *  @param[in]   aRecurse       A Boolean indicating whether (true) or not (false)
     309             :  *                              any encountered arrays or structures should be
     310             :  *                              descended into.
     311             :  *
     312             :  *  @retval  #CHIP_NO_ERROR                    On success.
     313             :  *
     314             :  *  @retval  #CHIP_ERROR_TLV_TAG_NOT_FOUND     If the specified tag @a aTag was not found.
     315             :  *
     316             :  */
     317          12 : CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult, const bool aRecurse)
     318             : {
     319          12 :     FindContext theContext = { aTag, aResult };
     320          12 :     CHIP_ERROR retval      = Iterate(aReader, FindHandler, &theContext, aRecurse);
     321             : 
     322          12 :     if (retval == CHIP_ERROR_SENTINEL)
     323           8 :         retval = CHIP_NO_ERROR;
     324             :     else
     325           4 :         retval = CHIP_ERROR_TLV_TAG_NOT_FOUND;
     326             : 
     327          12 :     return retval;
     328             : }
     329             : 
     330             : struct FindPredicateContext
     331             : {
     332             :     TLVReader & mResult;
     333             :     IterateHandler mHandler;
     334             :     void * mContext;
     335             :     FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext);
     336             : };
     337             : 
     338           3 : FindPredicateContext::FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext) :
     339           3 :     mResult(inReader), mHandler(inHandler), mContext(inContext)
     340           3 : {}
     341             : 
     342           4 : static CHIP_ERROR FindPredicateHandler(const TLVReader & aReader, size_t aDepth, void * aContext)
     343             : {
     344           4 :     FindPredicateContext * theContext = static_cast<FindPredicateContext *>(aContext);
     345             :     CHIP_ERROR err;
     346             : 
     347           4 :     err = theContext->mHandler(aReader, aDepth, theContext->mContext);
     348             : 
     349           4 :     if (err == CHIP_ERROR_SENTINEL)
     350           2 :         theContext->mResult.Init(aReader);
     351             : 
     352           4 :     return err;
     353             : }
     354             : 
     355             : /**
     356             :  *  Search for the first element matching the predicate within the TLV reader
     357             :  *  descending into arrays or structures. The @a aPredicate is applied
     358             :  *  to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL
     359             :  *  for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any
     360             :  *  other value to terminate the search.
     361             :  *
     362             :  *  @param[in] aReader     A read-only reference to the TLV reader in which to find the
     363             :  *                         element matching the predicate.
     364             :  *  @param[in] aPredicate  A predicate to be applied to each TLV element.  To
     365             :  *                         support the code reuse, aPredicate has the
     366             :  *                         IterateHandler type.  The return value of aPredicate
     367             :  *                         controls the search: a #CHIP_ERROR_SENTINEL signals that
     368             :  *                         desired element has been found, #CHIP_NO_ERROR
     369             :  *                         signals that the desired element has not been found,
     370             :  *                         and all other values signal that the search should be
     371             :  *                         terminated.
     372             :  *  @param[in] aContext    An optional pointer to caller-provided context data.
     373             :  *
     374             :  *  @param[out] aResult    A reference to storage to a TLV reader which
     375             :  *                         will be positioned at the specified tag
     376             :  *                         on success.
     377             :  *  @retval  #CHIP_NO_ERROR                    On success.
     378             :  *
     379             :  *  @retval  #CHIP_ERROR_TLV_TAG_NOT_FOUND     If the specified @a aPredicate did not locate the specified element
     380             :  *
     381             :  */
     382           0 : CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult)
     383             : {
     384           0 :     const bool recurse = true;
     385           0 :     return Find(aReader, aPredicate, aContext, aResult, recurse);
     386             : }
     387             : 
     388             : /**
     389             :  *  Search for the first element matching the predicate within the TLV reader
     390             :  *  optionally descending into arrays or structures. The @a aPredicate is applied
     391             :  *  to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL
     392             :  *  for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any
     393             :  *  other value to terminate the search.
     394             :  *
     395             :  *  @param[in] aReader     A read-only reference to the TLV reader in which to find the
     396             :  *                         element matching the predicate.
     397             :  *  @param[in] aPredicate  A predicate to be applied to each TLV element.  To
     398             :  *                         support the code reuse, aPredicate has the
     399             :  *                         @a IterateHandler type.  The return value of aPredicate
     400             :  *                         controls the search: a #CHIP_ERROR_SENTINEL signals that
     401             :  *                         desired element has been found, #CHIP_NO_ERROR
     402             :  *                         signals that the desired element has not been found,
     403             :  *                         and all other values signal that the saerch should be
     404             :  *                         terminated.
     405             :  *  @param[in] aContext    An optional pointer to caller-provided context data.
     406             :  *  @param[out] aResult    A reference to storage to a TLV reader which
     407             :  *                         will be positioned at the specified tag
     408             :  *                         on success.
     409             :  *  @param[in] aRecurse    A boolean indicating whether (true) or not (false) any
     410             :  *                         encountered arrays or structures should be descended
     411             :  *                         into.
     412             :  *
     413             :  *  @retval  #CHIP_NO_ERROR                    On success.
     414             :  *
     415             :  *  @retval  #CHIP_ERROR_TLV_TAG_NOT_FOUND     If the specified @a aPredicate did not locate the specified element
     416             :  *
     417             :  */
     418           3 : CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult, const bool aRecurse)
     419             : {
     420             :     CHIP_ERROR retval;
     421           3 :     FindPredicateContext theContext(aResult, aPredicate, aContext);
     422             : 
     423           3 :     retval = Iterate(aReader, FindPredicateHandler, &theContext, aRecurse);
     424             : 
     425           3 :     if (retval == CHIP_ERROR_SENTINEL)
     426           2 :         retval = CHIP_NO_ERROR;
     427             :     else
     428           1 :         retval = CHIP_ERROR_TLV_TAG_NOT_FOUND;
     429             : 
     430           3 :     return retval;
     431             : }
     432             : 
     433             : } // namespace Utilities
     434             : 
     435             : } // namespace TLV
     436             : 
     437             : } // namespace chip

Generated by: LCOV version 1.14