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
28namespace LIBC_NAMESPACE::internal {
29
30template <typename T>
31using storage_type = typename fputil::FPBits<T>::StorageType;
32
33template <typename T>
34printf_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(ch: format[cur_pos])) {
62 auto result = internal::strtointeger<int>(src: format + cur_pos, base: 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>>(from: 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
105template <typename T>
106int strfromfloat_convert(printf_core::Writer *writer,
107 const printf_core::FormatSection &section) {
108 if (!section.has_conv)
109 return writer->write(new_string: 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, to_conv: 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, to_conv: section);
127 case 'g':
128 case 'G':
129 return convert_float_dec_auto_typed(writer, section, strfromfloat_bits);
130 default:
131 return writer->write(new_string: 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

source code of libc/src/stdlib/str_from_util.h