15ea15203SVinayak Dev //===-- Implementation header for strfromx() utilitites -------------------===// 25ea15203SVinayak Dev // 35ea15203SVinayak Dev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ea15203SVinayak Dev // See https://llvm.org/LICENSE.txt for license information. 55ea15203SVinayak Dev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ea15203SVinayak Dev // 75ea15203SVinayak Dev //===----------------------------------------------------------------------===// 85ea15203SVinayak Dev 95ea15203SVinayak Dev // According to the C23 standard, any input character sequences except a 105ea15203SVinayak Dev // precision specifier and the usual floating point formats, namely 115ea15203SVinayak Dev // %{a,A,e,E,f,F,g,G}, are not allowed and any code that does otherwise results 125ea15203SVinayak Dev // in undefined behaviour(including use of a '%%' conversion specifier); which 135ea15203SVinayak Dev // in this case is that the buffer string is simply populated with the format 147789ec06SNick Desaulniers // string. The case of the input being nullptr should be handled in the calling 155ea15203SVinayak Dev // function (strfromf, strfromd, strfroml) itself. 165ea15203SVinayak Dev 175ea15203SVinayak Dev #ifndef LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H 185ea15203SVinayak Dev #define LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H 195ea15203SVinayak Dev 205ea15203SVinayak Dev #include "src/__support/CPP/type_traits.h" 21*5ff3ff33SPetr Hosek #include "src/__support/macros/config.h" 225ea15203SVinayak Dev #include "src/__support/str_to_integer.h" 235ea15203SVinayak Dev #include "src/stdio/printf_core/converter_atlas.h" 245ea15203SVinayak Dev #include "src/stdio/printf_core/core_structs.h" 255ea15203SVinayak Dev #include "src/stdio/printf_core/writer.h" 265ea15203SVinayak Dev 275ea15203SVinayak Dev #include <stddef.h> 285ea15203SVinayak Dev 29*5ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL { 30*5ff3ff33SPetr Hosek namespace internal { 315ea15203SVinayak Dev 325ea15203SVinayak Dev template <typename T> 335ea15203SVinayak Dev using storage_type = typename fputil::FPBits<T>::StorageType; 345ea15203SVinayak Dev 355ea15203SVinayak Dev template <typename T> 365ea15203SVinayak Dev printf_core::FormatSection parse_format_string(const char *__restrict format, 375ea15203SVinayak Dev T fp) { 385ea15203SVinayak Dev printf_core::FormatSection section; 395ea15203SVinayak Dev size_t cur_pos = 0; 405ea15203SVinayak Dev 415ea15203SVinayak Dev // There is no typed conversion function to convert single precision float 425ea15203SVinayak Dev // to hex exponential format, and the function convert_float_hex_exp() 435ea15203SVinayak Dev // requires a double or long double value to work correctly. 445ea15203SVinayak Dev // To work around this, we convert fp to double if it is single precision, and 455ea15203SVinayak Dev // then use that double precision value in the %{A, a} conversion specifiers. 465ea15203SVinayak Dev [[maybe_unused]] double new_fp; 475ea15203SVinayak Dev bool t_is_single_prec_type = cpp::is_same<T, float>::value; 485ea15203SVinayak Dev if (t_is_single_prec_type) 495ea15203SVinayak Dev new_fp = (double)fp; 505ea15203SVinayak Dev 515ea15203SVinayak Dev if (format[cur_pos] == '%') { 525ea15203SVinayak Dev section.has_conv = true; 535ea15203SVinayak Dev ++cur_pos; 545ea15203SVinayak Dev 555ea15203SVinayak Dev // handle precision 565ea15203SVinayak Dev section.precision = -1; 575ea15203SVinayak Dev if (format[cur_pos] == '.') { 585ea15203SVinayak Dev ++cur_pos; 595ea15203SVinayak Dev section.precision = 0; 605ea15203SVinayak Dev 615ea15203SVinayak Dev // The standard does not allow the '*' (asterisk) operator for strfromx() 625ea15203SVinayak Dev // functions 635ea15203SVinayak Dev if (internal::isdigit(format[cur_pos])) { 645ea15203SVinayak Dev auto result = internal::strtointeger<int>(format + cur_pos, 10); 655ea15203SVinayak Dev section.precision += result.value; 665ea15203SVinayak Dev cur_pos += result.parsed_len; 675ea15203SVinayak Dev } 685ea15203SVinayak Dev } 695ea15203SVinayak Dev 705ea15203SVinayak Dev section.conv_name = format[cur_pos]; 715ea15203SVinayak Dev switch (format[cur_pos]) { 725ea15203SVinayak Dev case 'a': 735ea15203SVinayak Dev case 'A': 745ea15203SVinayak Dev if (t_is_single_prec_type) 755ea15203SVinayak Dev section.conv_val_raw = cpp::bit_cast<storage_type<double>>(new_fp); 765ea15203SVinayak Dev else 775ea15203SVinayak Dev section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp); 785ea15203SVinayak Dev break; 795ea15203SVinayak Dev case 'e': 805ea15203SVinayak Dev case 'E': 815ea15203SVinayak Dev case 'f': 825ea15203SVinayak Dev case 'F': 835ea15203SVinayak Dev case 'g': 845ea15203SVinayak Dev case 'G': 855ea15203SVinayak Dev section.conv_val_raw = cpp::bit_cast<storage_type<T>>(fp); 865ea15203SVinayak Dev break; 875ea15203SVinayak Dev default: 885ea15203SVinayak Dev section.has_conv = false; 895ea15203SVinayak Dev while (format[cur_pos] != '\0') 905ea15203SVinayak Dev ++cur_pos; 915ea15203SVinayak Dev break; 925ea15203SVinayak Dev } 935ea15203SVinayak Dev 945ea15203SVinayak Dev if (format[cur_pos] != '\0') 955ea15203SVinayak Dev ++cur_pos; 965ea15203SVinayak Dev } else { 975ea15203SVinayak Dev section.has_conv = false; 985ea15203SVinayak Dev // We are looking for exactly one section, so no more '%' 995ea15203SVinayak Dev while (format[cur_pos] != '\0') 1005ea15203SVinayak Dev ++cur_pos; 1015ea15203SVinayak Dev } 1025ea15203SVinayak Dev 1035ea15203SVinayak Dev section.raw_string = {format, cur_pos}; 1045ea15203SVinayak Dev return section; 1055ea15203SVinayak Dev } 1065ea15203SVinayak Dev 1075ea15203SVinayak Dev template <typename T> 1085ea15203SVinayak Dev int strfromfloat_convert(printf_core::Writer *writer, 1095ea15203SVinayak Dev const printf_core::FormatSection §ion) { 1105ea15203SVinayak Dev if (!section.has_conv) 1115ea15203SVinayak Dev return writer->write(section.raw_string); 1125ea15203SVinayak Dev 1135ea15203SVinayak Dev auto res = static_cast<storage_type<T>>(section.conv_val_raw); 1145ea15203SVinayak Dev 1155ea15203SVinayak Dev fputil::FPBits<T> strfromfloat_bits(res); 1165ea15203SVinayak Dev if (strfromfloat_bits.is_inf_or_nan()) 1175ea15203SVinayak Dev return convert_inf_nan(writer, section); 1185ea15203SVinayak Dev 1195ea15203SVinayak Dev switch (section.conv_name) { 1205ea15203SVinayak Dev case 'f': 1215ea15203SVinayak Dev case 'F': 1225ea15203SVinayak Dev return convert_float_decimal_typed(writer, section, strfromfloat_bits); 1235ea15203SVinayak Dev case 'e': 1245ea15203SVinayak Dev case 'E': 1255ea15203SVinayak Dev return convert_float_dec_exp_typed(writer, section, strfromfloat_bits); 1265ea15203SVinayak Dev case 'a': 1275ea15203SVinayak Dev case 'A': 1285ea15203SVinayak Dev return convert_float_hex_exp(writer, section); 1295ea15203SVinayak Dev case 'g': 1305ea15203SVinayak Dev case 'G': 1315ea15203SVinayak Dev return convert_float_dec_auto_typed(writer, section, strfromfloat_bits); 1325ea15203SVinayak Dev default: 1335ea15203SVinayak Dev return writer->write(section.raw_string); 1345ea15203SVinayak Dev } 1355ea15203SVinayak Dev return -1; 1365ea15203SVinayak Dev } 1375ea15203SVinayak Dev 138*5ff3ff33SPetr Hosek } // namespace internal 139*5ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL 1405ea15203SVinayak Dev 1415ea15203SVinayak Dev #endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H 142