1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 // This header provides internal macros used by the tokenizer module.
16 #pragma once
17
18 #include <stdint.h>
19
20 #include "pw_preprocessor/arguments.h"
21 #include "pw_tokenizer/config.h"
22
23 // The size of the argument types variable determines the number of arguments
24 // supported in tokenized strings.
25 #if PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 4
26
27 #include "pw_tokenizer/internal/argument_types_macro_4_byte.h"
28
29 // Encoding types in a uint32_t supports 14 arguments with 2 bits per argument.
30 #define PW_TOKENIZER_MAX_SUPPORTED_ARGS 14
31 #define PW_TOKENIZER_TYPE_COUNT_SIZE_BITS 4u
32 #define PW_TOKENIZER_TYPE_COUNT_MASK 0x0Fu
33
34 typedef uint32_t pw_tokenizer_ArgTypes;
35
36 #elif PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 8
37
38 #include "pw_tokenizer/internal/argument_types_macro_8_byte.h"
39
40 // Encoding types in a uint64_t supports 29 arguments with 2 bits per argument.
41 #define PW_TOKENIZER_MAX_SUPPORTED_ARGS 29
42 #define PW_TOKENIZER_TYPE_COUNT_SIZE_BITS 6u
43 #define PW_TOKENIZER_TYPE_COUNT_MASK 0x1Fu // only 5 bits will be needed
44
45 typedef uint64_t pw_tokenizer_ArgTypes;
46
47 #else
48
49 #error "Unsupported value for PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES"
50
51 #endif // PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES
52
53 // The tokenized string encoding function is a variadic function that works
54 // similarly to printf. Instead of a format string, however, the argument types
55 // are packed into a pw_tokenizer_ArgTypes.
56 //
57 // The four supported argument types are represented by two-bit argument codes.
58 // Just four types are required because only printf-compatible arguments are
59 // supported, and variadic arguments are further converted to a more limited set
60 // of types.
61 //
62 // char* values cannot be printed as pointers with %p. These arguments are
63 // always encoded as strings. To format a char* as an address, cast it to void*
64 // or an integer.
65 #define PW_TOKENIZER_ARG_TYPE_INT ((pw_tokenizer_ArgTypes)0)
66 #define PW_TOKENIZER_ARG_TYPE_INT64 ((pw_tokenizer_ArgTypes)1)
67 #define PW_TOKENIZER_ARG_TYPE_DOUBLE ((pw_tokenizer_ArgTypes)2)
68 #define PW_TOKENIZER_ARG_TYPE_STRING ((pw_tokenizer_ArgTypes)3)
69
70 // Select the int argument type based on the size of the type. Values smaller
71 // than int are promoted to int.
72 #define _PW_TOKENIZER_SELECT_INT_TYPE(type) \
73 (sizeof(type) <= sizeof(int) ? PW_TOKENIZER_ARG_TYPE_INT \
74 : PW_TOKENIZER_ARG_TYPE_INT64)
75
76 // The _PW_VARARGS_TYPE macro selects the varargs-promoted type at compile time.
77 // The macro has to be different for C and C++ because C doesn't support
78 // templates and C++ doesn't support _Generic.
79 #ifdef __cplusplus
80
81 #include <type_traits>
82
83 #define _PW_VARARGS_TYPE(arg) ::pw::tokenizer::VarargsType<decltype(arg)>()
84
85 namespace pw::tokenizer {
86
87 // This function selects the matching type enum for supported argument types.
88 template <typename T>
VarargsType()89 constexpr pw_tokenizer_ArgTypes VarargsType() {
90 using ArgType = std::decay_t<T>;
91
92 if constexpr (std::is_floating_point<ArgType>()) {
93 return PW_TOKENIZER_ARG_TYPE_DOUBLE;
94 } else if constexpr (!std::is_null_pointer<ArgType>() &&
95 std::is_convertible<ArgType, const char*>()) {
96 return PW_TOKENIZER_ARG_TYPE_STRING;
97 } else if constexpr (sizeof(ArgType) == sizeof(int64_t)) {
98 return PW_TOKENIZER_ARG_TYPE_INT64;
99 } else {
100 static_assert(sizeof(ArgType) <= sizeof(int));
101 return PW_TOKENIZER_ARG_TYPE_INT;
102 }
103 }
104
105 } // namespace pw::tokenizer
106
107 #else // C version
108
109 // This uses a C11 _Generic to select the matching enum value for each supported
110 // argument type. _Generic evaluates to the expression matching the type of the
111 // provided expression at compile time.
112 // clang-format off
113 #define _PW_VARARGS_TYPE(arg) \
114 _Generic((arg), \
115 _Bool: PW_TOKENIZER_ARG_TYPE_INT, \
116 char: PW_TOKENIZER_ARG_TYPE_INT, \
117 signed char: PW_TOKENIZER_ARG_TYPE_INT, \
118 unsigned char: PW_TOKENIZER_ARG_TYPE_INT, \
119 signed short: PW_TOKENIZER_ARG_TYPE_INT, \
120 unsigned short: PW_TOKENIZER_ARG_TYPE_INT, \
121 signed int: PW_TOKENIZER_ARG_TYPE_INT, \
122 unsigned int: PW_TOKENIZER_ARG_TYPE_INT, \
123 signed long: _PW_TOKENIZER_SELECT_INT_TYPE(signed long), \
124 unsigned long: _PW_TOKENIZER_SELECT_INT_TYPE(unsigned long), \
125 signed long long: _PW_TOKENIZER_SELECT_INT_TYPE(signed long long), \
126 unsigned long long: _PW_TOKENIZER_SELECT_INT_TYPE(unsigned long long), \
127 float: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
128 double: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
129 long double: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
130 char*: PW_TOKENIZER_ARG_TYPE_STRING, \
131 const char*: PW_TOKENIZER_ARG_TYPE_STRING, \
132 default: _PW_TOKENIZER_SELECT_INT_TYPE(void*))
133 // clang-format on
134
135 #endif // __cplusplus
136
137 #define _PW_TOKENIZER_TYPES_0() ((pw_tokenizer_ArgTypes)0)
138