|
| 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 <stdarg.h> |
| 11 | +#include <stddef.h> |
| 12 | +#include <stdint.h> |
| 13 | +#include <toolchain.h> |
| 14 | +#include <sys/util.h> |
| 15 | +#include <sys/__assert.h> |
| 16 | + |
| 17 | +#ifdef __cplusplus |
| 18 | +extern "C" { |
| 19 | +#endif |
| 20 | + |
| 21 | +/* |
| 22 | + * Special alignment cases |
| 23 | + */ |
| 24 | + |
| 25 | +#if defined(__i386__) |
| 26 | +/* there are no gaps on the stack */ |
| 27 | +#define VA_STACK_ALIGN(type) 1 |
| 28 | +#elif defined(__sparc__) |
| 29 | +/* there are no gaps on the stack */ |
| 30 | +#define VA_STACK_ALIGN(type) 1 |
| 31 | +#elif defined(__x86_64__) |
| 32 | +#define VA_STACK_MIN_ALIGN 8 |
| 33 | +#elif defined(__aarch64__) |
| 34 | +#define VA_STACK_MIN_ALIGN 8 |
| 35 | +#elif defined(__riscv) |
| 36 | +#define VA_STACK_MIN_ALIGN (__riscv_xlen / 8) |
| 37 | +#endif |
| 38 | + |
| 39 | +/* |
| 40 | + * Default alignment values if not specified by architecture config |
| 41 | + */ |
| 42 | + |
| 43 | +#ifndef VA_STACK_MIN_ALIGN |
| 44 | +#define VA_STACK_MIN_ALIGN 1 |
| 45 | +#endif |
| 46 | + |
| 47 | +#ifndef VA_STACK_ALIGN |
| 48 | +#define VA_STACK_ALIGN(type) MAX(VA_STACK_MIN_ALIGN, __alignof__(type)) |
| 49 | +#endif |
| 50 | + |
| 51 | +/** @brief Return 1 if argument is a pointer to char or wchar_t |
| 52 | + * |
| 53 | + * @param x argument. |
| 54 | + * |
| 55 | + * @return 1 if char * or wchar_t *, 0 otherwise. |
| 56 | + */ |
| 57 | +#define Z_CBPRINTF_IS_PCHAR(x) _Generic((x), \ |
| 58 | + char * : 1, \ |
| 59 | + const char * : 1, \ |
| 60 | + volatile char * : 1, \ |
| 61 | + const volatile char * : 1, \ |
| 62 | + wchar_t * : 1, \ |
| 63 | + const wchar_t * : 1, \ |
| 64 | + volatile wchar_t * : 1, \ |
| 65 | + const volatile wchar_t * : 1, \ |
| 66 | + default : \ |
| 67 | + 0) |
| 68 | + |
| 69 | +/** @brief Calculate number of char * or wchar_t * arguments in the arguments. |
| 70 | + * |
| 71 | + * @param fmt string. |
| 72 | + * |
| 73 | + * @param ... string arguments. |
| 74 | + * |
| 75 | + * @return number of arguments which are char * or wchar_t *. |
| 76 | + */ |
| 77 | +#define Z_CBPRINTF_HAS_PCHAR_ARGS(fmt, ...) \ |
| 78 | + (FOR_EACH(Z_CBPRINTF_IS_PCHAR, (+), __VA_ARGS__)) |
| 79 | + |
| 80 | +/** |
| 81 | + * @brief Check if formatted string must be packaged in runtime. |
| 82 | + * |
| 83 | + * @param skip number of char/wchar_t pointers in the argument list which are |
| 84 | + * accepted for static packaging. |
| 85 | + * |
| 86 | + * @param ... String with arguments (fmt, ...). |
| 87 | + * |
| 88 | + * @retval 1 if string must be packaged at runtime. |
| 89 | + * @retval 0 if string can be statically packaged. |
| 90 | + */ |
| 91 | +#if Z_C_GENERIC |
| 92 | +#define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(skip, ...) \ |
| 93 | + COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), \ |
| 94 | + (0), \ |
| 95 | + (((Z_CBPRINTF_HAS_PCHAR_ARGS(__VA_ARGS__) - skip) > 0))) |
| 96 | +#else |
| 97 | +#define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(skip, ...) 1 |
| 98 | +#endif |
| 99 | + |
| 100 | +/** @brief Get storage size for given argument. |
| 101 | + * |
| 102 | + * Floats are promoted to double so they use size of double, others int storage |
| 103 | + * or it's own storage size if it is bigger than int. Strings are stored in |
| 104 | + * the package with 1 byte header indicating if string is stored as pointer or |
| 105 | + * by value. |
| 106 | + * |
| 107 | + * @param x argument. |
| 108 | + * |
| 109 | + * @return Number of bytes used for storing the argument. |
| 110 | + */ |
| 111 | +#define Z_CBPRINTF_ARG_SIZE(v) \ |
| 112 | + _Generic((v), \ |
| 113 | + float : sizeof(double), \ |
| 114 | + default : \ |
| 115 | + _Generic((v), \ |
| 116 | + void * : 0, \ |
| 117 | + default : \ |
| 118 | + sizeof((v)+0) \ |
| 119 | + ) \ |
| 120 | + ) |
| 121 | + |
| 122 | +/** @brief Promote and store argument in the buffer. |
| 123 | + * |
| 124 | + * @param buf Buffer. |
| 125 | + * |
| 126 | + * @param arg Argument. |
| 127 | + */ |
| 128 | +#define Z_CBPRINTF_STORE_ARG(buf, arg) \ |
| 129 | + *_Generic((arg) + 0, \ |
| 130 | + char : (int *)buf, \ |
| 131 | + unsigned char: (int *)buf, \ |
| 132 | + short : (int *)buf, \ |
| 133 | + unsigned short : (int *)buf, \ |
| 134 | + int : (int *)buf, \ |
| 135 | + unsigned int : (unsigned int *)buf, \ |
| 136 | + long : (long *)buf, \ |
| 137 | + unsigned long : (unsigned long *)buf, \ |
| 138 | + long long : (long long *)buf, \ |
| 139 | + unsigned long long : (unsigned long long *)buf, \ |
| 140 | + float : (double *)buf, \ |
| 141 | + double : (double *)buf, \ |
| 142 | + long double : (long double *)buf, \ |
| 143 | + default : \ |
| 144 | + (const void **)buf) = arg |
| 145 | + |
| 146 | +/** @brief Return alignment needed for given argument. |
| 147 | + * |
| 148 | + * @param _arg Argument |
| 149 | + * |
| 150 | + * @return Alignment in bytes. |
| 151 | + */ |
| 152 | +#define Z_CBPRINTF_ALIGNMENT(_arg) \ |
| 153 | + MAX(_Generic((_arg) + 0, \ |
| 154 | + float : VA_STACK_ALIGN(double), \ |
| 155 | + double : VA_STACK_ALIGN(double), \ |
| 156 | + long double : VA_STACK_ALIGN(long double), \ |
| 157 | + long long : VA_STACK_ALIGN(long long), \ |
| 158 | + unsigned long long : VA_STACK_ALIGN(long long), \ |
| 159 | + default : \ |
| 160 | + __alignof__((_arg) + 0)), VA_STACK_MIN_ALIGN) |
| 161 | + |
| 162 | +/** @brief Detect long double variable. |
| 163 | + * |
| 164 | + * @param x Argument. |
| 165 | + * |
| 166 | + * @return 1 if @p x is a long double, 0 otherwise. |
| 167 | + */ |
| 168 | +#define Z_CBPRINTF_IS_LONGDOUBLE(x) \ |
| 169 | + _Generic((x) + 0, long double : 1, default : 0) |
| 170 | + |
| 171 | +/** @brief Safely package arguments to a buffer. |
| 172 | + * |
| 173 | + * Argument is put into the buffer if capable buffer is provided. Length is |
| 174 | + * incremented even if data is not packaged. |
| 175 | + * |
| 176 | + * @param _buf buffer. |
| 177 | + * |
| 178 | + * @param _idx index. Index is postincremented. |
| 179 | + * |
| 180 | + * @param _max maximum index (buffer capacity). |
| 181 | + * |
| 182 | + * @param _arg argument. |
| 183 | + */ |
| 184 | +#define Z_CBPRINTF_PACK_ARG2(_buf, _idx, _max, _arg) do { \ |
| 185 | + BUILD_ASSERT(!((sizeof(double) < VA_STACK_ALIGN(long double)) && \ |
| 186 | + Z_CBPRINTF_IS_LONGDOUBLE(_arg) && \ |
| 187 | + !IS_ENABLED(CONFIG_CBPRINTF_PACKAGE_LONGDOUBLE)),\ |
| 188 | + "Packaging of long double not enabled in Kconfig."); \ |
| 189 | + while (_idx % Z_CBPRINTF_ALIGNMENT(_arg)) { \ |
| 190 | + _idx += sizeof(int); \ |
| 191 | + }; \ |
| 192 | + uint32_t _arg_size = Z_CBPRINTF_ARG_SIZE(_arg); \ |
| 193 | + if (_buf && _idx < _max) { \ |
| 194 | + Z_CBPRINTF_STORE_ARG(&_buf[_idx], _arg); \ |
| 195 | + } \ |
| 196 | + _idx += _arg_size; \ |
| 197 | +} while (0) |
| 198 | + |
| 199 | +/** @brief Package single argument. |
| 200 | + * |
| 201 | + * Macro is called in a loop for each argument in the string. |
| 202 | + * |
| 203 | + * @param arg argument. |
| 204 | + */ |
| 205 | +#define Z_CBPRINTF_PACK_ARG(arg) \ |
| 206 | + Z_CBPRINTF_PACK_ARG2(_pbuf, _pkg_len, _pmax, arg) |
| 207 | + |
| 208 | +/** @brief Package descriptor. |
| 209 | + * |
| 210 | + * @param len Package length. |
| 211 | + * |
| 212 | + * @param str_cnt Number of strings stored in the package. |
| 213 | + */ |
| 214 | +struct z_cbprintf_desc { |
| 215 | + uint8_t len; |
| 216 | + uint8_t str_cnt; |
| 217 | +}; |
| 218 | + |
| 219 | +/** @brief Package header. */ |
| 220 | +union z_cbprintf_hdr { |
| 221 | + struct z_cbprintf_desc desc; |
| 222 | + void *raw; |
| 223 | +}; |
| 224 | + |
| 225 | +/** @brief Statically package a formatted string with arguments. |
| 226 | + * |
| 227 | + * @param buf buffer. If null then only length is calculated. |
| 228 | + * |
| 229 | + * @param _inlen buffer capacity on input. Ignored when @p buf is null. |
| 230 | + * |
| 231 | + * @param _outlen number of bytes required to store the package. |
| 232 | + * |
| 233 | + * @param ... String with variable list of arguments. |
| 234 | + */ |
| 235 | +#define Z_CBPRINTF_STATIC_PACKAGE_GENERIC(buf, _inlen, _outlen, \ |
| 236 | + ... /* fmt, ... */) \ |
| 237 | +do { \ |
| 238 | + _Pragma("GCC diagnostic push") \ |
| 239 | + _Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \ |
| 240 | + if (IS_ENABLED(CONFIG_CBPRINTF_STATIC_PACKAGE_CHECK_ALIGNMENT)) { \ |
| 241 | + __ASSERT(!((uintptr_t)buf & (CBPRINTF_PACKAGE_ALIGNMENT - 1)), \ |
| 242 | + "Buffer must be aligned."); \ |
| 243 | + } \ |
| 244 | + uint8_t *_pbuf = buf; \ |
| 245 | + size_t _pmax = (buf != NULL) ? *_inlen : SIZE_MAX; \ |
| 246 | + size_t _pkg_len = 0; \ |
| 247 | + union z_cbprintf_hdr *_len_loc; \ |
| 248 | + /* package starts with string address and field with length */ \ |
| 249 | + if (_pmax < sizeof(char *) + 2 * sizeof(uint16_t)) { \ |
| 250 | + break; \ |
| 251 | + } \ |
| 252 | + _len_loc = (union z_cbprintf_hdr *)_pbuf; \ |
| 253 | + _pkg_len += sizeof(union z_cbprintf_hdr); \ |
| 254 | + if (_pbuf) { \ |
| 255 | + *(char **)&_pbuf[_pkg_len] = GET_ARG_N(1, __VA_ARGS__); \ |
| 256 | + } \ |
| 257 | + _pkg_len += sizeof(char *); \ |
| 258 | + /* Pack remaining arguments */\ |
| 259 | + COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (), ( \ |
| 260 | + FOR_EACH(Z_CBPRINTF_PACK_ARG, (;), GET_ARGS_LESS_N(1, __VA_ARGS__));\ |
| 261 | + )) \ |
| 262 | + /* Store length */ \ |
| 263 | + _outlen = (_pkg_len > _pmax) ? -ENOSPC : _pkg_len; \ |
| 264 | + /* Store length in the header, set number of dumped strings to 0 */ \ |
| 265 | + if (_pbuf) { \ |
| 266 | + union z_cbprintf_hdr hdr = { .desc = {.len = _pkg_len }}; \ |
| 267 | + *_len_loc = hdr; \ |
| 268 | + } \ |
| 269 | + _Pragma("GCC diagnostic pop") \ |
| 270 | +} while (0) |
| 271 | + |
| 272 | +#if Z_C_GENERIC |
| 273 | +#define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, ... /* fmt, ... */) \ |
| 274 | + Z_CBPRINTF_STATIC_PACKAGE_GENERIC(packaged, inlen, outlen, __VA_ARGS__) |
| 275 | +#else |
| 276 | +#define Z_CBPRINTF_STATIC_PACKAGE(packaged, inlen, outlen, ... /* fmt, ... */) \ |
| 277 | +do { \ |
| 278 | + /* Small trick needed to avoid warning on always true */ \ |
| 279 | + if (((uintptr_t)packaged + 1) != 1) { \ |
| 280 | + outlen = cbprintf_package(packaged, inlen, __VA_ARGS__); \ |
| 281 | + } else { \ |
| 282 | + outlen = cbprintf_package(NULL, 0, __VA_ARGS__); \ |
| 283 | + } \ |
| 284 | +} while (0) |
| 285 | +#endif /* Z_C_GENERIC */ |
| 286 | + |
| 287 | +#ifdef __cplusplus |
| 288 | +} |
| 289 | +#endif |
| 290 | + |
| 291 | + |
| 292 | +#endif /* ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_ */ |
0 commit comments