Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : * Copyright (c) 2013-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 defines and implements a number of miscellaneous
22 : * templates for finding object minima and maxima and interface
23 : * macros for assertion checking.
24 : *
25 : */
26 :
27 : #pragma once
28 :
29 : #include <lib/core/CHIPConfig.h>
30 : #include <lib/core/CHIPError.h>
31 : #include <lib/core/ErrorStr.h>
32 : #include <lib/support/ObjectDump.h>
33 : #include <lib/support/VerificationMacrosNoLogging.h>
34 : #include <lib/support/logging/TextOnlyLogging.h>
35 :
36 : /**
37 : * Base-level abnormal termination.
38 : *
39 : * Terminate the program immediately, without invoking destructors, atexit callbacks, etc.
40 : * Used to implement the default `chipDie()`.
41 : *
42 : * @note
43 : * This should never be invoked directly by code outside this file.
44 : */
45 : #if !defined(CHIP_CONFIG_ABORT)
46 : #define CHIP_CONFIG_ABORT() abort()
47 : #endif
48 :
49 : /**
50 : * @name chip-specific nlassert.h Overrides
51 : *
52 : * @{
53 : *
54 : */
55 :
56 : /**
57 : * @def NL_ASSERT_ABORT()
58 : *
59 : * @brief
60 : * This implements a chip-specific override for #NL_ASSERT_ABORT *
61 : * from nlassert.h.
62 : *
63 : */
64 : #if !defined(NL_ASSERT_ABORT)
65 : #define NL_ASSERT_ABORT() chipAbort()
66 : #endif
67 :
68 : /**
69 : * @def NL_ASSERT_LOG(aPrefix, aName, aCondition, aLabel, aFile, aLine, aMessage)
70 : *
71 : * @brief
72 : * This implements a chip-specific override for \c NL_ASSERT_LOG
73 : * from nlassert.h.
74 : *
75 : * @param[in] aPrefix A pointer to a NULL-terminated C string printed
76 : * at the beginning of the logged assertion
77 : * message. Typically this is and should be
78 : * \c NL_ASSERT_PREFIX_STRING.
79 : * @param[in] aName A pointer to a NULL-terminated C string printed
80 : * following @a aPrefix that indicates what
81 : * module, program, application or subsystem
82 : * the assertion occurred in Typically this
83 : * is and should be
84 : * \c NL_ASSERT_COMPONENT_STRING.
85 : * @param[in] aCondition A pointer to a NULL-terminated C string indicating
86 : * the expression that evaluated to false in
87 : * the assertion. Typically this is a
88 : * stringified version of the actual
89 : * assertion expression.
90 : * @param[in] aLabel An optional pointer to a NULL-terminated C string
91 : * indicating, for exception-style
92 : * assertions, the label that will be
93 : * branched to when the assertion expression
94 : * evaluates to false.
95 : * @param[in] aFile A pointer to a NULL-terminated C string indicating
96 : * the file in which the exception
97 : * occurred. Typically this is and should be
98 : * \_\_FILE\_\_ from the C preprocessor.
99 : * @param[in] aLine The line number in @a aFile on which the assertion
100 : * expression evaluated to false. Typically
101 : * this is and should be \_\_LINE\_\_ from the C
102 : * preprocessor.
103 : * @param[in] aMessage An optional pointer to a NULL-terminated C string
104 : * containing a caller-specified message
105 : * further describing the assertion failure.
106 : *
107 : */
108 : // clang-format off
109 : #if !defined(NL_ASSERT_LOG)
110 : #define NL_ASSERT_LOG(aPrefix, aName, aCondition, aLabel, aFile, aLine, aMessage) \
111 : do \
112 : { \
113 : ChipLogError(NotSpecified, \
114 : NL_ASSERT_LOG_FORMAT_DEFAULT, \
115 : aPrefix, \
116 : (((aName) == 0) || (*(aName) == '\0')) ? "" : aName, \
117 : (((aName) == 0) || (*(aName) == '\0')) ? "" : ": ", \
118 : aCondition, \
119 : (((aMessage) == 0) ? "" : aMessage), \
120 : (((aMessage) == 0) ? "" : ", "), \
121 : aFile, \
122 : aLine); \
123 : } while (0)
124 : #endif
125 : // clang-format on
126 :
127 : /**
128 : * @} chip-specific nlassert.h Overrides
129 : *
130 : */
131 :
132 : #include <nlassert.h>
133 :
134 : /**
135 : * @def ReturnErrorOnFailure(expr)
136 : *
137 : * @brief
138 : * Returns the error code if the expression returns an error. For a CHIP_ERROR expression, this means any value other
139 : * than CHIP_NO_ERROR. For an integer expression, this means non-zero.
140 : *
141 : * Example usage:
142 : *
143 : * @code
144 : * ReturnErrorOnFailure(channel->SendMsg(msg));
145 : * @endcode
146 : *
147 : * @param[in] expr An expression to be tested.
148 : */
149 : #define ReturnErrorOnFailure(expr) \
150 : do \
151 : { \
152 : auto __err = (expr); \
153 : if (!::chip::ChipError::IsSuccess(__err)) \
154 : { \
155 : return __err; \
156 : } \
157 : } while (false)
158 :
159 : /**
160 : * @def ReturnLogErrorOnFailure(expr)
161 : *
162 : * @brief
163 : * Returns the error code if the expression returns something different
164 : * than CHIP_NO_ERROR.
165 : *
166 : * Example usage:
167 : *
168 : * @code
169 : * ReturnLogErrorOnFailure(channel->SendMsg(msg));
170 : * @endcode
171 : *
172 : * @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
173 : */
174 : #define ReturnLogErrorOnFailure(expr) \
175 : do \
176 : { \
177 : CHIP_ERROR __err = (expr); \
178 : if (__err != CHIP_NO_ERROR) \
179 : { \
180 : ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \
181 : return __err; \
182 : } \
183 : } while (false)
184 :
185 : /**
186 : * @def ReturnOnFailure(expr)
187 : *
188 : * @brief
189 : * Returns if the expression returns an error. For a CHIP_ERROR expression, this means any value other
190 : * than CHIP_NO_ERROR. For an integer expression, this means non-zero.
191 : *
192 : * Example usage:
193 : *
194 : * @code
195 : * ReturnOnFailure(channel->SendMsg(msg));
196 : * @endcode
197 : *
198 : * @param[in] expr An expression to be tested.
199 : */
200 : #define ReturnOnFailure(expr) \
201 : do \
202 : { \
203 : auto __err = (expr); \
204 : if (!::chip::ChipError::IsSuccess(__err)) \
205 : { \
206 : return; \
207 : } \
208 : } while (false)
209 :
210 : /**
211 : * @def VerifyOrReturn(expr, ...)
212 : *
213 : * @brief
214 : * Returns from the void function if expression evaluates to false
215 : *
216 : * Example usage:
217 : *
218 : * @code
219 : * VerifyOrReturn(param != nullptr, LogError("param is nullptr"));
220 : * @endcode
221 : *
222 : * @param[in] expr A Boolean expression to be evaluated.
223 : * @param[in] ... Statements to execute before returning. Optional.
224 : */
225 : #define VerifyOrReturn(expr, ...) \
226 : do \
227 : { \
228 : if (!(expr)) \
229 : { \
230 : __VA_ARGS__; \
231 : return; \
232 : } \
233 : } while (false)
234 :
235 : /**
236 : * @def VerifyOrReturnError(expr, code, ...)
237 : *
238 : * @brief
239 : * Returns a specified error code if expression evaluates to false
240 : *
241 : * Example usage:
242 : *
243 : * @code
244 : * VerifyOrReturnError(param != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
245 : * @endcode
246 : *
247 : * @param[in] expr A Boolean expression to be evaluated.
248 : * @param[in] code A value to return if @a expr is false.
249 : * @param[in] ... Statements to execute before returning. Optional.
250 : */
251 : #define VerifyOrReturnError(expr, code, ...) VerifyOrReturnValue(expr, code, ##__VA_ARGS__)
252 :
253 : /**
254 : * @def VerifyOrReturnValue(expr, value, ...)
255 : *
256 : * @brief
257 : * Returns a specified value if expression evaluates to false
258 : *
259 : * Example usage:
260 : *
261 : * @code
262 : * VerifyOrReturnError(param != nullptr, Foo());
263 : * @endcode
264 : *
265 : * @param[in] expr A Boolean expression to be evaluated.
266 : * @param[in] value A value to return if @a expr is false.
267 : * @param[in] ... Statements to execute before returning. Optional.
268 : */
269 : #define VerifyOrReturnValue(expr, value, ...) \
270 : do \
271 : { \
272 : if (!(expr)) \
273 : { \
274 : __VA_ARGS__; \
275 : return (value); \
276 : } \
277 : } while (false)
278 :
279 : /**
280 : * @def VerifyOrReturnLogError(expr, code)
281 : *
282 : * @brief
283 : * Returns and print a specified error code if expression evaluates to false
284 : *
285 : * Example usage:
286 : *
287 : * @code
288 : * VerifyOrReturnLogError(param != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
289 : * @endcode
290 : *
291 : * @param[in] expr A Boolean expression to be evaluated.
292 : * @param[in] code A value to return if @a expr is false.
293 : */
294 : #if CHIP_CONFIG_ERROR_SOURCE
295 : #define VerifyOrReturnLogError(expr, code) \
296 : do \
297 : { \
298 : if (!(expr)) \
299 : { \
300 : ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(code), __FILE__, __LINE__); \
301 : return code; \
302 : } \
303 : } while (false)
304 : #else // CHIP_CONFIG_ERROR_SOURCE
305 : #define VerifyOrReturnLogError(expr, code) \
306 : do \
307 : { \
308 : if (!(expr)) \
309 : { \
310 : ChipLogError(NotSpecified, "%s:%d false: %" CHIP_ERROR_FORMAT, #expr, __LINE__, code.Format()); \
311 : return code; \
312 : } \
313 : } while (false)
314 : #endif // CHIP_CONFIG_ERROR_SOURCE
315 :
316 : /**
317 : * @def SuccessOrExit(error)
318 : *
319 : * @brief
320 : * This checks for the specified error, which is expected to
321 : * commonly be successful (CHIP_NO_ERROR), and branches to
322 : * the local label 'exit' if the status is unsuccessful.
323 : *
324 : * Example Usage:
325 : *
326 : * @code
327 : * CHIP_ERROR TryHard()
328 : * {
329 : * CHIP_ERROR err;
330 : *
331 : * err = TrySomething();
332 : * SuccessOrExit(err);
333 : *
334 : * err = TrySomethingElse();
335 : * SuccessOrExit(err);
336 : *
337 : * exit:
338 : * return err;
339 : * }
340 : * @endcode
341 : *
342 : * @param[in] error A ChipError object to be evaluated against success (CHIP_NO_ERROR).
343 : *
344 : */
345 : #define SuccessOrExit(error) nlEXPECT(::chip::ChipError::IsSuccess((error)), exit)
346 :
347 : /**
348 : * @def SuccessOrExitAction(error, anAction)
349 : *
350 : * @brief
351 : * This checks for the specified error, which is expected to
352 : * commonly be successful (CHIP_NO_ERROR), and both executes
353 : * @a anAction and branches to the local label 'exit' if the
354 : * status is unsuccessful.
355 : *
356 : * @param[in] error A ChipError object to be evaluated against success (CHIP_NO_ERROR).
357 : */
358 : #define SuccessOrExitAction(error, action) nlEXPECT_ACTION(::chip::ChipError::IsSuccess((error)), exit, action)
359 :
360 : /**
361 : * @def VerifyOrExit(aCondition, anAction)
362 : *
363 : * @brief
364 : * This checks for the specified condition, which is expected to
365 : * commonly be true, and both executes @a anAction and branches to
366 : * the local label 'exit' if the condition is false.
367 : *
368 : * Example Usage:
369 : *
370 : * @code
371 : * CHIP_ERROR MakeBuffer(const uint8_t *& buf)
372 : * {
373 : * CHIP_ERROR err = CHIP_NO_ERROR;
374 : *
375 : * buf = (uint8_t *)malloc(1024);
376 : * VerifyOrExit(buf != NULL, err = CHIP_ERROR_NO_MEMORY);
377 : *
378 : * memset(buf, 0, 1024);
379 : *
380 : * exit:
381 : * return err;
382 : * }
383 : * @endcode
384 : *
385 : * @param[in] aCondition A Boolean expression to be evaluated.
386 : * @param[in] anAction An expression or block to execute when the
387 : * assertion fails.
388 : *
389 : */
390 : #define VerifyOrExit(aCondition, anAction) nlEXPECT_ACTION(aCondition, exit, anAction)
391 :
392 : /**
393 : * @def ExitNow(...)
394 : *
395 : * @brief
396 : * This unconditionally executes @a ... and branches to the local
397 : * label 'exit'.
398 : *
399 : * @note The use of this interface implies neither success nor
400 : * failure for the overall exit status of the enclosing function
401 : * body.
402 : *
403 : * Example Usage:
404 : *
405 : * @code
406 : * CHIP_ERROR ReadAll(Reader& reader)
407 : * {
408 : * CHIP_ERROR err;
409 : *
410 : * while (true)
411 : * {
412 : * err = reader.ReadNext();
413 : * if (err == CHIP_ERROR_AT_END)
414 : * ExitNow(err = CHIP_NO_ERROR);
415 : * SuccessOrExit(err);
416 : * DoSomething();
417 : * }
418 : *
419 : * exit:
420 : * return err;
421 : * }
422 : * @endcode
423 : *
424 : * @param[in] ... Statements to execute. Optional.
425 : */
426 : // clang-format off
427 : #define ExitNow(...) \
428 : do { \
429 : __VA_ARGS__; \
430 : goto exit; \
431 : } while (0)
432 : // clang-format on
433 :
434 : /**
435 : * @brief
436 : * This is invoked when a #VerifyOrDie or #VerifyOrDieWithMsg
437 : * assertion expression evaluates to false.
438 : *
439 : * Developers may override and customize this by defining #chipDie
440 : * before CodeUtils.h is included by the preprocessor.
441 : *
442 : * Example Usage:
443 : *
444 : * @code
445 : * chipDie();
446 : * @endcode
447 : *
448 : */
449 : #ifndef chipAbort
450 : extern "C" void chipAbort(void) __attribute((noreturn));
451 :
452 0 : inline void chipAbort(void)
453 : {
454 : while (true)
455 : {
456 : // NL_ASSERT_ABORT is redefined to be chipAbort, so not useful here.
457 0 : CHIP_CONFIG_ABORT();
458 : }
459 : }
460 : #endif // chipAbort
461 : #ifndef chipDie
462 : extern "C" void chipDie(void) __attribute((noreturn));
463 :
464 0 : inline void chipDie(void)
465 : {
466 0 : ChipLogError(NotSpecified, "chipDie chipDie chipDie");
467 0 : chipAbort();
468 : }
469 : #endif // chipDie
470 :
471 : /**
472 : * @def VerifyOrDie(aCondition)
473 : *
474 : * @brief
475 : * This checks for the specified condition, which is expected to
476 : * commonly be true and forces an immediate abort if the condition
477 : * is false.
478 : *
479 : * Example Usage:
480 : *
481 : * @code
482 : * void FreeBuffer(const uint8_t *buf)
483 : * {
484 : * VerifyOrDie(buf != NULL);
485 : * free(buf);
486 : * }
487 : * @endcode
488 : *
489 : * @param[in] aCondition A Boolean expression to be evaluated.
490 : *
491 : * @sa #VerifyOrDieWithMsg
492 : * @sa #chipDie
493 : *
494 : */
495 : #if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
496 : #define VerifyOrDie(aCondition) \
497 : nlABORT_ACTION(aCondition, ChipLogError(Support, "VerifyOrDie failure at %s:%d: %s", __FILE__, __LINE__, #aCondition))
498 : #else // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
499 : #define VerifyOrDie(aCondition) VerifyOrDieWithoutLogging(aCondition)
500 : #endif // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
501 :
502 : /**
503 : * @def VerifyOrDieWithObject(aCondition, aObject)
504 : *
505 : * Like VerifyOrDie(), but calls DumpObjectToLog()
506 : * on the provided object on failure before aborting
507 : * if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE is enabled.
508 : */
509 : #if CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
510 : #define VerifyOrDieWithObject(aCondition, aObject) \
511 : nlABORT_ACTION(aCondition, ::chip::DumpObjectToLog(aObject); \
512 : ChipLogError(Support, "VerifyOrDie failure at %s:%d: %s", __FILE__, __LINE__, #aCondition))
513 : #else // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
514 : #define VerifyOrDieWithObject(aCondition, aObject) VerifyOrDieWithoutLogging(aCondition)
515 : #endif // CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE
516 :
517 : /**
518 : * @def VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...)
519 : *
520 : * @brief
521 : * This checks for the specified condition, which is expected to
522 : * commonly be true and both prints @a aMessage and forces an
523 : * immediate abort if the condition is false.
524 : *
525 : * Example Usage:
526 : *
527 : * @code
528 : * void FreeBuffer(const uint8_t *buf)
529 : * {
530 : * VerifyOrDieWithMsg(buf != NULL, MemoryManagement, "Invalid pointer passed to FreeBuffer");
531 : * free(buf);
532 : * }
533 : * @endcode
534 : *
535 : * @param[in] aCondition A Boolean expression to be evaluated.
536 : * @param[in] aModule A chip LogModule short-hand mnemonic identifing
537 : * the logical section of code that is a
538 : * source the logged message.
539 : * @param[in] aMessage A pointer to a NULL-terminated C string with
540 : * C Standard Library-style format specifiers
541 : * containing the log message to be formatted
542 : * and logged.
543 : * @param[in] ... A variadic argument list whose elements should
544 : * correspond to the format specifiers in @a
545 : * aMessage.
546 : *
547 : * @sa #VerifyOrDie
548 : * @sa #chipDie
549 : *
550 : */
551 : #define VerifyOrDieWithMsg(aCondition, aModule, aMessage, ...) \
552 : nlABORT_ACTION(aCondition, ChipLogError(aModule, aMessage, ##__VA_ARGS__))
553 :
554 : /**
555 : * @def LogErrorOnFailure(expr)
556 : *
557 : * @brief
558 : * Logs a message if the expression returns something different than CHIP_NO_ERROR.
559 : *
560 : * Example usage:
561 : *
562 : * @code
563 : * ReturnLogErrorOnFailure(channel->SendMsg(msg));
564 : * @endcode
565 : *
566 : * @param[in] expr A scalar expression to be evaluated against CHIP_NO_ERROR.
567 : */
568 : #define LogErrorOnFailure(expr) \
569 : do \
570 : { \
571 : CHIP_ERROR __err = (expr); \
572 : if (__err != CHIP_NO_ERROR) \
573 : { \
574 : ChipLogError(NotSpecified, "%s at %s:%d", ErrorStr(__err), __FILE__, __LINE__); \
575 : } \
576 : } while (false)
577 :
578 : /**
579 : * @def VerifyOrDo(expr, ...)
580 : *
581 : * @brief
582 : * do something if expression evaluates to false
583 : *
584 : * Example usage:
585 : *
586 : * @code
587 : * VerifyOrDo(param != nullptr, LogError("param is nullptr"));
588 : * @endcode
589 : *
590 : * @param[in] expr A Boolean expression to be evaluated.
591 : * @param[in] ... Statements to execute.
592 : */
593 : #define VerifyOrDo(expr, ...) \
594 : do \
595 : { \
596 : if (!(expr)) \
597 : { \
598 : __VA_ARGS__; \
599 : } \
600 : } while (false)
601 :
602 : #if (__cplusplus >= 201103L)
603 :
604 : #ifndef __FINAL
605 : #define __FINAL final
606 : #endif
607 :
608 : #ifndef __OVERRIDE
609 : #define __OVERRIDE override
610 : #endif
611 :
612 : #ifndef __CONSTEXPR
613 : #define __CONSTEXPR constexpr
614 : #endif
615 :
616 : #else
617 :
618 : #ifndef __FINAL
619 : #define __FINAL
620 : #endif
621 :
622 : #ifndef __OVERRIDE
623 : #define __OVERRIDE
624 : #endif
625 :
626 : #ifndef __CONSTEXPR
627 : #define __CONSTEXPR constexpr
628 : #endif
629 :
630 : #endif // (__cplusplus >= 201103L)
631 :
632 : #if ((__cplusplus >= 201703L) || (defined(__GNUC__) && (__GNUC__ >= 7)) || (defined(__clang__)) && (__clang_major__ >= 4))
633 : #define CHECK_RETURN_VALUE [[nodiscard]]
634 : #elif defined(__GNUC__) && (__GNUC__ >= 4)
635 : #define CHECK_RETURN_VALUE __attribute__((warn_unused_result))
636 : #elif defined(_MSC_VER) && (_MSC_VER >= 1700)
637 : #define CHECK_RETURN_VALUE _Check_return_
638 : #else
639 : #define CHECK_RETURN_VALUE
640 : #endif
641 :
642 : #if defined(__clang__)
643 : #define FALLTHROUGH [[clang::fallthrough]]
644 : #elif defined(__GNUC__)
645 : #define FALLTHROUGH __attribute__((fallthrough))
646 : #else
647 : #define FALLTHROUGH (void) 0
648 : #endif
649 :
650 : /**
651 : * @def ArraySize(aArray)
652 : *
653 : * @brief
654 : * Returns the size of an array in number of elements.
655 : *
656 : * Example Usage:
657 : *
658 : * @code
659 : * int numbers[10];
660 : * SortNumbers(numbers, ArraySize(numbers));
661 : * @endcode
662 : *
663 : * @return The size of an array in number of elements.
664 : *
665 : * @note Clever template-based solutions seem to fail when ArraySize is used
666 : * with a variable-length array argument, so we just do the C-compatible
667 : * thing in C++ as well.
668 : */
669 : #define ArraySize(a) (sizeof(a) / sizeof((a)[0]))
670 :
671 : /**
672 : * @brief Ensures that if `str` is NULL, a non-null `default_str_value` is provided
673 : *
674 : * @param str - null-terminated string pointer or nullptr
675 : * @param default_str_value - replacement value if `str` is nullptr
676 : * @return `str` if not null, otherwise `default_str_value`
677 : */
678 191 : inline const char * DefaultStringWhenNull(const char * str, const char * default_str_value)
679 : {
680 191 : return (str != nullptr) ? str : default_str_value;
681 : }
682 :
683 : /**
684 : * @brief Ensure that a string for a %s specifier is shown as "(null)" if null
685 : *
686 : * @param str - null-terminated string pointer or nullptr
687 : * @return `str` if not null, otherwise literal "(null)"
688 : */
689 191 : inline const char * StringOrNullMarker(const char * str)
690 : {
691 191 : return DefaultStringWhenNull(str, "(null)");
692 : }
693 :
694 : namespace chip {
695 :
696 : /**
697 : * Utility for checking, at compile time if the array is constexpr, whether an
698 : * array is sorted. Can be used for static_asserts.
699 : */
700 :
701 : template <typename T>
702 : constexpr bool ArrayIsSorted(const T * aArray, size_t aLength)
703 : {
704 : if (aLength == 0 || aLength == 1)
705 : {
706 : return true;
707 : }
708 :
709 : if (aArray[0] > aArray[1])
710 : {
711 : return false;
712 : }
713 :
714 : return ArrayIsSorted(aArray + 1, aLength - 1);
715 : }
716 :
717 : template <typename T, size_t N>
718 : constexpr bool ArrayIsSorted(const T (&aArray)[N])
719 : {
720 : return ArrayIsSorted(aArray, N);
721 : }
722 :
723 : } // namespace chip
|