1717
1818#include < cstdint>
1919#include < cstring>
20+ #include < cstddef>
2021
2122#include < algorithm>
2223#include < iostream>
2324#include < utility>
25+ #include < array>
2426
2527#include " Common.h"
2628#include " Cycles.h"
4345 */
4446namespace NanoLogInternal {
4547
48+ /* *
49+ * A struct that contains a list of ParamType enum alongside with its actual
50+ * size. Mainly used as return type of getParamInfos to store the ParamType
51+ * extracted from format string.
52+ *
53+ * \tparam N
54+ * The maximum number of elements avaliable in this struct.
55+ */
56+
57+ template <std::size_t N>
58+ struct ParamTypeContainer {
59+ std::array<ParamType, N> data;
60+ std::size_t size;
61+ };
62+
4663/* *
4764 * Checks whether a character is with the terminal set of format specifier
4865 * characters according to the printf specification:
@@ -116,26 +133,29 @@ isDigit(char c) {
116133
117134/* *
118135 * Analyzes a static printf style format string and extracts type information
119- * about the p-th parameter that would be used in a corresponding NANO_LOG()
136+ * about all of the parameters that would be used in a corresponding NANO_LOG()
120137 * invocation.
121138 *
122139 * \tparam N
123140 * Length of the static format string (automatically deduced)
124141 * \param fmt
125142 * Format string to parse
126- * \param paramNum
127- * p-th parameter to return type information for (starts from zero)
128143 * \return
129- * Returns an ParamType enum describing the type of the parameter
144+ * Returns a ParamTypeContainer<N> struct that contains all ParamType info
145+ * in order. Note: The maximum avaliable size template parameter N is set
146+ * with the fact that, the number of parameters in the format string shall
147+ * be less than the length of that string. And exceeding the maximum size
148+ * limit in core constant expression anyways will cause the program to be
149+ * ill-formed.
130150 */
131- template <int N>
132- constexpr inline ParamType
133- getParamInfo (const char (&fmt)[N],
134- int paramNum=0)
151+ template <std::size_t N>
152+ constexpr inline ParamTypeContainer<N>
153+ getParamInfos (const char (&fmt)[N])
135154{
136- int pos = 0 ;
137- while (pos < N - 1 ) {
138-
155+ ParamTypeContainer<N> paramTypes = {};
156+ std::size_t pos = 0 ;
157+ std::size_t paramIdx = 0 ;
158+ while (pos + 1 < N) {
139159 // The code below searches for something that looks like a printf
140160 // specifier (i.e. something that follows the format of
141161 // %<flags><width>.<precision><length><terminal>). We only care
@@ -163,10 +183,9 @@ getParamInfo(const char (&fmt)[N],
163183
164184 // Consume width
165185 if (fmt[pos] == ' *' ) {
166- if (paramNum == 0 )
167- return ParamType::DYNAMIC_WIDTH;
186+ paramTypes.data [paramIdx] = ParamType::DYNAMIC_WIDTH;
168187
169- --paramNum ;
188+ ++paramIdx ;
170189 ++pos;
171190 } else {
172191 while (NanoLogInternal::isDigit (fmt[pos]))
@@ -180,11 +199,11 @@ getParamInfo(const char (&fmt)[N],
180199 ++pos; // consume '.'
181200
182201 if (fmt[pos] == ' *' ) {
183- if (paramNum == 0 )
184- return ParamType::DYNAMIC_PRECISION;
202+ paramTypes. data [paramIdx] =
203+ ParamType::DYNAMIC_PRECISION;
185204
186205 hasDynamicPrecision = true ;
187- --paramNum ;
206+ ++paramIdx ;
188207 ++pos;
189208 } else {
190209 precision = 0 ;
@@ -198,7 +217,7 @@ getParamInfo(const char (&fmt)[N],
198217 // consume length
199218 while (isLength (fmt[pos]))
200219 ++pos;
201-
220+
202221 // Consume terminal
203222 if (!NanoLogInternal::isTerminal (fmt[pos])) {
204223 throw std::invalid_argument (
@@ -212,128 +231,90 @@ getParamInfo(const char (&fmt)[N],
212231 " %n specifiers are not support in NanoLog!" );
213232 }
214233
215- if (paramNum != 0 ) {
216- --paramNum;
217- ++pos;
218- continue ;
234+ if (fmt[pos] != ' s' ){
235+ paramTypes.data [paramIdx] = ParamType::NON_STRING;
236+ ++paramIdx;
237+ } else if (hasDynamicPrecision) {
238+ paramTypes.data [paramIdx] =
239+ ParamType::STRING_WITH_DYNAMIC_PRECISION;
240+ ++paramIdx;
241+ } else if (precision == -1 ) {
242+ paramTypes.data [paramIdx] =
243+ ParamType::STRING_WITH_NO_PRECISION;
244+ ++paramIdx;
219245 } else {
220- if (fmt[pos] != ' s' )
221- return ParamType::NON_STRING;
222-
223- if (hasDynamicPrecision)
224- return ParamType::STRING_WITH_DYNAMIC_PRECISION;
225-
226- if (precision == -1 )
227- return ParamType::STRING_WITH_NO_PRECISION;
228- else
229- return ParamType (precision);
246+ paramTypes.data [paramIdx] = ParamType (precision);
247+ ++paramIdx;
230248 }
231249 }
232250 }
233251 }
234252
235- return ParamType::INVALID;
236- }
237-
238-
239- /* *
240- * Helper to analyzeFormatString. This level of indirection is needed to
241- * unpack the index_sequence generated in analyzeFormatString and
242- * use the sequence as indices for calling getParamInfo.
243- *
244- * \tparam N
245- * Length of the format string (automatically deduced)
246- * \tparam Indices
247- * An index sequence from [0, N) where N is the number of parameters in
248- * the format string (automatically deduced)
249- *
250- * \param fmt
251- * printf format string to analyze
252- *
253- * \return
254- * An std::array describing the types at each index (zero based).
255- */
256- template <int N, std::size_t ... Indices>
257- constexpr std::array<ParamType, sizeof ...(Indices)>
258- analyzeFormatStringHelper (const char (&fmt)[N], std::index_sequence<Indices...>)
259- {
260- return {{ getParamInfo (fmt, Indices)... }};
253+ paramTypes.size = paramIdx;
254+ return paramTypes;
261255}
262256
263257
264258/* *
265259 * Computes a ParamType array describing the parameters that would be used
266- * with the provided printf style format string. The indices of the array
267- * correspond with the parameter position in the variable args portion of
268- * the invocation.
260+ * with the provided ParamTypeContainer analyzed from the printf style format
261+ * string. The indices of the array correspond with the parameter position in
262+ * the variable args portion of the invocation.
269263 *
270264 * \template NParams
271265 * The number of additional format parameters that follow the format
272266 * string in a printf-like function. For example printf("%*.*d", 9, 8, 7)
273267 * would have NParams = 3
274268 * \template N
275- * length of the printf style format string (automatically deduced)
276- *
277- * \param fmt
278- * Format string to generate the array for
269+ * template parameter N of the ParamTypeContainer parameter (automatically
270+ * deduced)
271+ * \param paramTypes
272+ * ParamTypeContainer object to generate the array for
279273 *
280274 * \return
281275 * An std::array where the n-th index indicates that the
282276 * n-th format parameter is a "%s" or not.
283277 */
284- template <int NParams, size_t N>
278+ template <int NParams, std:: size_t N>
285279constexpr std::array<ParamType, NParams>
286- analyzeFormatString (const char (&fmt)[N] )
280+ analyzeParamTypeContainer (const ParamTypeContainer<N> ¶mTypes )
287281{
288- return analyzeFormatStringHelper (fmt, std::make_index_sequence<NParams>{});
289- }
290-
291- /* *
292- * Counts the number of parameters that need to be passed in for a particular
293- * printf style format string.
294- *
295- * One subtle point is that we are counting parameters, not specifiers, so a
296- * specifier of "%*.*d" will actually count as 3 since the two '*" will result
297- * in a parameter being passed in each.
298- *
299- * \tparam N
300- * length of the printf style format string (automatically deduced)
301- *
302- * \param fmt
303- * printf style format string to analyze
304- *
305- * @return
306- */
307- template <int N>
308- constexpr inline int
309- countFmtParams (const char (&fmt)[N])
310- {
311- int count = 0 ;
312- while (getParamInfo (fmt, count) != ParamType::INVALID)
313- ++count;
314- return count;
282+ std::array<ParamType, NParams> ret = {};
283+ int fillSize = NParams;
284+ if (NParams > static_cast <int >(paramTypes.size ))
285+ fillSize = static_cast <int >(paramTypes.size );
286+ for (int i = 0 ; i < fillSize; ++i) {
287+ ret[i] = paramTypes.data [i];
288+ }
289+ if (NParams > static_cast <int >(paramTypes.size )) {
290+ for (int i = paramTypes.size ; i < NParams; ++i) {
291+ ret[i] = ParamType::INVALID;
292+ }
293+ }
294+ return ret;
315295}
316296
317297/* *
318298 * Counts the number of nibbles that would be needed to represent all
319299 * the non-string and dynamic width/precision specifiers for a given
320- * printf style format string in the NanoLog system.
300+ * ParamTypeContainer in the NanoLog system.
321301 *
322302 * \tparam N
323- * length of the printf style format string (automatically deduced)
324- * \param fmt
325- * printf style format string to analyze
303+ * template parameter N of the ParamTypeContainer parameter (automatically
304+ * deduced)
305+ * \param paramTypes
306+ * ParamTypeContainer object to analyze
326307 *
327308 * \return
328- * Number of non-string specifiers in the format string
309+ * Number of non-string specifiers in the paramTypes object.
329310 */
330311template <size_t N>
331312constexpr int
332- getNumNibblesNeeded (const char (&fmt)[N] )
313+ getNumNibblesNeeded (const ParamTypeContainer<N> ¶mTypes )
333314{
334315 int numNibbles = 0 ;
335- for (int i = 0 ; i < countFmtParams (fmt) ; ++i) {
336- ParamType t = getParamInfo (fmt, i) ;
316+ for (int i = 0 ; i < paramTypes. size ; ++i) {
317+ ParamType t = paramTypes. data [i] ;
337318 if (t == NON_STRING || t == DYNAMIC_PRECISION || t == DYNAMIC_WIDTH)
338319 ++numNibbles;
339320 }
@@ -1071,8 +1052,9 @@ checkFormat(NANOLOG_PRINTF_FORMAT const char *, ...) {}
10711052 * Log arguments associated with the printf-like string.
10721053 */
10731054#define NANO_LOG (severity, format, ...) do { \
1074- constexpr int numNibbles = NanoLogInternal::getNumNibblesNeeded (format); \
1075- constexpr int nParams = NanoLogInternal::countFmtParams (format); \
1055+ constexpr auto paramTypeInfo = NanoLogInternal::getParamInfos (format); \
1056+ constexpr int numNibbles = NanoLogInternal::getNumNibblesNeeded (paramTypeInfo); \
1057+ constexpr int nParams = paramTypeInfo.size ; \
10761058 \
10771059 /* ** Very Important*** These must be 'static' so that we can save pointers
10781060 * to these variables and have them persist beyond the invocation.
@@ -1081,7 +1063,7 @@ checkFormat(NANOLOG_PRINTF_FORMAT const char *, ...) {}
10811063 * used by the compression function, which is invoked in another thread
10821064 * at a much later time. */ \
10831065 static constexpr std::array<NanoLogInternal::ParamType, nParams> paramTypes = \
1084- NanoLogInternal::analyzeFormatString <nParams>(format ); \
1066+ NanoLogInternal::analyzeParamTypeContainer <nParams>(paramTypeInfo ); \
10851067 static int logId = NanoLogInternal::UNASSIGNED_LOGID; \
10861068 \
10871069 if (NanoLog::severity > NanoLog::getLogLevel ()) \
0 commit comments