1//===-- Format string parser for printf -------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
10#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
11
12#include "include/llvm-libc-macros/stdfix-macros.h"
13#include "src/__support/CPP/algorithm.h" // max
14#include "src/__support/CPP/optional.h"
15#include "src/__support/CPP/type_traits.h"
16#include "src/__support/str_to_integer.h"
17#include "src/stdio/printf_core/core_structs.h"
18#include "src/stdio/printf_core/printf_config.h"
19
20#include <stddef.h>
21
22#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
23#include "src/__support/fixed_point/fx_rep.h"
24#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
25
26namespace LIBC_NAMESPACE {
27namespace printf_core {
28
29template <typename T> struct int_type_of {
30 using type = T;
31};
32template <> struct int_type_of<double> {
33 using type = fputil::FPBits<double>::StorageType;
34};
35template <> struct int_type_of<long double> {
36 using type = fputil::FPBits<long double>::StorageType;
37};
38
39#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
40template <typename T>
41struct int_type_of<cpp::enable_if<cpp::is_fixed_point_v<T>, T>> {
42 using type = typename fixed_point::FXRep<T>::StorageType;
43};
44#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
45
46template <typename T> using int_type_of_v = typename int_type_of<T>::type;
47
48#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
49#define WRITE_ARG_VAL_SIMPLEST(dst, arg_type, index) \
50 { \
51 auto temp = get_arg_value<arg_type>(index); \
52 if (!temp.has_value()) { \
53 section.has_conv = false; \
54 } else { \
55 dst = cpp::bit_cast<int_type_of_v<arg_type>>(temp.value()); \
56 } \
57 }
58#else
59#define WRITE_ARG_VAL_SIMPLEST(dst, arg_type, _) \
60 dst = cpp::bit_cast<int_type_of_v<arg_type>>(get_next_arg_value<arg_type>())
61#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
62
63template <typename ArgProvider> class Parser {
64 const char *__restrict str;
65
66 size_t cur_pos = 0;
67 ArgProvider args_cur;
68
69#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
70 // args_start stores the start of the va_args, which is allows getting the
71 // value of arguments that have already been passed. args_index is tracked so
72 // that we know which argument args_cur is on.
73 ArgProvider args_start;
74 size_t args_index = 1;
75
76 // Defined in printf_config.h
77 static constexpr size_t DESC_ARR_LEN = LIBC_COPT_PRINTF_INDEX_ARR_LEN;
78
79 // desc_arr stores the sizes of the variables in the ArgProvider. This is used
80 // in index mode to reduce repeated string parsing. The sizes are stored as
81 // TypeDesc objects, which store the size as well as minimal type information.
82 // This is necessary because some systems separate the floating point and
83 // integer values in va_args.
84 TypeDesc desc_arr[DESC_ARR_LEN] = {type_desc_from_type<void>()};
85
86 // TODO: Look into object stores for optimization.
87
88#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
89
90public:
91#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
92 LIBC_INLINE Parser(const char *__restrict new_str, ArgProvider &args)
93 : str(new_str), args_cur(args), args_start(args) {}
94#else
95 LIBC_INLINE Parser(const char *__restrict new_str, ArgProvider &args)
96 : str(new_str), args_cur(args) {}
97#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
98
99 // get_next_section will parse the format string until it has a fully
100 // specified format section. This can either be a raw format section with no
101 // conversion, or a format section with a conversion that has all of its
102 // variables stored in the format section.
103 LIBC_INLINE FormatSection get_next_section() {
104 FormatSection section;
105 size_t starting_pos = cur_pos;
106 if (str[cur_pos] == '%') {
107 // format section
108 section.has_conv = true;
109
110 ++cur_pos;
111 [[maybe_unused]] size_t conv_index = 0;
112
113#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
114 conv_index = parse_index(local_pos: &cur_pos);
115#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
116
117 section.flags = parse_flags(local_pos: &cur_pos);
118
119 // handle width
120 section.min_width = 0;
121 if (str[cur_pos] == '*') {
122 ++cur_pos;
123
124 WRITE_ARG_VAL_SIMPLEST(section.min_width, int, parse_index(&cur_pos));
125 } else if (internal::isdigit(ch: str[cur_pos])) {
126 auto result = internal::strtointeger<int>(src: str + cur_pos, base: 10);
127 section.min_width = result.value;
128 cur_pos = cur_pos + result.parsed_len;
129 }
130 if (section.min_width < 0) {
131 section.min_width = -section.min_width;
132 section.flags = static_cast<FormatFlags>(section.flags |
133 FormatFlags::LEFT_JUSTIFIED);
134 }
135
136 // handle precision
137 section.precision = -1; // negative precisions are ignored.
138 if (str[cur_pos] == '.') {
139 ++cur_pos;
140 section.precision = 0; // if there's a . but no specified precision, the
141 // precision is implicitly 0.
142 if (str[cur_pos] == '*') {
143 ++cur_pos;
144
145 WRITE_ARG_VAL_SIMPLEST(section.precision, int, parse_index(&cur_pos));
146
147 } else if (internal::isdigit(ch: str[cur_pos])) {
148 auto result = internal::strtointeger<int>(src: str + cur_pos, base: 10);
149 section.precision = result.value;
150 cur_pos = cur_pos + result.parsed_len;
151 }
152 }
153
154 auto [lm, bw] = parse_length_modifier(local_pos: &cur_pos);
155 section.length_modifier = lm;
156 section.conv_name = str[cur_pos];
157 section.bit_width = bw;
158 switch (str[cur_pos]) {
159 case ('%'):
160 // Regardless of options, a % conversion is always safe. The standard
161 // says that "The complete conversion specification shall be %%" but it
162 // also says that "If a conversion specification is invalid, the
163 // behavior is undefined." Based on that we define that any conversion
164 // specification ending in '%' shall display as '%' regardless of any
165 // valid or invalid options.
166 section.has_conv = true;
167 break;
168 case ('c'):
169 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
170 break;
171 case ('d'):
172 case ('i'):
173 case ('o'):
174 case ('x'):
175 case ('X'):
176 case ('u'):
177 case ('b'):
178 case ('B'):
179 switch (lm) {
180 case (LengthModifier::hh):
181 case (LengthModifier::h):
182 case (LengthModifier::none):
183 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
184 break;
185 case (LengthModifier::l):
186 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long, conv_index);
187 break;
188 case (LengthModifier::ll):
189 case (LengthModifier::L): // This isn't in the standard, but is in other
190 // libc implementations.
191
192 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long long, conv_index);
193 break;
194 case (LengthModifier::j):
195
196 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, intmax_t, conv_index);
197 break;
198 case (LengthModifier::z):
199
200 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, size_t, conv_index);
201 break;
202 case (LengthModifier::t):
203
204 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, ptrdiff_t, conv_index);
205 break;
206
207 case (LengthModifier::w):
208 case (LengthModifier::wf):
209 if (bw == 0) {
210 section.has_conv = false;
211 } else if (bw <= INT_WIDTH) {
212 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
213 } else if (bw <= LONG_WIDTH) {
214 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long, conv_index);
215 } else if (bw <= LLONG_WIDTH) {
216 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long long, conv_index);
217 } else {
218 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, intmax_t, conv_index);
219 }
220 break;
221 }
222 break;
223#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
224 case ('f'):
225 case ('F'):
226 case ('e'):
227 case ('E'):
228 case ('a'):
229 case ('A'):
230 case ('g'):
231 case ('G'):
232 if (lm != LengthModifier::L) {
233 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
234 } else {
235 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);
236 }
237 break;
238#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
239#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
240 // Capitalization represents sign, but we only need to get the right
241 // bitwidth here so we ignore that.
242 case ('r'):
243 case ('R'):
244 // all fract sizes we support are less than 32 bits, and currently doing
245 // va_args with fixed point types just doesn't work.
246 // TODO: Move to fixed point types once va_args supports it.
247 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint32_t, conv_index);
248 break;
249 case ('k'):
250 case ('K'):
251 if (lm == LengthModifier::l) {
252 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint64_t, conv_index);
253 } else {
254 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint32_t, conv_index);
255 }
256 break;
257#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
258#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
259 case ('n'):
260#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
261 case ('p'):
262 WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, void *, conv_index);
263 break;
264 case ('s'):
265 WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, char *, conv_index);
266 break;
267 default:
268 // if the conversion is undefined, change this to a raw section.
269 section.has_conv = false;
270 break;
271 }
272 // If the end of the format section is on the '\0'. This means we need to
273 // not advance the cur_pos.
274 if (str[cur_pos] != '\0')
275 ++cur_pos;
276
277 } else {
278 // raw section
279 section.has_conv = false;
280 while (str[cur_pos] != '%' && str[cur_pos] != '\0')
281 ++cur_pos;
282 }
283 section.raw_string = {str + starting_pos, cur_pos - starting_pos};
284 return section;
285 }
286
287private:
288 // parse_flags parses the flags inside a format string. It assumes that
289 // str[*local_pos] is inside a format specifier, and parses any flags it
290 // finds. It returns a FormatFlags object containing the set of found flags
291 // arithmetically or'd together. local_pos will be moved past any flags found.
292 LIBC_INLINE FormatFlags parse_flags(size_t *local_pos) {
293 bool found_flag = true;
294 FormatFlags flags = FormatFlags(0);
295 while (found_flag) {
296 switch (str[*local_pos]) {
297 case '-':
298 flags = static_cast<FormatFlags>(flags | FormatFlags::LEFT_JUSTIFIED);
299 break;
300 case '+':
301 flags = static_cast<FormatFlags>(flags | FormatFlags::FORCE_SIGN);
302 break;
303 case ' ':
304 flags = static_cast<FormatFlags>(flags | FormatFlags::SPACE_PREFIX);
305 break;
306 case '#':
307 flags = static_cast<FormatFlags>(flags | FormatFlags::ALTERNATE_FORM);
308 break;
309 case '0':
310 flags = static_cast<FormatFlags>(flags | FormatFlags::LEADING_ZEROES);
311 break;
312 default:
313 found_flag = false;
314 }
315 if (found_flag)
316 ++*local_pos;
317 }
318 return flags;
319 }
320
321 // parse_length_modifier parses the length modifier inside a format string. It
322 // assumes that str[*local_pos] is inside a format specifier. It returns a
323 // LengthModifier with the length modifier it found. It will advance local_pos
324 // after the format specifier if one is found.
325 LIBC_INLINE LengthSpec parse_length_modifier(size_t *local_pos) {
326 switch (str[*local_pos]) {
327 case ('l'):
328 if (str[*local_pos + 1] == 'l') {
329 *local_pos += 2;
330 return {.lm: LengthModifier::ll, .bit_width: 0};
331 } else {
332 ++*local_pos;
333 return {.lm: LengthModifier::l, .bit_width: 0};
334 }
335 case ('w'): {
336 LengthModifier lm;
337 if (str[*local_pos + 1] == 'f') {
338 *local_pos += 2;
339 lm = LengthModifier::wf;
340 } else {
341 ++*local_pos;
342 lm = LengthModifier::w;
343 }
344 if (internal::isdigit(ch: str[*local_pos])) {
345 const auto result = internal::strtointeger<int>(src: str + *local_pos, base: 10);
346 *local_pos += result.parsed_len;
347 return {.lm: lm, .bit_width: static_cast<size_t>(cpp::max(a: 0, b: result.value))};
348 }
349 return {.lm: lm, .bit_width: 0};
350 }
351 case ('h'):
352 if (str[*local_pos + 1] == 'h') {
353 *local_pos += 2;
354 return {.lm: LengthModifier::hh, .bit_width: 0};
355 } else {
356 ++*local_pos;
357 return {.lm: LengthModifier::h, .bit_width: 0};
358 }
359 case ('L'):
360 ++*local_pos;
361 return {.lm: LengthModifier::L, .bit_width: 0};
362 case ('j'):
363 ++*local_pos;
364 return {.lm: LengthModifier::j, .bit_width: 0};
365 case ('z'):
366 ++*local_pos;
367 return {.lm: LengthModifier::z, .bit_width: 0};
368 case ('t'):
369 ++*local_pos;
370 return {.lm: LengthModifier::t, .bit_width: 0};
371 default:
372 return {.lm: LengthModifier::none, .bit_width: 0};
373 }
374 }
375
376 // get_next_arg_value gets the next value from the arg list as type T.
377 template <class T> LIBC_INLINE T get_next_arg_value() {
378 return args_cur.template next_var<T>();
379 }
380
381 //----------------------------------------------------
382 // INDEX MODE ONLY FUNCTIONS AFTER HERE:
383 //----------------------------------------------------
384
385#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
386
387 // parse_index parses the index of a value inside a format string. It
388 // assumes that str[*local_pos] points to character after a '%' or '*', and
389 // returns 0 if there is no closing $, or if it finds no number. If it finds a
390 // number, it will move local_pos past the end of the $, else it will not move
391 // local_pos.
392 LIBC_INLINE size_t parse_index(size_t *local_pos) {
393 if (internal::isdigit(ch: str[*local_pos])) {
394 auto result = internal::strtointeger<int>(src: str + *local_pos, base: 10);
395 size_t index = result.value;
396 if (str[*local_pos + result.parsed_len] != '$')
397 return 0;
398 *local_pos = 1 + result.parsed_len + *local_pos;
399 return index;
400 }
401 return 0;
402 }
403
404 LIBC_INLINE void set_type_desc(size_t index, TypeDesc value) {
405 if (index != 0 && index <= DESC_ARR_LEN)
406 desc_arr[index - 1] = value;
407 }
408
409 // get_arg_value gets the value from the arg list at index (starting at 1).
410 // This may require parsing the format string. An index of 0 is interpreted as
411 // the next value. If the format string is not valid, it may have gaps in its
412 // indexes. Requesting the value for any index after a gap will fail, since
413 // the arg list must be read in order and with the correct types.
414 template <class T> LIBC_INLINE cpp::optional<T> get_arg_value(size_t index) {
415 if (!(index == 0 || index == args_index)) {
416 bool success = args_to_index(index);
417 if (!success) {
418 // If we can't get to this index, then the value of the arg can't be
419 // found.
420 return cpp::optional<T>();
421 }
422 }
423
424 set_type_desc(index, value: type_desc_from_type<T>());
425
426 ++args_index;
427 return get_next_arg_value<T>();
428 }
429
430 // the ArgProvider can only return the next item in the list. This function is
431 // used in index mode when the item that needs to be read is not the next one.
432 // It moves cur_args to the index requested so the appropriate value may
433 // be read. This may involve parsing the format string, and is in the worst
434 // case an O(n^2) operation.
435 LIBC_INLINE bool args_to_index(size_t index) {
436 if (args_index > index) {
437 args_index = 1;
438 args_cur = args_start;
439 }
440
441 while (args_index < index) {
442 TypeDesc cur_type_desc = type_desc_from_type<void>();
443 if (args_index <= DESC_ARR_LEN)
444 cur_type_desc = desc_arr[args_index - 1];
445
446 if (cur_type_desc == type_desc_from_type<void>())
447 cur_type_desc = get_type_desc(index: args_index);
448
449 // A type of void represents the type being unknown. If the type for the
450 // requested index isn't in the desc_arr and isn't found by parsing the
451 // string, then then advancing to the requested index is impossible. In
452 // that case the function returns false.
453 if (cur_type_desc == type_desc_from_type<void>())
454 return false;
455
456 if (cur_type_desc == type_desc_from_type<uint32_t>())
457 args_cur.template next_var<uint32_t>();
458 else if (cur_type_desc == type_desc_from_type<uint64_t>())
459 args_cur.template next_var<uint64_t>();
460#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
461 // Floating point numbers are stored separately from the other arguments.
462 else if (cur_type_desc == type_desc_from_type<double>())
463 args_cur.template next_var<double>();
464 else if (cur_type_desc == type_desc_from_type<long double>())
465 args_cur.template next_var<long double>();
466#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
467#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
468 // Floating point numbers may be stored separately from the other
469 // arguments.
470 else if (cur_type_desc == type_desc_from_type<short fract>())
471 args_cur.template next_var<short fract>();
472 else if (cur_type_desc == type_desc_from_type<fract>())
473 args_cur.template next_var<fract>();
474 else if (cur_type_desc == type_desc_from_type<long fract>())
475 args_cur.template next_var<long fract>();
476 else if (cur_type_desc == type_desc_from_type<short accum>())
477 args_cur.template next_var<short accum>();
478 else if (cur_type_desc == type_desc_from_type<accum>())
479 args_cur.template next_var<accum>();
480 else if (cur_type_desc == type_desc_from_type<long accum>())
481 args_cur.template next_var<long accum>();
482#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
483 // pointers may be stored separately from normal values.
484 else if (cur_type_desc == type_desc_from_type<void *>())
485 args_cur.template next_var<void *>();
486 else
487 args_cur.template next_var<uint32_t>();
488
489 ++args_index;
490 }
491 return true;
492 }
493
494 // get_type_desc assumes that this format string uses index mode. It iterates
495 // through the format string until it finds a format specifier that defines
496 // the type of index, and returns a TypeDesc describing that type. It does not
497 // modify cur_pos.
498 LIBC_INLINE TypeDesc get_type_desc(size_t index) {
499 // index mode is assumed, and the indicies start at 1, so an index
500 // of 0 is invalid.
501 size_t local_pos = 0;
502
503 while (str[local_pos]) {
504 if (str[local_pos] == '%') {
505 ++local_pos;
506
507 size_t conv_index = parse_index(local_pos: &local_pos);
508
509 // the flags aren't relevant for this situation, but I need to skip past
510 // them so they're parsed but the result is discarded.
511 parse_flags(local_pos: &local_pos);
512
513 // handle width
514 if (str[local_pos] == '*') {
515 ++local_pos;
516
517 size_t width_index = parse_index(local_pos: &local_pos);
518 set_type_desc(index: width_index, value: type_desc_from_type<int>());
519 if (width_index == index)
520 return type_desc_from_type<int>();
521
522 } else if (internal::isdigit(ch: str[local_pos])) {
523 while (internal::isdigit(ch: str[local_pos]))
524 ++local_pos;
525 }
526
527 // handle precision
528 if (str[local_pos] == '.') {
529 ++local_pos;
530 if (str[local_pos] == '*') {
531 ++local_pos;
532
533 size_t precision_index = parse_index(local_pos: &local_pos);
534 set_type_desc(index: precision_index, value: type_desc_from_type<int>());
535 if (precision_index == index)
536 return type_desc_from_type<int>();
537
538 } else if (internal::isdigit(ch: str[local_pos])) {
539 while (internal::isdigit(ch: str[local_pos]))
540 ++local_pos;
541 }
542 }
543
544 auto [lm, bw] = parse_length_modifier(local_pos: &local_pos);
545
546 // if we don't have an index for this conversion, then its position is
547 // unknown and all this information is irrelevant. The rest of this
548 // logic has been for skipping past this conversion properly to avoid
549 // weirdness with %%.
550 if (conv_index == 0) {
551 if (str[local_pos] != '\0')
552 ++local_pos;
553 continue;
554 }
555
556 TypeDesc conv_size = type_desc_from_type<void>();
557 switch (str[local_pos]) {
558 case ('%'):
559 conv_size = type_desc_from_type<void>();
560 break;
561 case ('c'):
562 conv_size = type_desc_from_type<int>();
563 break;
564 case ('d'):
565 case ('i'):
566 case ('o'):
567 case ('x'):
568 case ('X'):
569 case ('u'):
570 case ('b'):
571 case ('B'):
572 switch (lm) {
573 case (LengthModifier::hh):
574 case (LengthModifier::h):
575 case (LengthModifier::none):
576 conv_size = type_desc_from_type<int>();
577 break;
578 case (LengthModifier::l):
579 conv_size = type_desc_from_type<long>();
580 break;
581 case (LengthModifier::ll):
582 case (LengthModifier::L): // This isn't in the standard, but is in
583 // other libc implementations.
584 conv_size = type_desc_from_type<long long>();
585 break;
586 case (LengthModifier::j):
587 conv_size = type_desc_from_type<intmax_t>();
588 break;
589 case (LengthModifier::z):
590 conv_size = type_desc_from_type<size_t>();
591 break;
592 case (LengthModifier::t):
593 conv_size = type_desc_from_type<ptrdiff_t>();
594 break;
595 case (LengthModifier::w):
596 case (LengthModifier::wf):
597 if (bw <= INT_WIDTH) {
598 conv_size = type_desc_from_type<int>();
599 } else if (bw <= LONG_WIDTH) {
600 conv_size = type_desc_from_type<long>();
601 } else if (bw <= LLONG_WIDTH) {
602 conv_size = type_desc_from_type<long long>();
603 } else {
604 conv_size = type_desc_from_type<intmax_t>();
605 }
606 break;
607 }
608 break;
609#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
610 case ('f'):
611 case ('F'):
612 case ('e'):
613 case ('E'):
614 case ('a'):
615 case ('A'):
616 case ('g'):
617 case ('G'):
618 if (lm != LengthModifier::L)
619 conv_size = type_desc_from_type<double>();
620 else
621 conv_size = type_desc_from_type<long double>();
622 break;
623#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
624#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
625 // Capitalization represents sign, but we only need to get the right
626 // bitwidth here so we ignore that.
627 case ('r'):
628 case ('R'):
629 conv_size = type_desc_from_type<uint32_t>();
630 break;
631 case ('k'):
632 case ('K'):
633 if (lm == LengthModifier::l) {
634 conv_size = type_desc_from_type<uint64_t>();
635 } else {
636 conv_size = type_desc_from_type<uint32_t>();
637 }
638 break;
639#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
640#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
641 case ('n'):
642#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
643 case ('p'):
644 case ('s'):
645 conv_size = type_desc_from_type<void *>();
646 break;
647 default:
648 conv_size = type_desc_from_type<int>();
649 break;
650 }
651
652 set_type_desc(index: conv_index, value: conv_size);
653 if (conv_index == index)
654 return conv_size;
655 }
656 // If the end of the format section is on the '\0'. This means we need to
657 // not advance the local_pos.
658 if (str[local_pos] != '\0')
659 ++local_pos;
660 }
661
662 // If there is no size for the requested index, then it's unknown. Return
663 // void.
664 return type_desc_from_type<void>();
665 }
666
667#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
668};
669
670} // namespace printf_core
671} // namespace LIBC_NAMESPACE
672
673#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
674

source code of libc/src/stdio/printf_core/parser.h