| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2015-2017 Nest Labs, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file implements utility interfaces for managing and |
| * working with CHIP TLV. |
| * |
| */ |
| |
| #include <lib/core/TLVUtilities.h> |
| |
| #include <lib/core/CHIPError.h> |
| #include <lib/core/TLVReader.h> |
| #include <lib/core/TLVTags.h> |
| #include <lib/core/TLVTypes.h> |
| #include <lib/support/CodeUtils.h> |
| |
| namespace chip { |
| |
| namespace TLV { |
| |
| namespace Utilities { |
| |
| namespace { |
| |
| // Sets up a limit on recursion depth, to avoid any stack overflows |
| // on very deep TLV structures. Embedded has limited stack space. |
| constexpr size_t kMaxRecursionDepth = 10; |
| |
| } // namespace |
| |
| struct FindContext |
| { |
| const Tag & mTag; |
| TLVReader & mReader; |
| }; |
| |
| /** |
| * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler |
| * for each visited TLV element in the context of @a aContext. |
| * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR |
| * |
| * @param[in] aReader A reference to the TLV reader containing the TLV |
| * data to iterate. |
| * @param[in] aDepth The current depth into the TLV data. |
| * @param[in] aHandler A callback to invoke for the current TLV element |
| * being visited. |
| * @param[in,out] aContext An optional pointer to caller-provided context data. |
| * @param[in] aRecurse A Boolean indicating whether (true) or not (false) |
| * any encountered arrays or structures should be |
| * descended into. |
| * |
| * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding, |
| * or to the end of a TLV container. |
| * |
| * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR |
| */ |
| static CHIP_ERROR Iterate(TLVReader & aReader, size_t aDepth, IterateHandler aHandler, void * aContext, bool aRecurse) |
| { |
| CHIP_ERROR retval = CHIP_NO_ERROR; |
| |
| if (aDepth >= kMaxRecursionDepth) |
| { |
| return CHIP_ERROR_RECURSION_DEPTH_LIMIT; |
| } |
| |
| if (aReader.GetType() == kTLVType_NotSpecified) |
| { |
| ReturnErrorOnFailure(aReader.Next()); |
| } |
| |
| do |
| { |
| const TLVType theType = aReader.GetType(); |
| |
| ReturnErrorOnFailure((aHandler) (aReader, aDepth, aContext)); |
| |
| if (aRecurse && TLVTypeIsContainer(theType)) |
| { |
| TLVType containerType; |
| |
| ReturnErrorOnFailure(aReader.EnterContainer(containerType)); |
| |
| retval = Iterate(aReader, aDepth + 1, aHandler, aContext, aRecurse); |
| if ((retval != CHIP_END_OF_TLV) && (retval != CHIP_NO_ERROR)) |
| { |
| return retval; |
| } |
| |
| ReturnErrorOnFailure(aReader.ExitContainer(containerType)); |
| } |
| } while ((retval = aReader.Next()) == CHIP_NO_ERROR); |
| |
| return retval; |
| } |
| |
| /** |
| * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler |
| * for each visited TLV element in the context of @a aContext. |
| * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR |
| * |
| * @param[in] aReader A reference to the TLV reader containing the TLV |
| * data to iterate. |
| * @param[in] aHandler A callback to invoke for the current TLV element |
| * being visited. |
| * @param[in,out] aContext An optional pointer to caller-provided context data. |
| * |
| * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding, |
| * or to the end of a TLV container. |
| * |
| * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aHandler is NULL. |
| * |
| * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR |
| * |
| */ |
| CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext) |
| { |
| const bool recurse = true; |
| CHIP_ERROR retval; |
| |
| retval = Iterate(aReader, aHandler, aContext, recurse); |
| |
| return retval; |
| } |
| |
| /** |
| * Iterate through the TLV data referenced by @a aReader and invoke @a aHandler |
| * for each visited TLV element in the context of @a aContext. |
| * The iteration is aborted if @a aHandler returns anything other than #CHIP_NO_ERROR |
| * |
| * @param[in] aReader A reference to the TLV reader containing the TLV |
| * data to iterate. |
| * @param[in] aHandler A callback to invoke for the current TLV element |
| * being visited. |
| * @param[in,out] aContext An optional pointer to caller-provided context data. |
| * @param[in] aRecurse A Boolean indicating whether (true) or not (false) |
| * any encountered arrays or structures should be |
| * descended into. |
| * |
| * @retval #CHIP_END_OF_TLV On a successful iteration to the end of a TLV encoding, |
| * or to the end of a TLV container. |
| * |
| * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aHandler is NULL. |
| * |
| * @retval The last value returned by @a aHandler, if different than #CHIP_NO_ERROR |
| * |
| */ |
| CHIP_ERROR Iterate(const TLVReader & aReader, IterateHandler aHandler, void * aContext, const bool aRecurse) |
| { |
| VerifyOrReturnError(aHandler != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| TLVReader temp; |
| temp.Init(aReader); |
| |
| constexpr size_t depth = 0; |
| return Iterate(temp, depth, aHandler, aContext, aRecurse); |
| } |
| |
| /** |
| * Increment the counter when iterating through the TLV data. |
| * |
| * @param[in] aReader A reference to the TLV reader containing the TLV |
| * data to count the number of TLV elements. |
| * @param[in] aDepth The current depth into the TLV data. |
| * @param[in,out] aContext A pointer to the handler-specific context which |
| * is a pointer to storage for the count value. |
| * |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aContext is NULL. |
| * |
| */ |
| static CHIP_ERROR CountHandler(const TLVReader & aReader, size_t aDepth, void * aContext) |
| { |
| VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| *static_cast<size_t *>(aContext) += 1; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * Count the number of TLV elements within the specified TLV reader, |
| * descending into arrays or structures. |
| * |
| * @param[in] aReader A read-only reference to the TLV reader for |
| * which to count the number of TLV elements. |
| * @param[in,out] aCount A reference to storage for the returned count. |
| * This is initialized to zero (0) prior to counting |
| * and is set to the number of elements counted on |
| * success. |
| * |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| */ |
| CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount) |
| { |
| constexpr bool recurse = true; |
| return Count(aReader, aCount, recurse); |
| } |
| |
| /** |
| * Count the number of TLV elements within the specified TLV reader, |
| * optionally descending into arrays or structures. |
| * |
| * @param[in] aReader A read-only reference to the TLV reader for |
| * which to count the number of TLV elements. |
| * @param[in,out] aCount A reference to storage for the returned count. |
| * This is initialized to zero (0) prior to counting |
| * and is set to the number of elements counted on |
| * success. |
| * @param[in] aRecurse A Boolean indicating whether (true) or not (false) |
| * any encountered arrays or structures should be |
| * descended into. |
| * |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| */ |
| CHIP_ERROR Count(const TLVReader & aReader, size_t & aCount, const bool aRecurse) |
| { |
| aCount = 0; |
| |
| CHIP_ERROR retval = Iterate(aReader, CountHandler, &aCount, aRecurse); |
| |
| if (retval == CHIP_END_OF_TLV) |
| retval = CHIP_NO_ERROR; |
| |
| return retval; |
| } |
| |
| /** |
| * Search for the specified tag within the provided TLV reader. |
| * |
| * @param[in] aReader A read-only reference to the TLV reader in |
| * which to find the specified tag. |
| * @param[in] aDepth The current depth into the TLV data. |
| * @param[in,out] aContext A pointer to the handler-specific context. |
| * |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| * @retval #CHIP_ERROR_INVALID_ARGUMENT If @a aContext is NULL. |
| * |
| * @retval #CHIP_ERROR_SENTINEL If the specified tag is found. |
| * |
| */ |
| static CHIP_ERROR FindHandler(const TLVReader & aReader, size_t aDepth, void * aContext) |
| { |
| VerifyOrReturnError(aContext != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| const FindContext * const theContext = static_cast<const FindContext *>(aContext); |
| |
| if (theContext->mTag == aReader.GetTag()) |
| { |
| theContext->mReader.Init(aReader); |
| // terminate the iteration when the specified tag is found |
| return CHIP_ERROR_SENTINEL; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * Search for the specified tag within the provided TLV reader. |
| * |
| * @param[in] aReader A read-only reference to the TLV reader in |
| * which to find the specified tag. |
| * @param[in] aTag A read-only reference to the TLV tag to find. |
| * @param[out] aResult A reference to storage to a TLV reader which |
| * will be positioned at the specified tag |
| * on success. |
| * |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified tag @a aTag was not found. |
| * |
| */ |
| CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult) |
| { |
| constexpr bool recurse = true; |
| return Find(aReader, aTag, aResult, recurse); |
| } |
| |
| /** |
| * Search for the specified tag within the provided TLV reader, |
| * optionally descending into arrays or structures. |
| * |
| * @param[in] aReader A read-only reference to the TLV reader in |
| * which to find the specified tag. |
| * @param[in] aTag A read-only reference to the TLV tag to find. |
| * @param[out] aResult A reference to storage to a TLV reader which |
| * will be positioned at the specified tag |
| * on success. |
| * @param[in] aRecurse A Boolean indicating whether (true) or not (false) |
| * any encountered arrays or structures should be |
| * descended into. |
| * |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified tag @a aTag was not found. |
| * |
| */ |
| CHIP_ERROR Find(const TLVReader & aReader, const Tag & aTag, TLVReader & aResult, const bool aRecurse) |
| { |
| FindContext theContext = { aTag, aResult }; |
| CHIP_ERROR retval = Iterate(aReader, FindHandler, &theContext, aRecurse); |
| |
| if (retval == CHIP_ERROR_SENTINEL) |
| retval = CHIP_NO_ERROR; |
| else |
| retval = CHIP_ERROR_TLV_TAG_NOT_FOUND; |
| |
| return retval; |
| } |
| |
| struct FindPredicateContext |
| { |
| TLVReader & mResult; |
| IterateHandler mHandler; |
| void * mContext; |
| FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext); |
| }; |
| |
| FindPredicateContext::FindPredicateContext(TLVReader & inReader, IterateHandler inHandler, void * inContext) : |
| mResult(inReader), mHandler(inHandler), mContext(inContext) |
| {} |
| |
| static CHIP_ERROR FindPredicateHandler(const TLVReader & aReader, size_t aDepth, void * aContext) |
| { |
| FindPredicateContext * theContext = static_cast<FindPredicateContext *>(aContext); |
| CHIP_ERROR err; |
| |
| err = theContext->mHandler(aReader, aDepth, theContext->mContext); |
| |
| if (err == CHIP_ERROR_SENTINEL) |
| theContext->mResult.Init(aReader); |
| |
| return err; |
| } |
| |
| /** |
| * Search for the first element matching the predicate within the TLV reader |
| * descending into arrays or structures. The @a aPredicate is applied |
| * to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL |
| * for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any |
| * other value to terminate the search. |
| * |
| * @param[in] aReader A read-only reference to the TLV reader in which to find the |
| * element matching the predicate. |
| * @param[in] aPredicate A predicate to be applied to each TLV element. To |
| * support the code reuse, aPredicate has the |
| * IterateHandler type. The return value of aPredicate |
| * controls the search: a #CHIP_ERROR_SENTINEL signals that |
| * desired element has been found, #CHIP_NO_ERROR |
| * signals that the desired element has not been found, |
| * and all other values signal that the search should be |
| * terminated. |
| * @param[in] aContext An optional pointer to caller-provided context data. |
| * |
| * @param[out] aResult A reference to storage to a TLV reader which |
| * will be positioned at the specified tag |
| * on success. |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified @a aPredicate did not locate the specified element |
| * |
| */ |
| CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult) |
| { |
| const bool recurse = true; |
| return Find(aReader, aPredicate, aContext, aResult, recurse); |
| } |
| |
| /** |
| * Search for the first element matching the predicate within the TLV reader |
| * optionally descending into arrays or structures. The @a aPredicate is applied |
| * to each visited TLV element; the @a aPredicate shall return #CHIP_ERROR_SENTINEL |
| * for the matching elements, #CHIP_NO_ERROR for non-matching elements, and any |
| * other value to terminate the search. |
| * |
| * @param[in] aReader A read-only reference to the TLV reader in which to find the |
| * element matching the predicate. |
| * @param[in] aPredicate A predicate to be applied to each TLV element. To |
| * support the code reuse, aPredicate has the |
| * @a IterateHandler type. The return value of aPredicate |
| * controls the search: a #CHIP_ERROR_SENTINEL signals that |
| * desired element has been found, #CHIP_NO_ERROR |
| * signals that the desired element has not been found, |
| * and all other values signal that the saerch should be |
| * terminated. |
| * @param[in] aContext An optional pointer to caller-provided context data. |
| * @param[out] aResult A reference to storage to a TLV reader which |
| * will be positioned at the specified tag |
| * on success. |
| * @param[in] aRecurse A boolean indicating whether (true) or not (false) any |
| * encountered arrays or structures should be descended |
| * into. |
| * |
| * @retval #CHIP_NO_ERROR On success. |
| * |
| * @retval #CHIP_ERROR_TLV_TAG_NOT_FOUND If the specified @a aPredicate did not locate the specified element |
| * |
| */ |
| CHIP_ERROR Find(const TLVReader & aReader, IterateHandler aPredicate, void * aContext, TLVReader & aResult, const bool aRecurse) |
| { |
| CHIP_ERROR retval; |
| FindPredicateContext theContext(aResult, aPredicate, aContext); |
| |
| retval = Iterate(aReader, FindPredicateHandler, &theContext, aRecurse); |
| |
| if (retval == CHIP_ERROR_SENTINEL) |
| retval = CHIP_NO_ERROR; |
| else |
| retval = CHIP_ERROR_TLV_TAG_NOT_FOUND; |
| |
| return retval; |
| } |
| |
| } // namespace Utilities |
| |
| } // namespace TLV |
| |
| } // namespace chip |