xref: /aosp_15_r20/external/llvm-libc/src/stdlib/str_from_util.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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/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"
26 
27 #include <stddef.h>
28 
29 namespace LIBC_NAMESPACE_DECL {
30 namespace internal {
31 
32 template <typename T>
33 using storage_type = typename fputil::FPBits<T>::StorageType;
34 
35 template <typename T>
parse_format_string(const char * __restrict format,T fp)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;
40 
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;
50 
51   if (format[cur_pos] == '%') {
52     section.has_conv = true;
53     ++cur_pos;
54 
55     // handle precision
56     section.precision = -1;
57     if (format[cur_pos] == '.') {
58       ++cur_pos;
59       section.precision = 0;
60 
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;
67       }
68     }
69 
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;
92     }
93 
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;
101   }
102 
103   section.raw_string = {format, cur_pos};
104   return section;
105 }
106 
107 template <typename T>
strfromfloat_convert(printf_core::Writer * writer,const printf_core::FormatSection & section)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);
112 
113   auto res = static_cast<storage_type<T>>(section.conv_val_raw);
114 
115   fputil::FPBits<T> strfromfloat_bits(res);
116   if (strfromfloat_bits.is_inf_or_nan())
117     return convert_inf_nan(writer, section);
118 
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);
134   }
135   return -1;
136 }
137 
138 } // namespace internal
139 } // namespace LIBC_NAMESPACE_DECL
140 
141 #endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H
142