• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 namespace LIBC_NAMESPACE {
27 namespace printf_core {
28 
29 template <typename T> struct int_type_of {
30   using type = T;
31 };
32 template <> struct int_type_of<double> {
33   using type = fputil::FPBits<double>::StorageType;
34 };
35 template <> struct int_type_of<long double> {
36   using type = fputil::FPBits<long double>::StorageType;
37 };
38 
39 #ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
40 template <typename T>
41 struct 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 
46 template <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 
63 template <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 
90 public:
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(&cur_pos);
115 #endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
116 
117       section.flags = parse_flags(&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(str[cur_pos])) {
126         auto result = internal::strtointeger<int>(str + cur_pos, 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(str[cur_pos])) {
148           auto result = internal::strtointeger<int>(str + cur_pos, 10);
149           section.precision = result.value;
150           cur_pos = cur_pos + result.parsed_len;
151         }
152       }
153 
154       auto [lm, bw] = parse_length_modifier(&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 
287 private:
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 {LengthModifier::ll, 0};
331       } else {
332         ++*local_pos;
333         return {LengthModifier::l, 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(str[*local_pos])) {
345         const auto result = internal::strtointeger<int>(str + *local_pos, 10);
346         *local_pos += result.parsed_len;
347         return {lm, static_cast<size_t>(cpp::max(0, result.value))};
348       }
349       return {lm, 0};
350     }
351     case ('h'):
352       if (str[*local_pos + 1] == 'h') {
353         *local_pos += 2;
354         return {LengthModifier::hh, 0};
355       } else {
356         ++*local_pos;
357         return {LengthModifier::h, 0};
358       }
359     case ('L'):
360       ++*local_pos;
361       return {LengthModifier::L, 0};
362     case ('j'):
363       ++*local_pos;
364       return {LengthModifier::j, 0};
365     case ('z'):
366       ++*local_pos;
367       return {LengthModifier::z, 0};
368     case ('t'):
369       ++*local_pos;
370       return {LengthModifier::t, 0};
371     default:
372       return {LengthModifier::none, 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(str[*local_pos])) {
394       auto result = internal::strtointeger<int>(str + *local_pos, 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, 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(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 indices 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);
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);
512 
513         // handle width
514         if (str[local_pos] == '*') {
515           ++local_pos;
516 
517           size_t width_index = parse_index(&local_pos);
518           set_type_desc(width_index, type_desc_from_type<int>());
519           if (width_index == index)
520             return type_desc_from_type<int>();
521 
522         } else if (internal::isdigit(str[local_pos])) {
523           while (internal::isdigit(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);
534             set_type_desc(precision_index, type_desc_from_type<int>());
535             if (precision_index == index)
536               return type_desc_from_type<int>();
537 
538           } else if (internal::isdigit(str[local_pos])) {
539             while (internal::isdigit(str[local_pos]))
540               ++local_pos;
541           }
542         }
543 
544         auto [lm, bw] = parse_length_modifier(&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(conv_index, 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