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