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> 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> 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); 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