142 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9// According to the C23 standard, any input character sequences except a10// precision specifier and the usual floating point formats, namely11// %{a,A,e,E,f,F,g,G}, are not allowed and any code that does otherwise results12// in undefined behaviour(including use of a '%%' conversion specifier); which13// in this case is that the buffer string is simply populated with the format14// string. The case of the input being nullptr should be handled in the calling15// function (strfromf, strfromd, strfroml) itself.16 17#ifndef LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H18#define LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H19 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 29namespace LIBC_NAMESPACE_DECL {30namespace internal {31 32template <typename T>33using storage_type = typename fputil::FPBits<T>::StorageType;34 35template <typename T>36printf_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 float42 // 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, and45 // 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 precision56 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 // functions63 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 else77 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 107template <typename T, printf_core::WriteMode write_mode>108int strfromfloat_convert(printf_core::Writer<write_mode> *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 internal139} // namespace LIBC_NAMESPACE_DECL140 141#endif // LLVM_LIBC_SRC_STDLIB_STRFROM_UTIL_H142