[libc] Use best-fit binary trie to make malloc logarithmic (#106259)
[llvm-project.git] / libc / src / stdio / printf_core / parser.h
blobacbbaa25b1c9da25ea523625d16af90c5e21fb13
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 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
10 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
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"
22 #include <stddef.h>
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
31 namespace LIBC_NAMESPACE_DECL {
32 namespace printf_core {
34 template <typename T> struct int_type_of {
35 using type = T;
37 template <> struct int_type_of<double> {
38 using type = fputil::FPBits<double>::StorageType;
40 template <> struct int_type_of<long double> {
41 using type = fputil::FPBits<long double>::StorageType;
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;
49 #endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
51 template <typename T> using int_type_of_v = typename int_type_of<T>::type;
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 } \
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
68 template <typename ArgProvider> class Parser {
69 const char *__restrict str;
71 size_t cur_pos = 0;
72 ArgProvider args_cur;
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;
81 // Defined in printf_config.h
82 static constexpr size_t DESC_ARR_LEN = LIBC_COPT_PRINTF_INDEX_ARR_LEN;
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>()};
91 // TODO: Look into object stores for optimization.
93 #endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
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
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;
115 ++cur_pos;
116 [[maybe_unused]] size_t conv_index = 0;
118 #ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
119 conv_index = parse_index(&cur_pos);
120 #endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
122 section.flags = parse_flags(&cur_pos);
124 // handle width
125 section.min_width = 0;
126 if (str[cur_pos] == '*') {
127 ++cur_pos;
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;
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);
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;
151 WRITE_ARG_VAL_SIMPLEST(section.precision, int, parse_index(&cur_pos));
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;
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.
198 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long long, conv_index);
199 break;
200 case (LengthModifier::j):
202 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, intmax_t, conv_index);
203 break;
204 case (LengthModifier::z):
206 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, size_t, conv_index);
207 break;
208 case (LengthModifier::t):
210 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, ptrdiff_t, conv_index);
211 break;
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);
226 break;
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);
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);
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;
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;
290 } else {
291 // raw section
292 section.has_conv = false;
293 while (str[cur_pos] != '%' && str[cur_pos] != '\0')
294 ++cur_pos;
296 section.raw_string = {str + starting_pos, cur_pos - starting_pos};
297 return section;
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;
328 if (found_flag)
329 ++*local_pos;
331 return flags;
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};
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;
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))};
362 return {lm, 0};
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};
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};
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>();
394 //----------------------------------------------------
395 // INDEX MODE ONLY FUNCTIONS AFTER HERE:
396 //----------------------------------------------------
398 #ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
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;
414 return 0;
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;
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>();
437 set_type_desc(index, type_desc_from_type<T>());
439 ++args_index;
440 return get_next_arg_value<T>();
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;
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];
459 if (cur_type_desc == type_desc_from_type<void>())
460 cur_type_desc = get_type_desc(args_index);
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;
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>();
502 ++args_index;
504 return true;
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;
516 while (str[local_pos]) {
517 if (str[local_pos] == '%') {
518 ++local_pos;
520 size_t conv_index = parse_index(&local_pos);
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);
526 // handle width
527 if (str[local_pos] == '*') {
528 ++local_pos;
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>();
535 } else if (internal::isdigit(str[local_pos])) {
536 while (internal::isdigit(str[local_pos]))
537 ++local_pos;
540 // handle precision
541 if (str[local_pos] == '.') {
542 ++local_pos;
543 if (str[local_pos] == '*') {
544 ++local_pos;
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>();
551 } else if (internal::isdigit(str[local_pos])) {
552 while (internal::isdigit(str[local_pos]))
553 ++local_pos;
557 auto [lm, bw] = parse_length_modifier(&local_pos);
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;
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>();
619 break;
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>();
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;
665 set_type_desc(conv_index, conv_size);
666 if (conv_index == index)
667 return conv_size;
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;
675 // If there is no size for the requested index, then it's unknown. Return
676 // void.
677 return type_desc_from_type<void>();
680 #endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
683 } // namespace printf_core
684 } // namespace LIBC_NAMESPACE_DECL
686 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H