1 | //===-- Implementation header for strfromx() utilitites -------------------===// |
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 | // According to the C23 standard, any input character sequences except a |
10 | // precision specifier and the usual floating point formats, namely |
11 | // %{a,A,e,E,f,F,g,G}, are not allowed and any code that does otherwise results |
12 | // in undefined behaviour(including use of a '%%' conversion specifier); which |
13 | // in this case is that the buffer string is simply populated with the format |
14 | // string. The case of the input being nullptr should be handled in the calling |
15 | // function (strfromf, strfromd, strfroml) itself. |
16 | |
17 | #ifndef LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H |
18 | #define LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H |
19 | |
20 | #include "src/__support/CPP/type_traits.h" |
21 | #include "src/__support/str_to_integer.h" |
22 | #include "src/stdio/printf_core/converter_atlas.h" |
23 | #include "src/stdio/printf_core/core_structs.h" |
24 | #include "src/stdio/printf_core/writer.h" |
25 | |
26 | #include <stddef.h> |
27 | |
28 | namespace LIBC_NAMESPACE::internal { |
29 | |
30 | template <typename T> |
31 | using storage_type = typename fputil::FPBits<T>::StorageType; |
32 | |
33 | template <typename T> |
34 | printf_core::FormatSection parse_format_string(const char *__restrict format, |
35 | T fp) { |
36 | printf_core::FormatSection section; |
37 | size_t cur_pos = 0; |
38 | |
39 | // There is no typed conversion function to convert single precision float |
40 | // to hex exponential format, and the function convert_float_hex_exp() |
41 | // requires a double or long double value to work correctly. |
42 | // To work around this, we convert fp to double if it is single precision, and |
43 | // then use that double precision value in the %{A, a} conversion specifiers. |
44 | [[maybe_unused]] double new_fp; |
45 | bool t_is_single_prec_type = cpp::is_same<T, float>::value; |
46 | if (t_is_single_prec_type) |
47 | new_fp = (double)fp; |
48 | |
49 | if (format[cur_pos] == '%') { |
50 | section.has_conv = true; |
51 | ++cur_pos; |
52 | |
53 | // handle precision |
54 | section.precision = -1; |
55 | if (format[cur_pos] == '.') { |
56 | ++cur_pos; |
57 | section.precision = 0; |
58 | |
59 | // The standard does not allow the '*' (asterisk) operator for strfromx() |
60 | // functions |
61 | if (internal::isdigit(ch: format[cur_pos])) { |
62 | auto result = internal::strtointeger<int>(src: format + cur_pos, base: 10); |
63 | section.precision += result.value; |
64 | cur_pos += result.parsed_len; |
65 | } |
66 | } |
67 | |
68 | section.conv_name = format[cur_pos]; |
69 | switch (format[cur_pos]) { |
70 | case 'a': |
71 | case 'A': |
72 | if (t_is_single_prec_type) |
73 | section.conv_val_raw = cpp::bit_cast<storage_type<double>>(from: new_fp); |
74 | else |
75 | section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp); |
76 | break; |
77 | case 'e': |
78 | case 'E': |
79 | case 'f': |
80 | case 'F': |
81 | case 'g': |
82 | case 'G': |
83 | section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp); |
84 | break; |
85 | default: |
86 | section.has_conv = false; |
87 | while (format[cur_pos] != '\0') |
88 | ++cur_pos; |
89 | break; |
90 | } |
91 | |
92 | if (format[cur_pos] != '\0') |
93 | ++cur_pos; |
94 | } else { |
95 | section.has_conv = false; |
96 | // We are looking for exactly one section, so no more '%' |
97 | while (format[cur_pos] != '\0') |
98 | ++cur_pos; |
99 | } |
100 | |
101 | section.raw_string = {format, cur_pos}; |
102 | return section; |
103 | } |
104 | |
105 | template <typename T> |
106 | int strfromfloat_convert(printf_core::Writer *writer, |
107 | const printf_core::FormatSection §ion) { |
108 | if (!section.has_conv) |
109 | return writer->write(new_string: section.raw_string); |
110 | |
111 | auto res = static_cast<storage_type<T>>(section.conv_val_raw); |
112 | |
113 | fputil::FPBits<T> strfromfloat_bits(res); |
114 | if (strfromfloat_bits.is_inf_or_nan()) |
115 | return convert_inf_nan(writer, to_conv: section); |
116 | |
117 | switch (section.conv_name) { |
118 | case 'f': |
119 | case 'F': |
120 | return convert_float_decimal_typed(writer, section, strfromfloat_bits); |
121 | case 'e': |
122 | case 'E': |
123 | return convert_float_dec_exp_typed(writer, section, strfromfloat_bits); |
124 | case 'a': |
125 | case 'A': |
126 | return convert_float_hex_exp(writer, to_conv: section); |
127 | case 'g': |
128 | case 'G': |
129 | return convert_float_dec_auto_typed(writer, section, strfromfloat_bits); |
130 | default: |
131 | return writer->write(new_string: section.raw_string); |
132 | } |
133 | return -1; |
134 | } |
135 | |
136 | } // namespace LIBC_NAMESPACE::internal |
137 | |
138 | #endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H |
139 | |