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