Skip to content

Commit 53d2162

Browse files
committed
lib: os: cbprintf: Add macros for static packaging
Added macro for determining if string can be statically packaged. Added macro for static packaging of a string. Added cbprintf_internal.h with internal macros to avoid spoiling API file. Additionally, added option for packaging to store first char pointer (fmt) as a standard pointer witouth 1 byte header which distinguish between storing by pointer and storing by value. This can significantly reduce storage, e.g. string without any arguments uses one word vs two word (byte header + alignment). Signed-off-by: Krzysztof Chruscinski <[email protected]>
1 parent fa21774 commit 53d2162

File tree

3 files changed

+308
-7
lines changed

3 files changed

+308
-7
lines changed

include/sys/cbprintf.h

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <stddef.h>
1212
#include <stdint.h>
1313
#include <toolchain.h>
14+
#include <sys/cbprintf_internal.h>
1415

1516
#ifdef __cplusplus
1617
extern "C" {
@@ -22,6 +23,14 @@ extern "C" {
2223
* @{
2324
*/
2425

26+
/**@defgroup CBPRINTF_PACKAGE_FLAGS packaging flags.
27+
* @{ */
28+
29+
/** @brief Indicate that format string is packaged as pointer. */
30+
#define CBPRINTF_PACKAGE_FMT_AS_PTR BIT(0)
31+
32+
/**@} */
33+
2534
/** @brief Signature for a cbprintf callback function.
2635
*
2736
* This function expects two parameters:
@@ -41,6 +50,62 @@ extern "C" {
4150
*/
4251
typedef int (*cbprintf_cb)(/* int c, void *ctx */);
4352

53+
/** @brief Determine if string must be packaged in run time.
54+
*
55+
* Static packaging can be applied if size and of the package can be determined
56+
* at compile time. In general, package size can be determined at compile time
57+
* if there are no string arguments which might be copied into package body if
58+
* they are considered transient.
59+
*
60+
* @param skip number of read only string arguments in the parameter list. It
61+
* shall be non-zero if there are known read only string arguments present
62+
* in the string (e.g. function name).
63+
* @param ... String with arguments.
64+
*
65+
* @retval 1 if string must be packaged in run time.
66+
* @retval 0 string can be statically packaged.
67+
*/
68+
#define CBPRINTF_MUST_RUNTIME_PACKAGE(skip, .../* fmt, ... */) \
69+
Z_CBPRINTF_MUST_RUNTIME_PACKAGE(skip, __VA_ARGS__)
70+
71+
/** @brief Statically package string.
72+
*
73+
* Build string package based on formatted string. Macro produces same package
74+
* as if @ref cbprintf_package was used with CBPRINTF_PACKAGE_FMT_AS_PTR set.
75+
*
76+
* @param packaged pointer to where the packaged data can be stored. Pass a
77+
* null pointer to store nothing but still calculate the total space required.
78+
* The data stored here is relocatable, that is it can be moved to another
79+
* contiguous block of memory.
80+
81+
* @param len on input this must be set to the number of bytes available at @p
82+
* packaged. If @p packaged is NULL the input value is ignored. On output
83+
* the referenced value will be updated to the number of bytes required to
84+
* completely store the packed information. The @p len parameter must not be
85+
* null.
86+
*
87+
* @param fmt_as_ptr Set to 1 to package format string as void pointer without
88+
* header. It optimizes package size for read only strings. It must be a
89+
* constant value at compile time.
90+
*
91+
* @param ... formatted string with arguments. Format string must be constant.
92+
*/
93+
#define CBPRINTF_STATIC_PACKAGE(packaged, len, fmt_as_ptr, ... /* fmt, ... */) \
94+
Z_CBPRINTF_STATIC_PACKAGE(packaged, len, fmt_as_ptr, __VA_ARGS__)
95+
96+
/** @brief Calculate package size at compile time.
97+
*
98+
* @param fmt_as_ptr Set to 1 to package format string as void pointer without
99+
* header. It optimizes package size for read only strings.
100+
*
101+
* @param ... String with arguments.
102+
*
103+
* @return Calculated package size. As it is determined at compile time it can
104+
* be assigned to a const variable.
105+
*/
106+
#define CBPRINTF_STATIC_PACKAGE_SIZE(fmt_as_ptr, ... /* fmt, ... */) \
107+
Z_CBPRINTF_STATIC_PACKAGE_SIZE(fmt_as_ptr, __VA_ARGS__)
108+
44109
/** @brief Capture state required to output formatted data later.
45110
*
46111
* Like cbprintf() but instead of processing the arguments and emitting the
@@ -66,6 +131,8 @@ typedef int (*cbprintf_cb)(/* int c, void *ctx */);
66131
* completely store the packed information. The @p len parameter must not be
67132
* null.
68133
*
134+
* @param flags Flags. See @ref CBPRINTF_PACKAGE_FLAGS.
135+
*
69136
* @param format a standard ISO C format string with characters and conversion
70137
* specifications.
71138
*
@@ -78,9 +145,10 @@ typedef int (*cbprintf_cb)(/* int c, void *ctx */);
78145
* @retval -ENOSPC if @p packaged was not null and the space required to store
79146
* exceed the input value of @p *len.
80147
*/
81-
__printf_like(3, 4)
148+
__printf_like(4, 5)
82149
int cbprintf_package(uint8_t *packaged,
83150
size_t *len,
151+
uint32_t flags,
84152
const char *format,
85153
...);
86154

@@ -109,6 +177,8 @@ int cbprintf_package(uint8_t *packaged,
109177
* completely store the packed information. The @p len parameter must not be
110178
* null.
111179
*
180+
* @param flags Flags. See @ref CBPRINTF_PACKAGE_FLAGS.
181+
*
112182
* @param format a standard ISO C format string with characters and conversion
113183
* specifications.
114184
*
@@ -123,6 +193,7 @@ int cbprintf_package(uint8_t *packaged,
123193
*/
124194
int cbvprintf_package(uint8_t *packaged,
125195
size_t *len,
196+
uint32_t flags,
126197
const char *format,
127198
va_list ap);
128199

@@ -133,6 +204,8 @@ int cbvprintf_package(uint8_t *packaged,
133204
*
134205
* @param ctx context provided when invoking out
135206
*
207+
* @param flags Flags. See @ref CBPRINTF_PACKAGE_FLAGS.
208+
*
136209
* @param packaged the data required to generate the formatted output, as
137210
* captured by cbprintf_package() or cbvprintf_package().
138211
*
@@ -141,6 +214,7 @@ int cbvprintf_package(uint8_t *packaged,
141214
*/
142215
int cbpprintf(cbprintf_cb out,
143216
void *ctx,
217+
uint32_t flags,
144218
const uint8_t *packaged);
145219

146220
/** @brief *printf-like output through a callback.

include/sys/cbprintf_internal.h

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
20+
/** @brief Return 1 if argument is a pointer to char or wchar_t
21+
*
22+
* @param x argument.
23+
*
24+
* @return 1 if char * or wchar_t *, 0 otherwise.
25+
*/
26+
#define Z_CBPRINTF_IS_PCHAR(x) _Generic((x), \
27+
char *: 1, \
28+
const char *: 1, \
29+
volatile char *: 1, \
30+
const volatile char *: 1, \
31+
wchar_t *: 1, \
32+
const wchar_t *: 1, \
33+
volatile wchar_t *: 1, \
34+
const volatile wchar_t *: 1, \
35+
default: 0)
36+
37+
38+
/** @brief Calculate number of char * or wchar_t * arguments in the arguments.
39+
*
40+
* @param fmt string.
41+
*
42+
* @param ... string arguments.
43+
*
44+
* @return number of arguments which are char * or wchar_t *.
45+
*/
46+
#define Z_CBPRINTF_HAS_PCHAR_ARGS_(fmt, ...) \
47+
(FAST_FOR_EACH(Z_CBPRINTF_IS_PCHAR, (+), __VA_ARGS__))
48+
49+
/**
50+
* @brief Check if formatted string must be packaged in runtime.
51+
*
52+
* @param skip number of char/wchar_t pointers in the argument list which are
53+
* accepted for static packaging.
54+
*
55+
* @param ... String with arguments (fmt, ...).
56+
*
57+
* @retval 1 if string must be packaged at runtime.
58+
* @retval 0 if string can be statically packaged.
59+
*/
60+
#define Z_CBPRINTF_MUST_RUNTIME_PACKAGE(skip, ...) \
61+
COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), \
62+
(0), \
63+
((Z_CBPRINTF_HAS_PCHAR_ARGS_(__VA_ARGS__) - skip) > 0 ?\
64+
1 : 0))
65+
66+
/** @brief Get storage size for given argument.
67+
*
68+
* Floats are promoted to double so they use size of double, others int storage
69+
* or it's own storage size if it is bigger than int. Strings are stored in
70+
* the package with 1 byte header indicating if string is stored as pointer or
71+
* by value.
72+
*
73+
* @param x argument.
74+
*
75+
* @return Number of bytes used for storing the argument.
76+
*/
77+
#define Z_CBPRINTF_ARG_SIZE(v) \
78+
_Generic((v), void *:sizeof(void *), \
79+
float: sizeof(double), \
80+
default: (MAX(sizeof(int), sizeof((v)+0))) + \
81+
_Generic((v) + 0, char *: 1, default: 0))
82+
83+
/** @brief Get storage size in words.
84+
*
85+
* @param x argument.
86+
*
87+
* @return number of words needed for storage.
88+
*/
89+
#define Z_CBPRINTF_ARG_WSIZE(x) (Z_CBPRINTF_ARG_SIZE(x) / sizeof(int))
90+
91+
/** @brief Macro for packaging single argument.
92+
*
93+
* Macro handles special cases:
94+
* - promotions of arguments smaller than int
95+
* - promotion of float
96+
* - char * packaging which is prefixed with null byte.
97+
* - special treatment of void * which would generate warning on arithmetic
98+
* operation ((void *)ptr + 0).
99+
*/
100+
#define Z_CBPRINTF_PACK(__buf, x, _arg_wsize) do {\
101+
uint8_t *_buf = __buf; \
102+
*_buf = 0; \
103+
_buf += _Generic((x), \
104+
void *: 0, \
105+
default: _Generic((x)+0, \
106+
char *:1, \
107+
volatile char *: 1, \
108+
default: 0)); \
109+
double _d = _Generic((x), \
110+
float: (x), \
111+
default: 0.0); \
112+
(void)_d; \
113+
int _i = _Generic((x), \
114+
short: (x), \
115+
default: 0); \
116+
(void)_i; \
117+
void *_v = _Generic((x), \
118+
void *: (x), \
119+
default: NULL); \
120+
(void)_v; \
121+
__auto_type _a = _Generic((x), \
122+
void *: NULL, \
123+
default: (x) + 0); \
124+
z_cbprintf_wcpy(_buf, \
125+
(void *)_Generic((x), \
126+
float: &_d, \
127+
short: &_i, \
128+
void *: &_v, \
129+
default: &_a), \
130+
_arg_wsize ); \
131+
} while (0)
132+
133+
/** @brief Safely package arguments to a buffer.
134+
*
135+
* Argument is put into the buffer if capable buffer is provided. Length is
136+
* incremented even if data is not packaged.
137+
*
138+
* @param _buf buffer.
139+
*
140+
* @param _idx index. Index is postincremented.
141+
*
142+
* @param _max maximum index (buffer capacity).
143+
*
144+
* @param _arg argument.
145+
*/
146+
#define Z_CBPRINTF_PACK_ARG(_buf, _idx, _max, _arg) \
147+
do { \
148+
uint32_t _arg_size = Z_CBPRINTF_ARG_SIZE(_arg); \
149+
uint32_t _arg_wsize = _arg_size / sizeof(uint32_t); \
150+
if (_buf && _idx < _max) { \
151+
Z_CBPRINTF_PACK(&_buf[_idx], _arg, _arg_wsize); \
152+
} \
153+
_idx += _arg_size; \
154+
} while (0)
155+
156+
/** @brief Package single argument.
157+
*
158+
* Macro is called in a loop for each argument in the string.
159+
*
160+
* @param arg argument.
161+
*/
162+
#define Z_CBPRINTF_LOOP_PACK_ARG(arg) \
163+
do { \
164+
Z_CBPRINTF_PACK_ARG(__package_buf, __package_len, \
165+
__package_max, arg); \
166+
} while (0)
167+
168+
/** @brief Statically package a formatted string with arguments.
169+
*
170+
* @param buf buffer. If null then only length is calculated.
171+
*
172+
* @param len buffer capacity on input, package size on output.
173+
*
174+
* @param fmt_as_ptr Flag indicating that format string is stored as void
175+
* pointer.
176+
*
177+
* @param ... String with variable list of arguments.
178+
*/
179+
#define Z_CBPRINTF_STATIC_PACKAGE(buf, len, fmt_as_ptr, ... /* fmt, ... */) \
180+
do { \
181+
uint8_t *__package_buf = buf; \
182+
size_t __package_max = (buf != NULL) ? len : SIZE_MAX; \
183+
size_t __package_len = 0; \
184+
FAST_FOR_EACH(Z_CBPRINTF_LOOP_PACK_ARG, (;), \
185+
IF_ENABLED(fmt_as_ptr, ((uint8_t *)))__VA_ARGS__); \
186+
len = __package_len; \
187+
} while (0)
188+
189+
#define Z_CBPRINTF_FMT_SIZE(fmt_as_ptr) (sizeof(void *) + (fmt_as_ptr ? 0 : 1))
190+
191+
/** @brief Calculate package size. 0 is retuned if only null pointer is given.*/
192+
#define Z_CBPRINTF_STATIC_PACKAGE_SIZE(fmt_as_ptr, ...) \
193+
COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), \
194+
(FAST_GET_ARG_N(1, __VA_ARGS__) == NULL ? \
195+
0 : Z_CBPRINTF_FMT_SIZE(fmt_as_ptr)), \
196+
(Z_CBPRINTF_FMT_SIZE(fmt_as_ptr) + \
197+
FAST_FOR_EACH(Z_CBPRINTF_ARG_SIZE, \
198+
(+), FAST_GET_ARGS_LESS_N(1,__VA_ARGS__))))
199+
200+
static inline void z_cbprintf_wcpy(void *dst, void *src, uint32_t wlen)
201+
{
202+
uint32_t *dst32 = dst;
203+
uint32_t *src32 = src;
204+
205+
for (uint32_t i = 0; i < wlen; i++) {
206+
dst32[i] = src32[i];
207+
}
208+
}
209+
210+
#ifdef __cplusplus
211+
}
212+
#endif
213+
214+
215+
#endif /* ZEPHYR_INCLUDE_SYS_CBPRINTF_INTERNAL_H_ */

0 commit comments

Comments
 (0)