689 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H10#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H11 12#include "include/llvm-libc-macros/stdfix-macros.h"13#include "src/__support/CPP/algorithm.h" // max14#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_POINT25#include "src/__support/fixed_point/fx_rep.h"26#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT27#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR28#include "src/__support/libc_errno.h"29#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR30 31namespace LIBC_NAMESPACE_DECL {32namespace printf_core {33 34template <typename T> struct int_type_of {35 using type = T;36};37template <> struct int_type_of<double> {38 using type = fputil::FPBits<double>::StorageType;39};40template <> struct int_type_of<long double> {41 using type = fputil::FPBits<long double>::StorageType;42};43 44#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT45template <typename T>46struct 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_POINT50 51template <typename T> using int_type_of_v = typename int_type_of<T>::type;52 53#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE54#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 = static_cast<decltype(dst)>( \61 cpp::bit_cast<int_type_of_v<arg_type>>(temp.value())); \62 } \63 }64#else65#define WRITE_ARG_VAL_SIMPLEST(dst, arg_type, _) \66 dst = cpp::bit_cast<int_type_of_v<arg_type>>(get_next_arg_value<arg_type>())67#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE68 69template <typename ArgProvider> class Parser {70 const char *__restrict str;71 72 size_t cur_pos = 0;73 ArgProvider args_cur;74 75#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE76 // args_start stores the start of the va_args, which is allows getting the77 // value of arguments that have already been passed. args_index is tracked so78 // that we know which argument args_cur is on.79 ArgProvider args_start;80 size_t args_index = 1;81 82 // Defined in printf_config.h83 static constexpr size_t DESC_ARR_LEN = LIBC_COPT_PRINTF_INDEX_ARR_LEN;84 85 // desc_arr stores the sizes of the variables in the ArgProvider. This is used86 // in index mode to reduce repeated string parsing. The sizes are stored as87 // TypeDesc objects, which store the size as well as minimal type information.88 // This is necessary because some systems separate the floating point and89 // integer values in va_args.90 TypeDesc desc_arr[DESC_ARR_LEN] = {type_desc_from_type<void>()};91 92 // TODO: Look into object stores for optimization.93 94#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE95 96public:97#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE98 LIBC_INLINE Parser(const char *__restrict new_str, ArgProvider &args)99 : str(new_str), args_cur(args), args_start(args) {}100#else101 LIBC_INLINE Parser(const char *__restrict new_str, ArgProvider &args)102 : str(new_str), args_cur(args) {}103#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE104 105 // get_next_section will parse the format string until it has a fully106 // specified format section. This can either be a raw format section with no107 // conversion, or a format section with a conversion that has all of its108 // variables stored in the format section.109 LIBC_INLINE FormatSection get_next_section() {110 FormatSection section;111 size_t starting_pos = cur_pos;112 if (str[cur_pos] == '%') {113 // format section114 section.has_conv = true;115 116 ++cur_pos;117 [[maybe_unused]] size_t conv_index = 0;118 119#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE120 conv_index = parse_index(&cur_pos);121#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE122 123 section.flags = parse_flags(&cur_pos);124 125 // handle width126 section.min_width = 0;127 if (str[cur_pos] == '*') {128 ++cur_pos;129 130 WRITE_ARG_VAL_SIMPLEST(section.min_width, int, parse_index(&cur_pos));131 } else if (internal::isdigit(str[cur_pos])) {132 auto result = internal::strtointeger<int>(str + cur_pos, 10);133 section.min_width = result.value;134 cur_pos = cur_pos + static_cast<size_t>(result.parsed_len);135 }136 if (section.min_width < 0) {137 section.min_width =138 (section.min_width == INT_MIN) ? INT_MAX : -section.min_width;139 section.flags = static_cast<FormatFlags>(section.flags |140 FormatFlags::LEFT_JUSTIFIED);141 }142 143 // handle precision144 section.precision = -1; // negative precisions are ignored.145 if (str[cur_pos] == '.') {146 ++cur_pos;147 section.precision = 0; // if there's a . but no specified precision, the148 // precision is implicitly 0.149 if (str[cur_pos] == '*') {150 ++cur_pos;151 152 WRITE_ARG_VAL_SIMPLEST(section.precision, int, parse_index(&cur_pos));153 154 } else if (internal::isdigit(str[cur_pos])) {155 auto result = internal::strtointeger<int>(str + cur_pos, 10);156 section.precision = result.value;157 cur_pos = cur_pos + static_cast<size_t>(result.parsed_len);158 }159 }160 161 auto [lm, bw] = parse_length_modifier(&cur_pos);162 section.length_modifier = lm;163 section.conv_name = str[cur_pos];164 section.bit_width = bw;165 switch (str[cur_pos]) {166 case ('%'):167 // Regardless of options, a % conversion is always safe. The standard168 // says that "The complete conversion specification shall be %%" but it169 // also says that "If a conversion specification is invalid, the170 // behavior is undefined." Based on that we define that any conversion171 // specification ending in '%' shall display as '%' regardless of any172 // valid or invalid options.173 section.has_conv = true;174 break;175 case ('c'):176 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);177 break;178 case ('d'):179 case ('i'):180 case ('o'):181 case ('x'):182 case ('X'):183 case ('u'):184 case ('b'):185 case ('B'):186 switch (lm) {187 case (LengthModifier::hh):188 case (LengthModifier::h):189 case (LengthModifier::none):190 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);191 break;192 case (LengthModifier::l):193 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long, conv_index);194 break;195 case (LengthModifier::ll):196 case (LengthModifier::L): // This isn't in the standard, but is in other197 // libc implementations.198 199 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long long, conv_index);200 break;201 case (LengthModifier::j):202 203 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, intmax_t, conv_index);204 break;205 case (LengthModifier::z):206 207 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, size_t, conv_index);208 break;209 case (LengthModifier::t):210 211 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, ptrdiff_t, conv_index);212 break;213 214 case (LengthModifier::w):215 case (LengthModifier::wf):216 if (bw == 0) {217 section.has_conv = false;218 } else if (bw <= cpp::numeric_limits<unsigned int>::digits) {219 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);220 } else if (bw <= cpp::numeric_limits<unsigned long>::digits) {221 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long, conv_index);222 } else if (bw <= cpp::numeric_limits<unsigned long long>::digits) {223 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long long, conv_index);224 } else {225 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, intmax_t, conv_index);226 }227 break;228 }229 break;230#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT231 case ('f'):232 case ('F'):233 case ('e'):234 case ('E'):235 case ('a'):236 case ('A'):237 case ('g'):238 case ('G'):239 if (lm != LengthModifier::L) {240 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);241 } else {242 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);243 }244 break;245#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT246#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT247 // Capitalization represents sign, but we only need to get the right248 // bitwidth here so we ignore that.249 case ('r'):250 case ('R'):251 // all fract sizes we support are less than 32 bits, and currently doing252 // va_args with fixed point types just doesn't work.253 // TODO: Move to fixed point types once va_args supports it.254 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint32_t, conv_index);255 break;256 case ('k'):257 case ('K'):258 if (lm == LengthModifier::l) {259 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint64_t, conv_index);260 } else {261 WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, uint32_t, conv_index);262 }263 break;264#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT265#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR266 case ('m'):267 // %m is an odd conversion in that it doesn't consume an argument, it268 // just takes the current value of errno as its argument.269 section.conv_val_raw =270 static_cast<fputil::FPBits<double>::StorageType>(libc_errno);271 break;272#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR273#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT274 case ('n'): // Intentional fallthrough275#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT276 case ('p'):277 WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, void *, conv_index);278 break;279 case ('s'):280 WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, char *, conv_index);281 break;282 default:283 // if the conversion is undefined, change this to a raw section.284 section.has_conv = false;285 break;286 }287 // If the end of the format section is on the '\0'. This means we need to288 // not advance the cur_pos.289 if (str[cur_pos] != '\0')290 ++cur_pos;291 292 } else {293 // raw section294 section.has_conv = false;295 while (str[cur_pos] != '%' && str[cur_pos] != '\0')296 ++cur_pos;297 }298 section.raw_string = {str + starting_pos, cur_pos - starting_pos};299 return section;300 }301 302private:303 // parse_flags parses the flags inside a format string. It assumes that304 // str[*local_pos] is inside a format specifier, and parses any flags it305 // finds. It returns a FormatFlags object containing the set of found flags306 // arithmetically or'd together. local_pos will be moved past any flags found.307 LIBC_INLINE FormatFlags parse_flags(size_t *local_pos) {308 bool found_flag = true;309 FormatFlags flags = FormatFlags(0);310 while (found_flag) {311 switch (str[*local_pos]) {312 case '-':313 flags = static_cast<FormatFlags>(flags | FormatFlags::LEFT_JUSTIFIED);314 break;315 case '+':316 flags = static_cast<FormatFlags>(flags | FormatFlags::FORCE_SIGN);317 break;318 case ' ':319 flags = static_cast<FormatFlags>(flags | FormatFlags::SPACE_PREFIX);320 break;321 case '#':322 flags = static_cast<FormatFlags>(flags | FormatFlags::ALTERNATE_FORM);323 break;324 case '0':325 flags = static_cast<FormatFlags>(flags | FormatFlags::LEADING_ZEROES);326 break;327 default:328 found_flag = false;329 }330 if (found_flag)331 ++*local_pos;332 }333 return flags;334 }335 336 // parse_length_modifier parses the length modifier inside a format string. It337 // assumes that str[*local_pos] is inside a format specifier. It returns a338 // LengthModifier with the length modifier it found. It will advance local_pos339 // after the format specifier if one is found.340 LIBC_INLINE LengthSpec parse_length_modifier(size_t *local_pos) {341 switch (str[*local_pos]) {342 case ('l'):343 if (str[*local_pos + 1] == 'l') {344 *local_pos += 2;345 return {LengthModifier::ll, 0};346 } else {347 ++*local_pos;348 return {LengthModifier::l, 0};349 }350 case ('w'): {351 LengthModifier lm;352 if (str[*local_pos + 1] == 'f') {353 *local_pos += 2;354 lm = LengthModifier::wf;355 } else {356 ++*local_pos;357 lm = LengthModifier::w;358 }359 if (internal::isdigit(str[*local_pos])) {360 const auto result = internal::strtointeger<int>(str + *local_pos, 10);361 *local_pos += static_cast<size_t>(result.parsed_len);362 return {lm, static_cast<size_t>(cpp::max(0, result.value))};363 }364 return {lm, 0};365 }366 case ('h'):367 if (str[*local_pos + 1] == 'h') {368 *local_pos += 2;369 return {LengthModifier::hh, 0};370 } else {371 ++*local_pos;372 return {LengthModifier::h, 0};373 }374 case ('L'):375 ++*local_pos;376 return {LengthModifier::L, 0};377 case ('j'):378 ++*local_pos;379 return {LengthModifier::j, 0};380 case ('z'):381 ++*local_pos;382 return {LengthModifier::z, 0};383 case ('t'):384 ++*local_pos;385 return {LengthModifier::t, 0};386 default:387 return {LengthModifier::none, 0};388 }389 }390 391 // get_next_arg_value gets the next value from the arg list as type T.392 template <class T> LIBC_INLINE T get_next_arg_value() {393 return args_cur.template next_var<T>();394 }395 396 //----------------------------------------------------397 // INDEX MODE ONLY FUNCTIONS AFTER HERE:398 //----------------------------------------------------399 400#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE401 402 // parse_index parses the index of a value inside a format string. It403 // assumes that str[*local_pos] points to character after a '%' or '*', and404 // returns 0 if there is no closing $, or if it finds no number. If it finds a405 // number, it will move local_pos past the end of the $, else it will not move406 // local_pos.407 LIBC_INLINE size_t parse_index(size_t *local_pos) {408 if (internal::isdigit(str[*local_pos])) {409 auto result = internal::strtointeger<int>(str + *local_pos, 10);410 size_t index = static_cast<size_t>(result.value);411 if (str[*local_pos + static_cast<size_t>(result.parsed_len)] != '$')412 return 0;413 *local_pos = static_cast<size_t>(1 + result.parsed_len) + *local_pos;414 return index;415 }416 return 0;417 }418 419 LIBC_INLINE void set_type_desc(size_t index, TypeDesc value) {420 if (index != 0 && index <= DESC_ARR_LEN)421 desc_arr[index - 1] = value;422 }423 424 // get_arg_value gets the value from the arg list at index (starting at 1).425 // This may require parsing the format string. An index of 0 is interpreted as426 // the next value. If the format string is not valid, it may have gaps in its427 // indexes. Requesting the value for any index after a gap will fail, since428 // the arg list must be read in order and with the correct types.429 template <class T> LIBC_INLINE cpp::optional<T> get_arg_value(size_t index) {430 if (!(index == 0 || index == args_index)) {431 bool success = args_to_index(index);432 if (!success) {433 // If we can't get to this index, then the value of the arg can't be434 // found.435 return cpp::optional<T>();436 }437 }438 439 set_type_desc(index, type_desc_from_type<T>());440 441 ++args_index;442 return get_next_arg_value<T>();443 }444 445 // the ArgProvider can only return the next item in the list. This function is446 // used in index mode when the item that needs to be read is not the next one.447 // It moves cur_args to the index requested so the appropriate value may448 // be read. This may involve parsing the format string, and is in the worst449 // case an O(n^2) operation.450 LIBC_INLINE bool args_to_index(size_t index) {451 if (args_index > index) {452 args_index = 1;453 args_cur = args_start;454 }455 456 while (args_index < index) {457 TypeDesc cur_type_desc = type_desc_from_type<void>();458 if (args_index <= DESC_ARR_LEN)459 cur_type_desc = desc_arr[args_index - 1];460 461 if (cur_type_desc == type_desc_from_type<void>())462 cur_type_desc = get_type_desc(args_index);463 464 // A type of void represents the type being unknown. If the type for the465 // requested index isn't in the desc_arr and isn't found by parsing the466 // string, then then advancing to the requested index is impossible. In467 // that case the function returns false.468 if (cur_type_desc == type_desc_from_type<void>())469 return false;470 471 if (cur_type_desc == type_desc_from_type<uint32_t>())472 args_cur.template next_var<uint32_t>();473 else if (cur_type_desc == type_desc_from_type<uint64_t>())474 args_cur.template next_var<uint64_t>();475#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT476 // Floating point numbers are stored separately from the other arguments.477 else if (cur_type_desc == type_desc_from_type<double>())478 args_cur.template next_var<double>();479 else if (cur_type_desc == type_desc_from_type<long double>())480 args_cur.template next_var<long double>();481#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT482#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT483 // Floating point numbers may be stored separately from the other484 // arguments.485 else if (cur_type_desc == type_desc_from_type<short fract>())486 args_cur.template next_var<short fract>();487 else if (cur_type_desc == type_desc_from_type<fract>())488 args_cur.template next_var<fract>();489 else if (cur_type_desc == type_desc_from_type<long fract>())490 args_cur.template next_var<long fract>();491 else if (cur_type_desc == type_desc_from_type<short accum>())492 args_cur.template next_var<short accum>();493 else if (cur_type_desc == type_desc_from_type<accum>())494 args_cur.template next_var<accum>();495 else if (cur_type_desc == type_desc_from_type<long accum>())496 args_cur.template next_var<long accum>();497#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT498 // pointers may be stored separately from normal values.499 else if (cur_type_desc == type_desc_from_type<void *>())500 args_cur.template next_var<void *>();501 else502 args_cur.template next_var<uint32_t>();503 504 ++args_index;505 }506 return true;507 }508 509 // get_type_desc assumes that this format string uses index mode. It iterates510 // through the format string until it finds a format specifier that defines511 // the type of index, and returns a TypeDesc describing that type. It does not512 // modify cur_pos.513 LIBC_INLINE TypeDesc get_type_desc(size_t index) {514 // index mode is assumed, and the indices start at 1, so an index515 // of 0 is invalid.516 size_t local_pos = 0;517 518 while (str[local_pos]) {519 if (str[local_pos] == '%') {520 ++local_pos;521 522 size_t conv_index = parse_index(&local_pos);523 524 // the flags aren't relevant for this situation, but I need to skip past525 // them so they're parsed but the result is discarded.526 parse_flags(&local_pos);527 528 // handle width529 if (str[local_pos] == '*') {530 ++local_pos;531 532 size_t width_index = parse_index(&local_pos);533 set_type_desc(width_index, type_desc_from_type<int>());534 if (width_index == index)535 return type_desc_from_type<int>();536 537 } else if (internal::isdigit(str[local_pos])) {538 while (internal::isdigit(str[local_pos]))539 ++local_pos;540 }541 542 // handle precision543 if (str[local_pos] == '.') {544 ++local_pos;545 if (str[local_pos] == '*') {546 ++local_pos;547 548 size_t precision_index = parse_index(&local_pos);549 set_type_desc(precision_index, type_desc_from_type<int>());550 if (precision_index == index)551 return type_desc_from_type<int>();552 553 } else if (internal::isdigit(str[local_pos])) {554 while (internal::isdigit(str[local_pos]))555 ++local_pos;556 }557 }558 559 auto [lm, bw] = parse_length_modifier(&local_pos);560 561 // if we don't have an index for this conversion, then its position is562 // unknown and all this information is irrelevant. The rest of this563 // logic has been for skipping past this conversion properly to avoid564 // weirdness with %%.565 if (conv_index == 0) {566 if (str[local_pos] != '\0')567 ++local_pos;568 continue;569 }570 571 TypeDesc conv_size = type_desc_from_type<void>();572 switch (str[local_pos]) {573 case ('%'):574 conv_size = type_desc_from_type<void>();575 break;576 case ('c'):577 conv_size = type_desc_from_type<int>();578 break;579 case ('d'):580 case ('i'):581 case ('o'):582 case ('x'):583 case ('X'):584 case ('u'):585 case ('b'):586 case ('B'):587 switch (lm) {588 case (LengthModifier::hh):589 case (LengthModifier::h):590 case (LengthModifier::none):591 conv_size = type_desc_from_type<int>();592 break;593 case (LengthModifier::l):594 conv_size = type_desc_from_type<long>();595 break;596 case (LengthModifier::ll):597 case (LengthModifier::L): // This isn't in the standard, but is in598 // other libc implementations.599 conv_size = type_desc_from_type<long long>();600 break;601 case (LengthModifier::j):602 conv_size = type_desc_from_type<intmax_t>();603 break;604 case (LengthModifier::z):605 conv_size = type_desc_from_type<size_t>();606 break;607 case (LengthModifier::t):608 conv_size = type_desc_from_type<ptrdiff_t>();609 break;610 case (LengthModifier::w):611 case (LengthModifier::wf):612 if (bw <= cpp::numeric_limits<unsigned int>::digits) {613 conv_size = type_desc_from_type<int>();614 } else if (bw <= cpp::numeric_limits<unsigned long>::digits) {615 conv_size = type_desc_from_type<long>();616 } else if (bw <= cpp::numeric_limits<unsigned long long>::digits) {617 conv_size = type_desc_from_type<long long>();618 } else {619 conv_size = type_desc_from_type<intmax_t>();620 }621 break;622 }623 break;624#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT625 case ('f'):626 case ('F'):627 case ('e'):628 case ('E'):629 case ('a'):630 case ('A'):631 case ('g'):632 case ('G'):633 if (lm != LengthModifier::L)634 conv_size = type_desc_from_type<double>();635 else636 conv_size = type_desc_from_type<long double>();637 break;638#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT639#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT640 // Capitalization represents sign, but we only need to get the right641 // bitwidth here so we ignore that.642 case ('r'):643 case ('R'):644 conv_size = type_desc_from_type<uint32_t>();645 break;646 case ('k'):647 case ('K'):648 if (lm == LengthModifier::l) {649 conv_size = type_desc_from_type<uint64_t>();650 } else {651 conv_size = type_desc_from_type<uint32_t>();652 }653 break;654#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT655#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT656 case ('n'):657#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT658 case ('p'):659 case ('s'):660 conv_size = type_desc_from_type<void *>();661 break;662 default:663 conv_size = type_desc_from_type<int>();664 break;665 }666 667 set_type_desc(conv_index, conv_size);668 if (conv_index == index)669 return conv_size;670 }671 // If the end of the format section is on the '\0'. This means we need to672 // not advance the local_pos.673 if (str[local_pos] != '\0')674 ++local_pos;675 }676 677 // If there is no size for the requested index, then it's unknown. Return678 // void.679 return type_desc_from_type<void>();680 }681 682#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE683};684 685} // namespace printf_core686} // namespace LIBC_NAMESPACE_DECL687 688#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H689