| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2013-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 defines and implements a number of miscellaneous |
| * templates for finding object minima and maxima and interface |
| * macros for assertion checking. |
| * |
| */ |
| |
| #pragma once |
| |
| #include <lib/core/CHIPConfig.h> |
| #include <lib/core/CHIPError.h> |
| #include <lib/support/ErrorStr.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| /** |
| * Base-level abnormal termination. |
| * |
| * Terminate the program immediately, without invoking destructors, atexit callbacks, etc. |
| * Used to implement the default `chipDie()`. |
| * |
| * @note |
| * This should never be invoked directly by code outside this file. |
| */ |
| #if !defined(CHIP_CONFIG_ABORT) |
| #define CHIP_CONFIG_ABORT() abort() |
| #endif |
| |
| /** |
| * @name chip-specific nlassert.h Overrides |
| * |
| * @{ |
| * |
| */ |
| |
| /** |
| * @def NL_ASSERT_ABORT() |
| * |
| * @brief |
| * This implements a chip-specific override for #NL_ASSERT_ABORT * |
| * from nlassert.h. |
| * |
| */ |
| #if !defined(NL_ASSERT_ABORT) |
| #define NL_ASSERT_ABORT() chipAbort() |
| #endif |
| |
| /** |
| * @def NL_ASSERT_LOG(aPrefix, aName, aCondition, aLabel, aFile, aLine, aMessage) |
| * |
| * @brief |
| * This implements a chip-specific override for \c NL_ASSERT_LOG |
| * from nlassert.h. |
| * |
| * @param[in] aPrefix A pointer to a NULL-terminated C string printed |
| * at the beginning of the logged assertion |
| * message. Typically this is and should be |
| * \c NL_ASSERT_PREFIX_STRING. |
| * @param[in] aName A pointer to a NULL-terminated C string printed |
| * following @a aPrefix that indicates what |
| * module, program, application or subsystem |
| * the assertion occurred in Typically this |
| * is and should be |
| * \c NL_ASSERT_COMPONENT_STRING. |
| * @param[in] aCondition A pointer to a NULL-terminated C string indicating |
| * the expression that evaluated to false in |
| * the assertion. Typically this is a |
| * stringified version of the actual |
| * assertion expression. |
| * @param[in] aLabel An optional pointer to a NULL-terminated C string |
| * indicating, for exception-style |
| * assertions, the label that will be |
| * branched to when the assertion expression |
| * evaluates to false. |
| * @param[in] aFile A pointer to a NULL-terminated C string indicating |
| * the file in which the exception |
| * occurred. Typically this is and should be |
| * \_\_FILE\_\_ from the C preprocessor. |
| * @param[in] aLine The line number in @a aFile on which the assertion |
| * expression evaluated to false. Typically |
| * this is and should be \_\_LINE\_\_ from the C |
| * preprocessor. |
| * @param[in] aMessage An optional pointer to a NULL-terminated C string |
| * containing a caller-specified message |
| * further describing the assertion failure. |
| * |
| */ |
| // clang-format off |
| #if !defined(NL_ASSERT_LOG) |
| #define NL_ASSERT_LOG(aPrefix, aName, aCondition, aLabel, aFile, aLine, aMessage) \ |
| do \ |
| { \ |
| ChipLogError(NotSpecified, \ |
| NL_ASSERT_LOG_FORMAT_DEFAULT, \ |
| aPrefix, \ |
| (((aName) == 0) || (*(aName) == '\0')) ? "" : aName, \ |
| (((aName) == 0) || (*(aName) == '\0')) ? "" : ": ", \ |
| aCondition, \ |
| (((aMessage) == 0) ? "" : aMessage), \ |
| (((aMessage) == 0) ? "" : ", "), \ |
| aFile, \ |
| aLine); \ |
| } while (0) |
| #endif |
| // clang-format on |
| |
| /** |
| * @} chip-specific nlassert.h Overrides |
| * |
| */ |
| |
| #include <nlassert.h> |
| |
| namespace chip { |
| |
| // Generic min() and max() functions |
| // |
| template <typename _T> |
| constexpr inline const _T & min(const _T & a, const _T & b) |
| { |
| if (b < a) |
| return b; |
| |
| return a; |
| } |
| |
| template <typename _T> |
| constexpr inline const _T & max(const _T & a, const _T & b) |
| { |
| if (a < b) |
| return b; |
| |
| return a; |
| } |
| |
| } // namespace chip |
| |
| /** |
| * @def IgnoreUnusedVariable(aVariable) |
| * |
| * @brief |
| * This casts the specified @a aVariable to void to quell any |
| * compiler-issued unused variable warnings or errors. |
| * |
| * @code |
| * void foo (int err) |
| * { |
| * IgnoreUnusedVariable(err) |
| * } |
| * @endcode |
| * |
| */ |
| #define IgnoreUnusedVariable(aVariable) ((void) (aVariable)) |
| |
| /** |
| * @def ReturnErrorOnFailure(expr) |
| * |
| * @brief |
| * Returns the error code if the expression returns an error. For a CHIP_ERROR expression, this means any value other |
| * than CHIP_NO_ERROR. For an integer expression, this means non-zero. |
| * |
| * Example usage: |
| * |
| * @code |
| * ReturnErrorOnFailure(channel->SendMsg(msg)); |
| * @endcode |
| * |
| * @param[in] expr An expression to be tested. |
| */ |
| #define ReturnErrorOnFailure(expr) \ |
| do \ |
| { \ |
| auto __err = (expr); \ |
| if (!::chip::ChipError::IsSuccess(__err)) \ |
| { \ |
| return __err; \ |
| } \ |
| } while (false) |
| |
| /** |
| * @def ReturnLogErrorOnFailure(expr) |
| * |
| * @brief |
| * Returns the error code if the expression returns something different |
| * than CHIP_NO_ERROR. |
| * |
| * Example usage: |
| * |
| * @code |
| * ReturnLogErrorOnFailure(channel->SendMsg(msg)); |
| * @endcode |
| * |
| * @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR. |
| */ |
| #define ReturnLogErrorOnFailure(expr) \ |
| do \ |
| { \ |
| CHIP_ERROR __err = (expr); \ |
| if (__err != CHIP_NO_ERROR) \ |
| { \ |
| ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ |
| return __err; \ |
| } \ |
| } while (false) |
| |
| /** |
| * @def ReturnOnFailure(expr) |
| * |
| * @brief |
| * Returns if the expression returns an error. For a CHIP_ERROR expression, this means any value other |
| * than CHIP_NO_ERROR. For an integer expression, this means non-zero. |
| * |
| * Example usage: |
| * |
| * @code |
| * ReturnOnFailure(channel->SendMsg(msg)); |
| * @endcode |
| * |
| * @param[in] expr An expression to be tested. |
| */ |
| #define ReturnOnFailure(expr) \ |
| do \ |
| { \ |
| auto __err = (expr); \ |
| if (!::chip::ChipError::IsSuccess(__err)) \ |
| { \ |
| return; \ |
| } \ |
| } while (false) |
| |
| /** |
| * @def VerifyOrReturn(expr, ...) |
| * |
| * @brief |
| * Returns from the void function if expression evaluates to false |
| * |
| * Example usage: |
| * |
| * @code |
| * VerifyOrReturn(param != nullptr, LogError("param is nullptr")); |
| * @endcode |
| * |
| * @param[in] expr A Boolean expression to be evaluated. |
| * @param[in] ... Statements to execute before returning. Optional. |
| */ |
| #define VerifyOrReturn(expr, ...) \ |
| do \ |
| { \ |
| if (!(expr)) \ |
| { \ |
| __VA_ARGS__; \ |
| return; \ |
| } \ |
| } while (false) |
| |
| /** |
| * @def VerifyOrReturnError(expr, code, ...) |
| * |
| * @brief |
| * Returns a specified error code if expression evaluates to false |
| * |
| * Example usage: |
| * |
| * @code |
| * VerifyOrReturnError(param != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| * @endcode |
| * |
| * @param[in] expr A Boolean expression to be evaluated. |
| * @param[in] code A value to return if @a expr is false. |
| * @param[in] ... Statements to execute before returning. Optional. |
| */ |
| #define VerifyOrReturnError(expr, code, ...) VerifyOrReturnValue(expr, code, ##__VA_ARGS__) |
| |
| /** |
| * @def VerifyOrReturnValue(expr, value, ...) |
| * |
| * @brief |
| * Returns a specified value if expression evaluates to false |
| * |
| * Example usage: |
| * |
| * @code |
| * VerifyOrReturnError(param != nullptr, Foo()); |
| * @endcode |
| * |
| * @param[in] expr A Boolean expression to be evaluated. |
| * @param[in] value A value to return if @a expr is false. |
| * @param[in] ... Statements to execute before returning. Optional. |
| */ |
| #define VerifyOrReturnValue(expr, value, ...) \ |
| do \ |
| { \ |
| if (!(expr)) \ |
| { \ |
| __VA_ARGS__; \ |
| return (value); \ |
| } \ |
| } while (false) |
| |
| /** |
| * @def VerifyOrReturnLogError(expr, code) |
| * |
| * @brief |
| * Returns and print a specified error code if expression evaluates to false |
| * |
| * Example usage: |
| * |
| * @code |
| * VerifyOrReturnLogError(param != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| * @endcode |
| * |
| * @param[in] expr A Boolean expression to be evaluated. |
| * @param[in] code A value to return if @a expr is false. |
| */ |
| #if CHIP_CONFIG_ERROR_SOURCE |
| #define VerifyOrReturnLogError(expr, code) \ |
| do \ |
| { \ |
| if (!(expr)) \ |
| { \ |
| ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(code), __FILE__, __LINE__); \ |
| return code; \ |
| } \ |
| } while (false) |
| #else // CHIP_CONFIG_ERROR_SOURCE |
| #define VerifyOrReturnLogError(expr, code) \ |
| do \ |
| { \ |
| if (!(expr)) \ |
| { \ |
| ChipLogError(NotSpecified, "%s:%d false: %" CHIP_ERROR_FORMAT, #expr, __LINE__, code.Format()); \ |
| return code; \ |
| } \ |
| } while (false) |
| #endif // CHIP_CONFIG_ERROR_SOURCE |
| |
| /** |
| * @def ReturnErrorCodeIf(expr, code) |
| * |
| * @brief |
| * Returns a specified error code if expression evaluates to true |
| * |
| * Example usage: |
| * |
| * @code |
| * ReturnErrorCodeIf(state == kInitialized, CHIP_NO_ERROR); |
| * ReturnErrorCodeIf(state == kInitialized, CHIP_ERROR_INCORRECT_STATE); |
| * @endcode |
| * |
| * @param[in] expr A Boolean expression to be evaluated. |
| * @param[in] code A value to return if @a expr is false. |
| */ |
| #define ReturnErrorCodeIf(expr, code) \ |
| do \ |
| { \ |
| if (expr) \ |
| { \ |
| return code; \ |
| } \ |
| } while (false) |
| |
| /** |
| * @def SuccessOrExit(aStatus) |
| * |
| * @brief |
| * This checks for the specified status, which is expected to |
| * commonly be successful (CHIP_NO_ERROR), and branches to |
| * the local label 'exit' if the status is unsuccessful. |
| * |
| * Example Usage: |
| * |
| * @code |
| * CHIP_ERROR TryHard() |
| * { |
| * CHIP_ERROR err; |
| * |
| * err = TrySomething(); |
| * SuccessOrExit(err); |
| * |
| * err = TrySomethingElse(); |
| * SuccessOrExit(err); |
| * |
| * exit: |
| * return err; |
| * } |
| * @endcode |
| * |
| * @param[in] aStatus A scalar status to be evaluated against zero (0). |
| * |
| */ |
| #define SuccessOrExit(aStatus) nlEXPECT(::chip::ChipError::IsSuccess((aStatus)), exit) |
| |
| /** |
| * @def VerifyOrExit(aCondition, anAction) |
| * |
| * @brief |
| * This checks for the specified condition, which is expected to |
| * commonly be true, and both executes @a anAction and branches to |
| * the local label 'exit' if the condition is false. |
| * |
| * Example Usage: |
| * |
| * @code |
| * CHIP_ERROR MakeBuffer(const uint8_t *& buf) |
| * { |
| * CHIP_ERROR err = CHIP_NO_ERROR; |
| * |
| * buf = (uint8_t *)malloc(1024); |
| * VerifyOrExit(buf != NULL, err = CHIP_ERROR_NO_MEMORY); |
| * |
| * memset(buf, 0, 1024); |
| * |
| * exit: |
| * return err; |
| * } |
| * @endcode |
| * |
| * @param[in] aCondition A Boolean expression to be evaluated. |
| * @param[in] anAction An expression or block to execute when the |
| * assertion fails. |
| * |
| */ |
| #define VerifyOrExit(aCondition, anAction) nlEXPECT_ACTION(aCondition, exit, anAction) |
| |
| /** |
| * @def ExitNow(...) |
| * |
| * @brief |
| * This unconditionally executes @a ... and branches to the local |
| * label 'exit'. |
| * |
| * @note The use of this interface implies neither success nor |
| * failure for the overall exit status of the enclosing function |
| * body. |
| * |
| * Example Usage: |
| * |
| * @code |
| * CHIP_ERROR ReadAll(Reader& reader) |
| * { |
| * CHIP_ERROR err; |
| * |
| * while (true) |
| * { |
| * err = reader.ReadNext(); |
| * if (err == CHIP_ERROR_AT_END) |
| * ExitNow(err = CHIP_NO_ERROR); |
| * SuccessOrExit(err); |
| * DoSomething(); |
| * } |
| * |
| * exit: |
| * return err; |
| * } |
| * @endcode |
| * |
| * @param[in] ... Statements to execute. Optional. |
| */ |
| // clang-format off |
| #define ExitNow(...) \ |
| do { \ |
| __VA_ARGS__; \ |
| goto exit; \ |
| } while (0) |
| // clang-format on |
| |
| /** |
| * @brief |
| * This is invoked when a #VerifyOrDie or #VerifyOrDieWithMsg |
| * assertion expression evaluates to false. |
| * |
| * Developers may override and customize this by defining #chipDie |
| * before CodeUtils.h is included by the preprocessor. |
| * |
| * Example Usage: |
| * |
| * @code |
| * chipDie(); |
| * @endcode |
| * |
| */ |
| #ifndef chipAbort |
| extern "C" void chipAbort(void) __attribute((noreturn)); |
| |
| inline void chipAbort(void) |
| { |
| while (true) |
| { |
| // NL_ASSERT_ABORT is redefined to be chipAbort, so not useful here. |
| CHIP_CONFIG_ABORT(); |
| } |
| } |
| #endif // chipAbort |
| #ifndef chipDie |
| extern "C" void chipDie(void) __attribute((noreturn)); |
| |
| inline void chipDie(void) |
| { |
| ChipLogError(NotSpecified, "chipDie chipDie chipDie"); |
| chipAbort(); |
| } |
| #endif // chipDie |
| |
| /** |
| * @def VerifyOrDie(aCondition) |
| * |
| * @brief |
| * This checks for the specified condition, which is expected to |
| * commonly be true and forces an immediate abort if the condition |
| * is false. |
| * |
| * Example Usage: |
| * |
| * @code |
| * void FreeBuffer(const uint8_t *buf) |
| * { |
| * VerifyOrDie(buf != NULL); |
| * free(buf); |
| * } |
| * @endcode |
| * |
| * @param[in] aCondition A Boolean expression to be evaluated. |
| * |
| * @sa #VerifyOrDieWithMsg |
| * @sa #chipDie |
| * |
| */ |
| #if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE |
| #define VerifyOrDie(aCondition) \ |
| nlABORT_ACTION(aCondition, ChipLogDetail(Support, "VerifyOrDie failure at %s:%d: %s", __FILE__, __LINE__, #aCondition)) |
| #else // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE |
| #define VerifyOrDie(aCondition) nlABORT(aCondition) |
| #endif // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE |
| |
| /** |
| * @def VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) |
| * |
| * @brief |
| * This checks for the specified condition, which is expected to |
| * commonly be true and both prints @a aMessage and forces an |
| * immediate abort if the condition is false. |
| * |
| * Example Usage: |
| * |
| * @code |
| * void FreeBuffer(const uint8_t *buf) |
| * { |
| * VerifyOrDieWithMsg(buf != NULL, MemoryManagement, "Invalid pointer passed to FreeBuffer"); |
| * free(buf); |
| * } |
| * @endcode |
| * |
| * @param[in] aCondition A Boolean expression to be evaluated. |
| * @param[in] aModule A chip LogModule short-hand mnemonic identifing |
| * the logical section of code that is a |
| * source the logged message. |
| * @param[in] aMessage A pointer to a NULL-terminated C string with |
| * C Standard Library-style format specifiers |
| * containing the log message to be formatted |
| * and logged. |
| * @param[in] ... A variadic argument list whose elements should |
| * correspond to the format specifiers in @a |
| * aMessage. |
| * |
| * @sa #VerifyOrDie |
| * @sa #chipDie |
| * |
| */ |
| #define VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) \ |
| nlABORT_ACTION(aCondition, ChipLogDetail(aModule, aMessage, ##__VA_ARGS__)) |
| |
| /** |
| * @def LogErrorOnFailure(expr) |
| * |
| * @brief |
| * Logs a message if the expression returns something different than CHIP_NO_ERROR. |
| * |
| * Example usage: |
| * |
| * @code |
| * ReturnLogErrorOnFailure(channel->SendMsg(msg)); |
| * @endcode |
| * |
| * @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR. |
| */ |
| #define LogErrorOnFailure(expr) \ |
| do \ |
| { \ |
| CHIP_ERROR __err = (expr); \ |
| if (__err != CHIP_NO_ERROR) \ |
| { \ |
| ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \ |
| } \ |
| } while (false) |
| |
| /** |
| * @def VerifyOrDo(expr, ...) |
| * |
| * @brief |
| * do something if expression evaluates to false |
| * |
| * Example usage: |
| * |
| * @code |
| * VerifyOrDo(param != nullptr, LogError("param is nullptr")); |
| * @endcode |
| * |
| * @param[in] expr A Boolean expression to be evaluated. |
| * @param[in] ... Statements to execute. |
| */ |
| #define VerifyOrDo(expr, ...) \ |
| do \ |
| { \ |
| if (!(expr)) \ |
| { \ |
| __VA_ARGS__; \ |
| } \ |
| } while (false) |
| |
| #if (__cplusplus >= 201103L) |
| |
| #ifndef __FINAL |
| #define __FINAL final |
| #endif |
| |
| #ifndef __OVERRIDE |
| #define __OVERRIDE override |
| #endif |
| |
| #ifndef __CONSTEXPR |
| #define __CONSTEXPR constexpr |
| #endif |
| |
| #else |
| |
| #ifndef __FINAL |
| #define __FINAL |
| #endif |
| |
| #ifndef __OVERRIDE |
| #define __OVERRIDE |
| #endif |
| |
| #ifndef __CONSTEXPR |
| #define __CONSTEXPR constexpr |
| #endif |
| |
| #endif // (__cplusplus >= 201103L) |
| |
| #if ((__cplusplus >= 201703L) || (defined(__GNUC__) && (__GNUC__ >= 7)) || (defined(__clang__)) && (__clang_major__ >= 4)) |
| #define CHECK_RETURN_VALUE [[nodiscard]] |
| #elif defined(__GNUC__) && (__GNUC__ >= 4) |
| #define CHECK_RETURN_VALUE __attribute__((warn_unused_result)) |
| #elif defined(_MSC_VER) && (_MSC_VER >= 1700) |
| #define CHECK_RETURN_VALUE _Check_return_ |
| #else |
| #define CHECK_RETURN_VALUE |
| #endif |
| |
| #if defined(__clang__) |
| #define FALLTHROUGH [[clang::fallthrough]] |
| #elif defined(__GNUC__) |
| #define FALLTHROUGH __attribute__((fallthrough)) |
| #else |
| #define FALLTHROUGH (void) 0 |
| #endif |
| |
| /** |
| * @def ArraySize(aArray) |
| * |
| * @brief |
| * Returns the size of an array in number of elements. |
| * |
| * Example Usage: |
| * |
| * @code |
| * int numbers[10]; |
| * SortNumbers(numbers, ArraySize(numbers)); |
| * @endcode |
| * |
| * @return The size of an array in number of elements. |
| * |
| * @note Clever template-based solutions seem to fail when ArraySize is used |
| * with a variable-length array argument, so we just do the C-compatible |
| * thing in C++ as well. |
| */ |
| #define ArraySize(a) (sizeof(a) / sizeof((a)[0])) |
| |
| /** |
| * @brief Ensures that if `str` is NULL, a non-null `default_str_value` is provided |
| * |
| * @param str - null-terminated string pointer or nullptr |
| * @param default_str_value - replacement value if `str` is nullptr |
| * @return `str` if not null, otherwise `default_str_value` |
| */ |
| inline const char * DefaultStringWhenNull(const char * str, const char * default_str_value) |
| { |
| return (str != nullptr) ? str : default_str_value; |
| } |
| |
| /** |
| * @brief Ensure that a string for a %s specifier is shown as "(null)" if null |
| * |
| * @param str - null-terminated string pointer or nullptr |
| * @return `str` if not null, otherwise literal "(null)" |
| */ |
| inline const char * StringOrNullMarker(const char * str) |
| { |
| return DefaultStringWhenNull(str, "(null)"); |
| } |
| |
| namespace chip { |
| |
| /** |
| * Utility for checking, at compile time if the array is constexpr, whether an |
| * array is sorted. Can be used for static_asserts. |
| */ |
| |
| template <typename T> |
| constexpr bool ArrayIsSorted(const T * aArray, size_t aLength) |
| { |
| if (aLength == 0 || aLength == 1) |
| { |
| return true; |
| } |
| |
| if (aArray[0] > aArray[1]) |
| { |
| return false; |
| } |
| |
| return ArrayIsSorted(aArray + 1, aLength - 1); |
| } |
| |
| template <typename T, size_t N> |
| constexpr bool ArrayIsSorted(const T (&aArray)[N]) |
| { |
| return ArrayIsSorted(aArray, N); |
| } |
| |
| } // namespace chip |