1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_
8 #define ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_
9 
10 #include <errno.h>
11 #include <stdarg.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <zephyr/toolchain.h>
15 #include <zephyr/sys/util.h>
16 #include <zephyr/sys/__assert.h>
17 #include <zephyr/arch/cpu.h>
18 
19 /*
20  * Special alignment cases
21  */
22 
23 #if defined(__i386__)
24 /* there are no gaps on the stack */
25 #define VA_STACK_ALIGN(type)	1
26 #elif defined(__sparc__)
27 /* there are no gaps on the stack */
28 #define VA_STACK_ALIGN(type)	1
29 #elif defined(__x86_64__)
30 #define VA_STACK_MIN_ALIGN	8
31 #elif defined(__aarch64__)
32 #define VA_STACK_MIN_ALIGN	8
33 #elif defined(CONFIG_ARC)
34 #define VA_STACK_MIN_ALIGN	ARCH_STACK_PTR_ALIGN
35 #elif defined(__riscv)
36 #ifdef CONFIG_RISCV_ISA_RV32E
37 #define VA_STACK_ALIGN(type)	4
38 #else
39 #define VA_STACK_MIN_ALIGN	(__riscv_xlen / 8)
40 #endif /* CONFIG_RISCV_ISA_RV32E */
41 #endif
42 
43 /*
44  * Default alignment values if not specified by architecture config
45  */
46 
47 #ifndef VA_STACK_MIN_ALIGN
48 #define VA_STACK_MIN_ALIGN	1
49 #endif
50 
51 #ifndef VA_STACK_ALIGN
52 #define VA_STACK_ALIGN(type)	MAX(VA_STACK_MIN_ALIGN, __alignof__(type))
53 #endif
54 
z_cbprintf_wcpy(int * dst,int * src,size_t len)55 static inline void z_cbprintf_wcpy(int *dst, int *src, size_t len)
56 {
57 	for (size_t i = 0; i < len; i++) {
58 		dst[i] = src[i];
59 	}
60 }
61 
62 #include <zephyr/sys/cbprintf_cxx.h>
63 
64 #ifdef __cplusplus
65 extern "C" {
66 #endif
67 
68 
69 #if defined(__sparc__)
70 /* The SPARC V8 ABI guarantees that the arguments of a variable argument
71  * list function are stored on the stack at addresses which are 32-bit
72  * aligned. It means that variables of type unit64_t and double may not
73  * be properly aligned on the stack.
74  *
75  * The compiler is aware of the ABI and takes care of this. However,
76  * as we are directly accessing the variable argument list here, we need
77  * to take the alignment into consideration and copy 64-bit arguments
78  * as 32-bit words.
79  */
80 #define Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY	1
81 #else
82 #define Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY	0
83 #endif
84 
85 /** @brief Return 1 if argument is a pointer to char or wchar_t
86  *
87  * @param x argument.
88  *
89  * @return 1 if char * or wchar_t *, 0 otherwise.
90  */
91 #ifdef __cplusplus
92 #define Z_CBPRINTF_IS_PCHAR(x, flags) \
93 	z_cbprintf_cxx_is_pchar(x, (flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO)
94 #else
95 #define Z_CBPRINTF_IS_PCHAR(x, flags) \
96 	_Generic((x) + 0, \
97 		/* char * */ \
98 		char * : 1, \
99 		const char * : ((flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO) ? 0 : 1, \
100 		volatile char * : 1, \
101 		const volatile char * : 1, \
102 		/* unsigned char * */ \
103 		unsigned char * : 1, \
104 		const unsigned char * : ((flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO) ? 0 : 1, \
105 		volatile unsigned char * : 1, \
106 		const volatile unsigned char * : 1,\
107 		/* wchar_t * */ \
108 		wchar_t * : 1, \
109 		const wchar_t * : ((flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO) ? 0 : 1, \
110 		volatile wchar_t * : 1, \
111 		const volatile wchar_t * : 1, \
112 		default : \
113 			0)
114 #endif
115 
116 /* @brief Check if argument is a certain type of char pointer. What exectly is checked
117  * depends on @p flags. If flags is 0 then 1 is returned if @p x is a char pointer.
118  *
119  * @param idx Argument index.
120  * @param x Argument.
121  * @param flags Flags. See @p CBPRINTF_PACKAGE_FLAGS.
122  *
123  * @retval 1 if @p x is char pointer meeting criteria identified by @p flags.
124  * @retval 0 otherwise.
125  */
126 #define Z_CBPRINTF_IS_X_PCHAR(idx, x, flags) \
127 	  (idx < Z_CBPRINTF_PACKAGE_FIRST_RO_STR_CNT_GET(flags) ? \
128 		0 : Z_CBPRINTF_IS_PCHAR(x, flags))
129 
130 /** @brief Calculate number of char * or wchar_t * arguments in the arguments.
131  *
132  * @param fmt string.
133  *
134  * @param ... string arguments.
135  *
136  * @return number of arguments which are char * or wchar_t *.
137  */
138 #define Z_CBPRINTF_HAS_PCHAR_ARGS(flags, fmt, ...) \
139 	(FOR_EACH_IDX_FIXED_ARG(Z_CBPRINTF_IS_X_PCHAR, (+), flags, __VA_ARGS__))
140 
141 #define Z_CBPRINTF_PCHAR_COUNT(flags, ...) \
142 	COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), \
143 		    (0), \
144 		    (Z_CBPRINTF_HAS_PCHAR_ARGS(flags, __VA_ARGS__)))
145 
146 /**
147  * @brief Check if formatted string must be packaged in runtime.
148  *
149  * @param ... String with arguments (fmt, ...).
150  *
151  * @retval 1 if string must be packaged at runtime.
152  * @retval 0 if string can be statically packaged.
153  */
154 #if Z_C_GENERIC
155 #define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(flags, ...) ({\
156 	_Pragma("GCC diagnostic push") \
157 	_Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \
158 	int _rv; \
159 	if ((flags) & CBPRINTF_PACKAGE_ADD_RW_STR_POS) { \
160 		_rv = 0; \
161 	} else { \
162 		_rv = Z_CBPRINTF_PCHAR_COUNT(flags, __VA_ARGS__) > 0 ? 1 : 0; \
163 	} \
164 	_Pragma("GCC diagnostic pop")\
165 	_rv; \
166 })
167 #else
168 #define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(flags, ...) 1
169 #endif
170 
171 /** @brief Get storage size for given argument.
172  *
173  * Floats are promoted to double so they use size of double, others int storage
174  * or it's own storage size if it is bigger than int.
175  *
176  * @param x argument.
177  *
178  * @return Number of bytes used for storing the argument.
179  */
180 #ifdef __cplusplus
181 #define Z_CBPRINTF_ARG_SIZE(v) z_cbprintf_cxx_arg_size(v)
182 #else
183 #define Z_CBPRINTF_ARG_SIZE(v) ({\
184 	__auto_type __v = (v) + 0; \
185 	/* Static code analysis may complain about unused variable. */ \
186 	(void)__v; \
187 	size_t __arg_size = _Generic((v), \
188 		float : sizeof(double), \
189 		default : \
190 			sizeof((__v)) \
191 		); \
192 	__arg_size; \
193 })
194 #endif
195 
196 /** @brief Promote and store argument in the buffer.
197  *
198  * @param buf Buffer.
199  *
200  * @param arg Argument.
201  */
202 #ifdef __cplusplus
203 #define Z_CBPRINTF_STORE_ARG(buf, arg) z_cbprintf_cxx_store_arg(buf, arg)
204 #else
205 #define Z_CBPRINTF_STORE_ARG(buf, arg) do { \
206 	if (Z_CBPRINTF_VA_STACK_LL_DBL_MEMCPY) { \
207 		/* If required, copy arguments by word to avoid unaligned access.*/ \
208 		__auto_type _v = (arg) + 0; \
209 		double _d = _Generic((arg) + 0, \
210 				float : (arg) + 0, \
211 				default : \
212 					0.0); \
213 		/* Static code analysis may complain about unused variable. */ \
214 		(void)_v; \
215 		(void)_d; \
216 		size_t arg_size = Z_CBPRINTF_ARG_SIZE(arg); \
217 		size_t _wsize = arg_size / sizeof(int); \
218 		z_cbprintf_wcpy((int *)buf, \
219 			      (int *) _Generic((arg) + 0, float : &_d, default : &_v), \
220 			      _wsize); \
221 	} else { \
222 		*_Generic((arg) + 0, \
223 			char : (int *)buf, \
224 			unsigned char: (int *)buf, \
225 			short : (int *)buf, \
226 			unsigned short : (int *)buf, \
227 			int : (int *)buf, \
228 			unsigned int : (unsigned int *)buf, \
229 			long : (long *)buf, \
230 			unsigned long : (unsigned long *)buf, \
231 			long long : (long long *)buf, \
232 			unsigned long long : (unsigned long long *)buf, \
233 			float : (double *)buf, \
234 			double : (double *)buf, \
235 			long double : (long double *)buf, \
236 			default : \
237 				(const void **)buf) = arg; \
238 	} \
239 } while (false)
240 #endif
241 
242 /** @brief Return alignment needed for given argument.
243  *
244  * @param _arg Argument
245  *
246  * @return Alignment in bytes.
247  */
248 #ifdef __cplusplus
249 #define Z_CBPRINTF_ALIGNMENT(_arg) z_cbprintf_cxx_alignment(_arg)
250 #else
251 #define Z_CBPRINTF_ALIGNMENT(_arg) \
252 	MAX(_Generic((_arg) + 0, \
253 		float : VA_STACK_ALIGN(double), \
254 		double : VA_STACK_ALIGN(double), \
255 		long double : VA_STACK_ALIGN(long double), \
256 		long long : VA_STACK_ALIGN(long long), \
257 		unsigned long long : VA_STACK_ALIGN(long long), \
258 		default : \
259 			__alignof__((_arg) + 0)), VA_STACK_MIN_ALIGN)
260 #endif
261 
262 /** @brief Detect long double variable as a constant expression.
263  *
264  * Macro is used in static assertion. On some platforms C++ static inline
265  * template function is not a constant expression and cannot be used. In that
266  * case long double usage will not be detected.
267  *
268  * @param x Argument.
269  *
270  * @return 1 if @p x is a long double, 0 otherwise.
271  */
272 #ifdef __cplusplus
273 #if defined(__x86_64__) || defined(__riscv) || defined(__aarch64__)
274 #define Z_CBPRINTF_IS_LONGDOUBLE(x) 0
275 #else
276 #define Z_CBPRINTF_IS_LONGDOUBLE(x) z_cbprintf_cxx_is_longdouble(x)
277 #endif
278 #else
279 #define Z_CBPRINTF_IS_LONGDOUBLE(x) \
280 	_Generic((x) + 0, long double : 1, default : 0)
281 #endif
282 
283 /** @brief Safely package arguments to a buffer.
284  *
285  * Argument is put into the buffer if capable buffer is provided. Length is
286  * incremented even if data is not packaged.
287  *
288  * @param _buf buffer.
289  *
290  * @param _idx index. Index is postincremented.
291  *
292  * @param _align_offset Current index with alignment offset.
293  *
294  * @param _max maximum index (buffer capacity).
295  *
296  * @param _arg argument.
297  */
298 #define Z_CBPRINTF_PACK_ARG2(arg_idx, _buf, _idx, _align_offset, _max, _arg) \
299 do { \
300 	BUILD_ASSERT(!((sizeof(double) < VA_STACK_ALIGN(long double)) && \
301 			Z_CBPRINTF_IS_LONGDOUBLE(_arg) && \
302 			!IS_ENABLED(CONFIG_CBPRINTF_PACKAGE_LONGDOUBLE)),\
303 			"Packaging of long double not enabled in Kconfig."); \
304 	while (_align_offset % Z_CBPRINTF_ALIGNMENT(_arg) != 0UL) { \
305 		_idx += sizeof(int); \
306 		_align_offset += sizeof(int); \
307 	} \
308 	uint32_t _arg_size = Z_CBPRINTF_ARG_SIZE(_arg); \
309 	uint32_t _loc = _idx / sizeof(int); \
310 	if (arg_idx < 1 + _fros_cnt) { \
311 		if (_ros_pos_en) { \
312 			_ros_pos_buf[_ros_pos_idx++] = _loc; \
313 		} \
314 	} else if (Z_CBPRINTF_IS_PCHAR(_arg, 0)) { \
315 		if (_cros_en) { \
316 			if (Z_CBPRINTF_IS_X_PCHAR(arg_idx, _arg, _flags)) { \
317 				if (_rws_pos_en) { \
318 					_rws_buffer[_rws_pos_idx++] = arg_idx - 1; \
319 					_rws_buffer[_rws_pos_idx++] = _loc; \
320 				} \
321 			} else { \
322 				if (_ros_pos_en) { \
323 					_ros_pos_buf[_ros_pos_idx++] = _loc; \
324 				} \
325 			} \
326 		} else if (_rws_pos_en) { \
327 			_rws_buffer[_rws_pos_idx++] = arg_idx - 1; \
328 			_rws_buffer[_rws_pos_idx++] = _idx / sizeof(int); \
329 		} \
330 	} \
331 	if (_buf && _idx < (int)_max) { \
332 		Z_CBPRINTF_STORE_ARG(&_buf[_idx], _arg); \
333 	} \
334 	_idx += _arg_size; \
335 	_align_offset += _arg_size; \
336 } while (false)
337 
338 /** @brief Package single argument.
339  *
340  * Macro is called in a loop for each argument in the string.
341  *
342  * @param arg argument.
343  */
344 #define Z_CBPRINTF_PACK_ARG(arg_idx, arg) \
345 	Z_CBPRINTF_PACK_ARG2(arg_idx, _pbuf, _pkg_len, _pkg_offset, _pmax, arg)
346 
347 /* When using clang additional warning needs to be suppressed since each
348  * argument of fmt string is used for sizeof() which results in the warning
349  * if argument is a string literal. Suppression is added here instead of
350  * the macro which generates the warning to not slow down the compiler.
351  */
352 #ifdef __clang__
353 #define Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY \
354 	_Pragma("GCC diagnostic ignored \"-Wsizeof-array-decay\"")
355 #else
356 #define Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY
357 #endif
358 
359 /* Allocation to avoid using VLA and alloca. Alloc frees space when leaving
360  * a function which can lead to increased stack usage if logging is used
361  * multiple times. VLA is not always available.
362  *
363  * Use large array when optimization is off to avoid increased stack usage.
364  */
365 #ifdef CONFIG_NO_OPTIMIZATIONS
366 #define Z_CBPRINTF_ON_STACK_ALLOC(_name, _len) \
367 	__ASSERT(_len <= 32, "Too many string arguments."); \
368 	uint8_t _name##_buf32[32]; \
369 	_name = _name##_buf32
370 #else
371 #define Z_CBPRINTF_ON_STACK_ALLOC(_name, _len) \
372 	__ASSERT(_len <= 32, "Too many string arguments."); \
373 	uint8_t _name##_buf4[4]; \
374 	uint8_t _name##_buf8[8]; \
375 	uint8_t _name##_buf12[12]; \
376 	uint8_t _name##_buf16[16]; \
377 	uint8_t _name##_buf32[32]; \
378 	_name = (_len) <= 4 ? _name##_buf4 : \
379 		((_len) <= 8 ? _name##_buf8 : \
380 		((_len) <= 12 ? _name##_buf12 : \
381 		((_len) <= 16 ? _name##_buf16 : \
382 		 _name##_buf32)))
383 #endif
384 
385 /* When the first argument of Z_CBPRINTF_STATIC_PACKAGE_GENERIC() is a
386  * static memory location, some compiler warns you if you compare the
387  * location against NULL.  ___is_null() is used to kill this warning.
388  *
389  * The warnings would be visible when you built with -save-temps=obj,
390  * our standard debugging tip for macro problems.
391  *
392  * https://github.com/zephyrproject-rtos/zephyr/issues/51528
393  */
___is_null(void * p)394 static ALWAYS_INLINE bool ___is_null(void *p)
395 {
396 	return p == NULL;
397 }
398 
399 /** @brief Statically package a formatted string with arguments.
400  *
401  * @param buf buffer. If null then only length is calculated.
402  *
403  * @param _inlen buffer capacity on input. Ignored when @p buf is null.
404  *
405  * @param _outlen number of bytes required to store the package.
406  *
407  * @param _align_offset Input buffer alignment offset in words. Where offset 0
408  * means that buffer is aligned to CBPRINTF_PACKAGE_ALIGNMENT.
409  *
410  * @param flags Option flags. See @ref CBPRINTF_PACKAGE_FLAGS.
411  *
412  * @param ... String with variable list of arguments.
413  */
414 #define Z_CBPRINTF_STATIC_PACKAGE_GENERIC(buf, _inlen, _outlen, _align_offset, \
415 					  flags, ... /* fmt, ... */) \
416 do { \
417 	_Pragma("GCC diagnostic push") \
418 	_Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \
419 	Z_CBPRINTF_SUPPRESS_SIZEOF_ARRAY_DECAY \
420 	BUILD_ASSERT(!IS_ENABLED(CONFIG_XTENSA) || \
421 		     (IS_ENABLED(CONFIG_XTENSA) && \
422 		      !(_align_offset % CBPRINTF_PACKAGE_ALIGNMENT)), \
423 			"Xtensa requires aligned package."); \
424 	BUILD_ASSERT((_align_offset % sizeof(int)) == 0, \
425 			"Alignment offset must be multiply of a word."); \
426 	IF_ENABLED(CONFIG_CBPRINTF_STATIC_PACKAGE_CHECK_ALIGNMENT, \
427 		(__ASSERT(!((uintptr_t)buf & (CBPRINTF_PACKAGE_ALIGNMENT - 1)), \
428 			  "Buffer must be aligned.");)) \
429 	uint32_t _flags = flags; \
430 	bool _ros_pos_en = (_flags) & CBPRINTF_PACKAGE_ADD_RO_STR_POS; \
431 	bool _rws_pos_en = (_flags) & CBPRINTF_PACKAGE_ADD_RW_STR_POS; \
432 	bool _cros_en = (_flags) & CBPRINTF_PACKAGE_CONST_CHAR_RO; \
433 	uint8_t *_pbuf = buf; \
434 	uint8_t _rws_pos_idx = 0; \
435 	uint8_t _ros_pos_idx = 0; \
436 	/* Variable holds count of all string pointer arguments. */ \
437 	uint8_t _alls_cnt = Z_CBPRINTF_PCHAR_COUNT(0, __VA_ARGS__); \
438 	uint8_t _fros_cnt = Z_CBPRINTF_PACKAGE_FIRST_RO_STR_CNT_GET(_flags); \
439 	/* Variable holds count of non const string pointers. */ \
440 	uint8_t _rws_cnt = _cros_en ? \
441 		Z_CBPRINTF_PCHAR_COUNT(_flags, __VA_ARGS__) : _alls_cnt - _fros_cnt; \
442 	uint8_t _ros_cnt = _ros_pos_en ? (1 + _alls_cnt - _rws_cnt) : 0; \
443 	uint8_t *_ros_pos_buf; \
444 	Z_CBPRINTF_ON_STACK_ALLOC(_ros_pos_buf, _ros_cnt); \
445 	uint8_t *_rws_buffer; \
446 	Z_CBPRINTF_ON_STACK_ALLOC(_rws_buffer, 2 * _rws_cnt); \
447 	size_t _pmax = !___is_null(buf) ? _inlen : INT32_MAX; \
448 	int _pkg_len = 0; \
449 	int _total_len = 0; \
450 	int _pkg_offset = _align_offset; \
451 	union cbprintf_package_hdr *_len_loc; \
452 	/* If string has rw string arguments CBPRINTF_PACKAGE_ADD_RW_STR_POS is a must. */ \
453 	if (_rws_cnt && !((_flags) & CBPRINTF_PACKAGE_ADD_RW_STR_POS)) { \
454 		_outlen = -EINVAL; \
455 		break; \
456 	} \
457 	/* package starts with string address and field with length */ \
458 	if (_pmax < sizeof(*_len_loc)) { \
459 		_outlen = -ENOSPC; \
460 		break; \
461 	} \
462 	_len_loc = (union cbprintf_package_hdr *)_pbuf; \
463 	_pkg_len += sizeof(*_len_loc); \
464 	_pkg_offset += sizeof(*_len_loc); \
465 	/* Pack remaining arguments */\
466 	FOR_EACH_IDX(Z_CBPRINTF_PACK_ARG, (;), __VA_ARGS__);\
467 	_total_len = _pkg_len; \
468 	/* Append string indexes to the package. */ \
469 	_total_len += _ros_cnt; \
470 	_total_len += 2 * _rws_cnt; \
471 	if (_pbuf != NULL) { \
472 		/* Append string locations. */ \
473 		uint8_t *_pbuf_loc = &_pbuf[_pkg_len]; \
474 		for (size_t _ros_idx = 0; _ros_idx < _ros_cnt; _ros_idx++) { \
475 			*_pbuf_loc++ = _ros_pos_buf[_ros_idx]; \
476 		} \
477 		for (size_t _rws_idx = 0; _rws_idx < (2 * _rws_cnt); _rws_idx++) { \
478 			*_pbuf_loc++ = _rws_buffer[_rws_idx]; \
479 		} \
480 	} \
481 	/* Store length */ \
482 	_outlen = (_total_len > (int)_pmax) ? -ENOSPC : _total_len; \
483 	/* Store length in the header, set number of dumped strings to 0 */ \
484 	if (_pbuf != NULL) { \
485 		union cbprintf_package_hdr pkg_hdr = { \
486 			.desc = { \
487 				.len = (uint8_t)(_pkg_len / sizeof(int)), \
488 				.str_cnt = 0, \
489 				.ro_str_cnt = _ros_cnt, \
490 				.rw_str_cnt = _rws_cnt, \
491 			} \
492 		}; \
493 		IF_ENABLED(CONFIG_CBPRINTF_PACKAGE_HEADER_STORE_CREATION_FLAGS, \
494 			   (pkg_hdr.desc.pkg_flags = flags)); \
495 		*_len_loc = pkg_hdr; \
496 	} \
497 	_Pragma("GCC diagnostic pop") \
498 } while (false)
499 
500 #if Z_C_GENERIC
501 #define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, align_offset, flags, \
502 				  ... /* fmt, ... */) \
503 	Z_CBPRINTF_STATIC_PACKAGE_GENERIC(packaged, inlen, outlen, \
504 					  align_offset, flags, __VA_ARGS__)
505 #else
506 #define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, align_offset, flags, \
507 				  ... /* fmt, ... */) \
508 do { \
509 	/* Small trick needed to avoid warning on always true */ \
510 	if (((uintptr_t)packaged + 1) != 1) { \
511 		outlen = cbprintf_package(packaged, inlen, flags, __VA_ARGS__); \
512 	} else { \
513 		outlen = cbprintf_package(NULL, align_offset, flags, __VA_ARGS__); \
514 	} \
515 } while (false)
516 #endif /* Z_C_GENERIC */
517 
518 #ifdef __cplusplus
519 }
520 #endif
521 
522 #ifdef CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS
523 #ifdef __cplusplus
524 /*
525  * Remove qualifiers like const, volatile. And also transform
526  * C++ argument reference back to its basic type.
527  */
528 #define Z_CBPRINTF_ARG_REMOVE_QUAL(arg) \
529 	z_cbprintf_cxx_remove_cv < \
530 		z_cbprintf_cxx_remove_reference < decltype(arg) > ::type \
531 	> ::type
532 
533 /*
534  * Get the type of elements in an array.
535  */
536 #define Z_CBPRINTF_CXX_ARG_ARRAY_TYPE(arg) \
537 	z_cbprintf_cxx_remove_cv < \
538 		z_cbprintf_cxx_remove_extent < decltype(arg) > ::type \
539 	> ::type
540 
541 /*
542  * Determine if incoming type is char.
543  */
544 #define Z_CBPRINTF_CXX_ARG_IS_TYPE_CHAR(type) \
545 	(z_cbprintf_cxx_is_same_type < type, \
546 	 char > :: value ? \
547 	 true : \
548 	 (z_cbprintf_cxx_is_same_type < type, \
549 	  const char > :: value ? \
550 	  true : \
551 	  (z_cbprintf_cxx_is_same_type < type, \
552 	   volatile char > :: value ? \
553 	   true : \
554 	   (z_cbprintf_cxx_is_same_type < type, \
555 	    const volatile char > :: value ? \
556 	    true : \
557 	    false))))
558 
559 /*
560  * Figure out if this is a char array since (char *) and (char[])
561  * are of different types in C++.
562  */
563 #define Z_CBPRINTF_CXX_ARG_IS_CHAR_ARRAY(arg) \
564 	(z_cbprintf_cxx_is_array < decltype(arg) > :: value ? \
565 	 (Z_CBPRINTF_CXX_ARG_IS_TYPE_CHAR(Z_CBPRINTF_CXX_ARG_ARRAY_TYPE(arg)) ? \
566 	  true : \
567 	  false) : \
568 	 false)
569 
570 /*
571  * Note that qualifiers of char * must be explicitly matched
572  * due to type matching in C++, where remove_cv() does not work.
573  */
574 #define Z_CBPRINTF_ARG_TYPE(arg) \
575 	(z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
576 	 char > ::value ? \
577 	 CBPRINTF_PACKAGE_ARG_TYPE_CHAR : \
578 	 (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
579 	  unsigned char > ::value ? \
580 	  CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_CHAR : \
581 	  (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
582 	   short > ::value ? \
583 	   CBPRINTF_PACKAGE_ARG_TYPE_SHORT : \
584 	   (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
585 	    unsigned short > ::value ? \
586 	    CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_SHORT : \
587 	    (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
588 	     int > ::value ? \
589 	     CBPRINTF_PACKAGE_ARG_TYPE_INT : \
590 	     (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
591 	      unsigned int > ::value ? \
592 	      CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_INT : \
593 	      (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
594 	       long > ::value ? \
595 	       CBPRINTF_PACKAGE_ARG_TYPE_LONG : \
596 	       (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
597 		unsigned long > ::value ? \
598 		CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_LONG : \
599 		(z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
600 		 long long > ::value ? \
601 		 CBPRINTF_PACKAGE_ARG_TYPE_LONG_LONG : \
602 		 (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
603 		  unsigned long long > ::value ? \
604 		  CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_LONG_LONG : \
605 		  (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
606 		   float > ::value ? \
607 		   CBPRINTF_PACKAGE_ARG_TYPE_FLOAT : \
608 		   (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
609 		    double > ::value ? \
610 		    CBPRINTF_PACKAGE_ARG_TYPE_DOUBLE : \
611 		    (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
612 		     long double > ::value ? \
613 		     CBPRINTF_PACKAGE_ARG_TYPE_LONG_DOUBLE : \
614 		      (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
615 		       char * > :: value ? \
616 		       CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR : \
617 		       (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
618 			const char * > :: value ? \
619 			CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR : \
620 			(z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
621 			 volatile char * > :: value ? \
622 			 CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR : \
623 			 (z_cbprintf_cxx_is_same_type < Z_CBPRINTF_ARG_REMOVE_QUAL(arg), \
624 			  const volatile char * > :: value ? \
625 			  CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR : \
626 			  (Z_CBPRINTF_CXX_ARG_IS_CHAR_ARRAY(arg) ? \
627 			   CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR : \
628 			   CBPRINTF_PACKAGE_ARG_TYPE_PTR_VOID))))))))))))))))))
629 #else
630 #define Z_CBPRINTF_ARG_TYPE(arg) \
631 	_Generic(arg, \
632 		char : CBPRINTF_PACKAGE_ARG_TYPE_CHAR, \
633 		unsigned char : CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_CHAR, \
634 		short : CBPRINTF_PACKAGE_ARG_TYPE_SHORT, \
635 		unsigned short : CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_SHORT, \
636 		int : CBPRINTF_PACKAGE_ARG_TYPE_INT, \
637 		unsigned int : CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_INT, \
638 		long : CBPRINTF_PACKAGE_ARG_TYPE_LONG, \
639 		unsigned long : CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_LONG, \
640 		long long : CBPRINTF_PACKAGE_ARG_TYPE_LONG_LONG, \
641 		unsigned long long : CBPRINTF_PACKAGE_ARG_TYPE_UNSIGNED_LONG_LONG, \
642 		float : CBPRINTF_PACKAGE_ARG_TYPE_FLOAT, \
643 		double : CBPRINTF_PACKAGE_ARG_TYPE_DOUBLE, \
644 		long double : CBPRINTF_PACKAGE_ARG_TYPE_LONG_DOUBLE, \
645 		char * : CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR, \
646 		const char * : CBPRINTF_PACKAGE_ARG_TYPE_PTR_CHAR, \
647 		void * : CBPRINTF_PACKAGE_ARG_TYPE_PTR_VOID, \
648 		default : \
649 			CBPRINTF_PACKAGE_ARG_TYPE_PTR_VOID \
650 	)
651 #endif /* _cplusplus */
652 
653 #define Z_CBPRINTF_TAGGED_EMPTY_ARGS(...) \
654 	CBPRINTF_PACKAGE_ARG_TYPE_END
655 
656 #define Z_CBPRINTF_TAGGED_ARGS_3(arg) \
657 	Z_CBPRINTF_ARG_TYPE(arg), arg
658 
659 #define Z_CBPRINTF_TAGGED_ARGS_2(...) \
660 	FOR_EACH(Z_CBPRINTF_TAGGED_ARGS_3, (,), __VA_ARGS__), \
661 	CBPRINTF_PACKAGE_ARG_TYPE_END
662 
663 #define Z_CBPRINTF_TAGGED_ARGS(_num_args, ...) \
664 	COND_CODE_0(_num_args, \
665 		    (CBPRINTF_PACKAGE_ARG_TYPE_END), \
666 		    (Z_CBPRINTF_TAGGED_ARGS_2(__VA_ARGS__)))
667 
668 #endif /* CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS */
669 
670 #endif /* ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_ */
671