[libc++][Android] Allow testing libc++ with clang-r536225 (#116149)
[llvm-project.git] / libc / src / stdlib / str_from_util.h
blob7f54bdf71a018232321e82d03f53f921e58e54cb
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 //===----------------------------------------------------------------------===//
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"
27 #include <stddef.h>
29 namespace LIBC_NAMESPACE_DECL {
30 namespace internal {
32 template <typename T>
33 using storage_type = typename fputil::FPBits<T>::StorageType;
35 template <typename T>
36 printf_core::FormatSection parse_format_string(const char *__restrict format,
37 T fp) {
38 printf_core::FormatSection section;
39 size_t cur_pos = 0;
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)
49 new_fp = (double)fp;
51 if (format[cur_pos] == '%') {
52 section.has_conv = true;
53 ++cur_pos;
55 // handle precision
56 section.precision = -1;
57 if (format[cur_pos] == '.') {
58 ++cur_pos;
59 section.precision = 0;
61 // The standard does not allow the '*' (asterisk) operator for strfromx()
62 // functions
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]) {
72 case 'a':
73 case 'A':
74 if (t_is_single_prec_type)
75 section.conv_val_raw = cpp::bit_cast<storage_type<double>>(new_fp);
76 else
77 section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp);
78 break;
79 case 'e':
80 case 'E':
81 case 'f':
82 case 'F':
83 case 'g':
84 case 'G':
85 section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp);
86 break;
87 default:
88 section.has_conv = false;
89 while (format[cur_pos] != '\0')
90 ++cur_pos;
91 break;
94 if (format[cur_pos] != '\0')
95 ++cur_pos;
96 } else {
97 section.has_conv = false;
98 // We are looking for exactly one section, so no more '%'
99 while (format[cur_pos] != '\0')
100 ++cur_pos;
103 section.raw_string = {format, cur_pos};
104 return section;
107 template <typename T>
108 int strfromfloat_convert(printf_core::Writer *writer,
109 const printf_core::FormatSection &section) {
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) {
120 case 'f':
121 case 'F':
122 return convert_float_decimal_typed(writer, section, strfromfloat_bits);
123 case 'e':
124 case 'E':
125 return convert_float_dec_exp_typed(writer, section, strfromfloat_bits);
126 case 'a':
127 case 'A':
128 return convert_float_hex_exp(writer, section);
129 case 'g':
130 case 'G':
131 return convert_float_dec_auto_typed(writer, section, strfromfloat_bits);
132 default:
133 return writer->write(section.raw_string);
135 return -1;
138 } // namespace internal
139 } // namespace LIBC_NAMESPACE_DECL
141 #endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H