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