• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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