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 {
86 namespace tokenizer {
87
88 #ifdef __cpp_if_constexpr // C++17 version
89
90 // This function selects the matching type enum for supported argument types.
91 template <typename T>
VarargsType()92 constexpr pw_tokenizer_ArgTypes VarargsType() {
93 using ArgType = std::decay_t<T>;
94
95 if constexpr (std::is_floating_point<ArgType>()) {
96 return PW_TOKENIZER_ARG_TYPE_DOUBLE;
97 } else if constexpr (!std::is_null_pointer<ArgType>() &&
98 std::is_convertible<ArgType, const char*>()) {
99 return PW_TOKENIZER_ARG_TYPE_STRING;
100 } else if constexpr (sizeof(ArgType) == sizeof(int64_t)) {
101 return PW_TOKENIZER_ARG_TYPE_INT64;
102 } else {
103 static_assert(sizeof(ArgType) <= sizeof(int));
104 return PW_TOKENIZER_ARG_TYPE_INT;
105 }
106 }
107
108 #else // C++14 version
109
110 template <typename T,
111 bool kIsDouble = std::is_floating_point<T>(),
112 bool kIsString = !std::is_null_pointer<T>() &&
113 std::is_convertible<T, const char*>(),
114 bool kIsInt64 = sizeof(T) == sizeof(int64_t)>
115 struct SelectVarargsType;
116
117 template <typename T, bool kDontCare1, bool kDontCare2>
118 struct SelectVarargsType<T, true, kDontCare1, kDontCare2> {
119 static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_DOUBLE;
120 };
121
122 template <typename T, bool kDontCare>
123 struct SelectVarargsType<T, false, true, kDontCare> {
124 static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_STRING;
125 };
126
127 template <typename T>
128 struct SelectVarargsType<T, false, false, true> {
129 static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT64;
130 };
131
132 template <typename T>
133 struct SelectVarargsType<T, false, false, false> {
134 static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT;
135 };
136
137 template <typename T>
138 constexpr pw_tokenizer_ArgTypes VarargsType() {
139 return SelectVarargsType<typename std::decay<T>::type>::kValue;
140 }
141
142 #endif // __cpp_if_constexpr
143
144 } // namespace tokenizer
145 } // namespace pw
146
147 #else // C version
148
149 // This uses a C11 _Generic to select the matching enum value for each supported
150 // argument type. _Generic evaluates to the expression matching the type of the
151 // provided expression at compile time.
152 // clang-format off
153 #define _PW_VARARGS_TYPE(arg) \
154 _Generic((arg), \
155 _Bool: PW_TOKENIZER_ARG_TYPE_INT, \
156 char: PW_TOKENIZER_ARG_TYPE_INT, \
157 signed char: PW_TOKENIZER_ARG_TYPE_INT, \
158 unsigned char: PW_TOKENIZER_ARG_TYPE_INT, \
159 signed short: PW_TOKENIZER_ARG_TYPE_INT, \
160 unsigned short: PW_TOKENIZER_ARG_TYPE_INT, \
161 signed int: PW_TOKENIZER_ARG_TYPE_INT, \
162 unsigned int: PW_TOKENIZER_ARG_TYPE_INT, \
163 signed long: _PW_TOKENIZER_SELECT_INT_TYPE(signed long), \
164 unsigned long: _PW_TOKENIZER_SELECT_INT_TYPE(unsigned long), \
165 signed long long: _PW_TOKENIZER_SELECT_INT_TYPE(signed long long), \
166 unsigned long long: _PW_TOKENIZER_SELECT_INT_TYPE(unsigned long long), \
167 float: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
168 double: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
169 long double: PW_TOKENIZER_ARG_TYPE_DOUBLE, \
170 char*: PW_TOKENIZER_ARG_TYPE_STRING, \
171 const char*: PW_TOKENIZER_ARG_TYPE_STRING, \
172 default: _PW_TOKENIZER_SELECT_INT_TYPE(void*))
173 // clang-format on
174
175 #endif // __cplusplus
176
177 #define _PW_TOKENIZER_TYPES_0() ((pw_tokenizer_ArgTypes)0)
178