• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
parse_format_string(const char * __restrict format,T fp)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(format[cur_pos])) {
62         auto result = internal::strtointeger<int>(format + cur_pos, 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>>(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>
strfromfloat_convert(printf_core::Writer * writer,const printf_core::FormatSection & section)106 int strfromfloat_convert(printf_core::Writer *writer,
107                          const printf_core::FormatSection &section) {
108   if (!section.has_conv)
109     return writer->write(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, 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, section);
127   case 'g':
128   case 'G':
129     return convert_float_dec_auto_typed(writer, section, strfromfloat_bits);
130   default:
131     return writer->write(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