Warning: This file is not a C or C++ file. It does not have highlighting.

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/__support/libc_errno.h"
29#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
30
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_POINT
45template <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_POINT
50
51template <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 = static_cast<decltype(dst)>( \
61 cpp::bit_cast<int_type_of_v<arg_type>>(temp.value())); \
62 } \
63 }
64#else
65#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_MODE
68
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_MODE
76 // args_start stores the start of the va_args, which is allows getting the
77 // value of arguments that have already been passed. args_index is tracked so
78 // 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.h
83 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 used
86 // in index mode to reduce repeated string parsing. The sizes are stored as
87 // TypeDesc objects, which store the size as well as minimal type information.
88 // This is necessary because some systems separate the floating point and
89 // 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_MODE
95
96public:
97#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
98 LIBC_INLINE Parser(const char *__restrict new_str, ArgProvider &args)
99 : str(new_str), args_cur(args), args_start(args) {}
100#else
101 LIBC_INLINE Parser(const char *__restrict new_str, ArgProvider &args)
102 : str(new_str), args_cur(args) {}
103#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
104
105 // get_next_section will parse the format string until it has a fully
106 // specified format section. This can either be a raw format section with no
107 // conversion, or a format section with a conversion that has all of its
108 // 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 section
114 section.has_conv = true;
115
116 ++cur_pos;
117 [[maybe_unused]] size_t conv_index = 0;
118
119#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
120 conv_index = parse_index(&cur_pos);
121#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
122
123 section.flags = parse_flags(&cur_pos);
124
125 // handle width
126 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 precision
144 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, the
148 // 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 standard
168 // says that "The complete conversion specification shall be %%" but it
169 // also says that "If a conversion specification is invalid, the
170 // behavior is undefined." Based on that we define that any conversion
171 // specification ending in '%' shall display as '%' regardless of any
172 // 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 other
197 // 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_FLOAT
231 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_FLOAT
246#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
247 // Capitalization represents sign, but we only need to get the right
248 // 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 doing
252 // 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_POINT
265#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
266 case ('m'):
267 // %m is an odd conversion in that it doesn't consume an argument, it
268 // 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_STRERROR
273#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
274 case ('n'): // Intentional fallthrough
275#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
276 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 to
288 // not advance the cur_pos.
289 if (str[cur_pos] != '\0')
290 ++cur_pos;
291
292 } else {
293 // raw section
294 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 that
304 // str[*local_pos] is inside a format specifier, and parses any flags it
305 // finds. It returns a FormatFlags object containing the set of found flags
306 // 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. It
337 // assumes that str[*local_pos] is inside a format specifier. It returns a
338 // LengthModifier with the length modifier it found. It will advance local_pos
339 // 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_MODE
401
402 // parse_index parses the index of a value inside a format string. It
403 // assumes that str[*local_pos] points to character after a '%' or '*', and
404 // returns 0 if there is no closing $, or if it finds no number. If it finds a
405 // number, it will move local_pos past the end of the $, else it will not move
406 // 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 as
426 // the next value. If the format string is not valid, it may have gaps in its
427 // indexes. Requesting the value for any index after a gap will fail, since
428 // 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 be
434 // 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 is
446 // 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 may
448 // be read. This may involve parsing the format string, and is in the worst
449 // 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 the
465 // requested index isn't in the desc_arr and isn't found by parsing the
466 // string, then then advancing to the requested index is impossible. In
467 // 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_FLOAT
476 // 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_FLOAT
482#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
483 // Floating point numbers may be stored separately from the other
484 // 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_POINT
498 // 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 else
502 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 iterates
510 // through the format string until it finds a format specifier that defines
511 // the type of index, and returns a TypeDesc describing that type. It does not
512 // 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 index
515 // 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 past
525 // them so they're parsed but the result is discarded.
526 parse_flags(&local_pos);
527
528 // handle width
529 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 precision
543 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 is
562 // unknown and all this information is irrelevant. The rest of this
563 // logic has been for skipping past this conversion properly to avoid
564 // 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 in
598 // 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_FLOAT
625 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 else
636 conv_size = type_desc_from_type<long double>();
637 break;
638#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
639#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
640 // Capitalization represents sign, but we only need to get the right
641 // 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_POINT
655#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
656 case ('n'):
657#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
658 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 to
672 // 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. Return
678 // void.
679 return type_desc_from_type<void>();
680 }
681
682#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
683};
684
685} // namespace printf_core
686} // namespace LIBC_NAMESPACE_DECL
687
688#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
689

Warning: This file is not a C or C++ file. It does not have highlighting.

source code of libc/src/stdio/printf_core/parser.h