1 //===-- Implementation header for strfromx() utilitites -------------------===//
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
7 //===----------------------------------------------------------------------===//
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.
17 #ifndef LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
18 #define LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
20 #include "src/__support/CPP/type_traits.h"
21 #include "src/__support/macros/config.h"
22 #include "src/__support/str_to_integer.h"
23 #include "src/stdio/printf_core/converter_atlas.h"
24 #include "src/stdio/printf_core/core_structs.h"
25 #include "src/stdio/printf_core/writer.h"
29 namespace LIBC_NAMESPACE_DECL
{
33 using storage_type
= typename
fputil::FPBits
<T
>::StorageType
;
36 printf_core::FormatSection
parse_format_string(const char *__restrict format
,
38 printf_core::FormatSection section
;
41 // There is no typed conversion function to convert single precision float
42 // to hex exponential format, and the function convert_float_hex_exp()
43 // requires a double or long double value to work correctly.
44 // To work around this, we convert fp to double if it is single precision, and
45 // then use that double precision value in the %{A, a} conversion specifiers.
46 [[maybe_unused
]] double new_fp
;
47 bool t_is_single_prec_type
= cpp::is_same
<T
, float>::value
;
48 if (t_is_single_prec_type
)
51 if (format
[cur_pos
] == '%') {
52 section
.has_conv
= true;
56 section
.precision
= -1;
57 if (format
[cur_pos
] == '.') {
59 section
.precision
= 0;
61 // The standard does not allow the '*' (asterisk) operator for strfromx()
63 if (internal::isdigit(format
[cur_pos
])) {
64 auto result
= internal::strtointeger
<int>(format
+ cur_pos
, 10);
65 section
.precision
+= result
.value
;
66 cur_pos
+= result
.parsed_len
;
70 section
.conv_name
= format
[cur_pos
];
71 switch (format
[cur_pos
]) {
74 if (t_is_single_prec_type
)
75 section
.conv_val_raw
= cpp::bit_cast
<storage_type
<double>>(new_fp
);
77 section
.conv_val_raw
= cpp::bit_cast
<storage_type
<T
>>(fp
);
85 section
.conv_val_raw
= cpp::bit_cast
<storage_type
<T
>>(fp
);
88 section
.has_conv
= false;
89 while (format
[cur_pos
] != '\0')
94 if (format
[cur_pos
] != '\0')
97 section
.has_conv
= false;
98 // We are looking for exactly one section, so no more '%'
99 while (format
[cur_pos
] != '\0')
103 section
.raw_string
= {format
, cur_pos
};
107 template <typename T
>
108 int strfromfloat_convert(printf_core::Writer
*writer
,
109 const printf_core::FormatSection
§ion
) {
110 if (!section
.has_conv
)
111 return writer
->write(section
.raw_string
);
113 auto res
= static_cast<storage_type
<T
>>(section
.conv_val_raw
);
115 fputil::FPBits
<T
> strfromfloat_bits(res
);
116 if (strfromfloat_bits
.is_inf_or_nan())
117 return convert_inf_nan(writer
, section
);
119 switch (section
.conv_name
) {
122 return convert_float_decimal_typed(writer
, section
, strfromfloat_bits
);
125 return convert_float_dec_exp_typed(writer
, section
, strfromfloat_bits
);
128 return convert_float_hex_exp(writer
, section
);
131 return convert_float_dec_auto_typed(writer
, section
, strfromfloat_bits
);
133 return writer
->write(section
.raw_string
);
138 } // namespace internal
139 } // namespace LIBC_NAMESPACE_DECL
141 #endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H