xref: /llvm-project/libc/src/stdlib/str_from_util.h (revision 5ff3ff33ff930e4ec49da7910612d8a41eb068cb)
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 &section) {
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