blob: 5292655b7934c79a457d75a435526b67cf2d8810 [file] [log] [blame]
/*
*
* 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/CHIPTLVDebug.hpp>
#include <lib/core/CHIPTLVUtilities.hpp>
#include <lib/support/CodeUtils.h>
namespace chip {
namespace TLV {
namespace Utilities {
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 (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