/src/boost/boost/charconv/detail/fallback_routines.hpp
Line | Count | Source |
1 | | // Copyright 2024 Matt Borland |
2 | | // Distributed under the Boost Software License, Version 1.0. |
3 | | // https://www.boost.org/LICENSE_1_0.txt |
4 | | |
5 | | #ifndef BOOST_FALLBACK_ROUTINES_HPP |
6 | | #define BOOST_FALLBACK_ROUTINES_HPP |
7 | | |
8 | | #include <boost/charconv/detail/to_chars_integer_impl.hpp> |
9 | | #include <boost/charconv/detail/dragonbox/floff.hpp> |
10 | | #include <boost/charconv/detail/config.hpp> |
11 | | #include <boost/charconv/detail/from_chars_result.hpp> |
12 | | #include <boost/charconv/chars_format.hpp> |
13 | | #include <system_error> |
14 | | #include <type_traits> |
15 | | #include <locale> |
16 | | #include <clocale> |
17 | | #include <cstring> |
18 | | #include <cstdio> |
19 | | |
20 | | namespace boost { |
21 | | namespace charconv { |
22 | | namespace detail { |
23 | | |
24 | | template <typename T> |
25 | | inline int print_val(char* first, std::size_t size, char* format, T value) noexcept |
26 | 0 | { |
27 | 0 | return std::snprintf(first, size, format, value); |
28 | 0 | } |
29 | | |
30 | | template <typename T> |
31 | | to_chars_result to_chars_printf_impl(char* first, char* last, T value, chars_format fmt, int precision) |
32 | 0 | { |
33 | | // v % + . + num_digits(INT_MAX) + specifier + null terminator |
34 | | // 1 + 1 + 10 + 1 + 1 |
35 | 0 | char format[14] {}; |
36 | 0 | std::memcpy(format, "%", 1); // NOLINT : No null terminator is purposeful |
37 | 0 | std::size_t pos = 1; |
38 | | |
39 | | // precision of -1 is unspecified |
40 | 0 | if (precision != -1 && fmt != chars_format::fixed) |
41 | 0 | { |
42 | 0 | format[pos] = '.'; |
43 | 0 | ++pos; |
44 | 0 | const auto unsigned_precision = static_cast<std::uint32_t>(precision); |
45 | 0 | if (unsigned_precision < 10) |
46 | 0 | { |
47 | 0 | boost::charconv::detail::print_1_digit(unsigned_precision, format + pos); |
48 | 0 | ++pos; |
49 | 0 | } |
50 | 0 | else if (unsigned_precision < 100) |
51 | 0 | { |
52 | 0 | boost::charconv::detail::print_2_digits(unsigned_precision, format + pos); |
53 | 0 | pos += 2; |
54 | 0 | } |
55 | 0 | else |
56 | 0 | { |
57 | 0 | boost::charconv::detail::to_chars_int(format + pos, format + sizeof(format), precision); |
58 | 0 | pos = std::strlen(format); |
59 | 0 | } |
60 | 0 | } |
61 | 0 | else if (fmt == chars_format::fixed) |
62 | 0 | { |
63 | | // Force 0 decimal places |
64 | 0 | std::memcpy(format + pos, ".0", 2); // NOLINT : No null terminator is purposeful |
65 | 0 | pos += 2; |
66 | 0 | } |
67 | | |
68 | | // Add the type identifier |
69 | 0 | BOOST_CHARCONV_IF_CONSTEXPR (std::is_same<T, long double>::value) |
70 | 0 | { |
71 | 0 | format[pos] = 'L'; |
72 | 0 | ++pos; |
73 | 0 | } |
74 | | |
75 | | // Add the format character |
76 | 0 | switch (fmt) |
77 | 0 | { |
78 | 0 | case boost::charconv::chars_format::general: |
79 | 0 | format[pos] = 'g'; |
80 | 0 | break; |
81 | | |
82 | 0 | case boost::charconv::chars_format::scientific: |
83 | 0 | format[pos] = 'e'; |
84 | 0 | break; |
85 | | |
86 | 0 | case boost::charconv::chars_format::fixed: |
87 | 0 | format[pos] = 'f'; |
88 | 0 | break; |
89 | | |
90 | 0 | case boost::charconv::chars_format::hex: |
91 | 0 | format[pos] = 'a'; |
92 | 0 | break; |
93 | 0 | } |
94 | | |
95 | 0 | const auto rv = print_val(first, static_cast<std::size_t>(last - first), format, value); |
96 | |
|
97 | 0 | if (rv <= 0) |
98 | 0 | { |
99 | 0 | return {last, static_cast<std::errc>(errno)}; |
100 | 0 | } |
101 | | |
102 | 0 | return {first + rv, std::errc()}; |
103 | 0 | } |
104 | | |
105 | | #ifdef BOOST_MSVC |
106 | | # pragma warning(push) |
107 | | # pragma warning(disable: 4244) // Implict converion when BOOST_IF_CONSTEXPR expands to if |
108 | | #elif defined(__GNUC__) && __GNUC__ >= 5 |
109 | | # pragma GCC diagnostic push |
110 | | # pragma GCC diagnostic ignored "-Wmissing-field-initializers" |
111 | | # pragma GCC diagnostic ignored "-Wfloat-conversion" |
112 | | #elif defined(__clang__) && __clang_major__ > 7 |
113 | | # pragma clang diagnostic push |
114 | | # pragma clang diagnostic ignored "-Wimplicit-float-conversion" |
115 | | #elif defined(__clang__) && __clang_major__ <= 7 |
116 | | # pragma clang diagnostic push |
117 | | # pragma clang diagnostic ignored "-Wconversion" |
118 | | #endif |
119 | | |
120 | | // We know that the string is in the "C" locale because it would have previously passed through our parser. |
121 | | // Convert the string into the current locale so that the strto* family of functions |
122 | | // works correctly for the given locale. |
123 | | // |
124 | | // We are operating on our own copy of the buffer, so we are free to modify it. |
125 | | inline void convert_string_locale(char* buffer) noexcept |
126 | 0 | { |
127 | 0 | const auto locale_decimal_point = *std::localeconv()->decimal_point; |
128 | 0 | if (locale_decimal_point != '.') |
129 | 0 | { |
130 | 0 | auto p = std::strchr(buffer, '.'); |
131 | 0 | if (p != nullptr) |
132 | 0 | { |
133 | 0 | *p = locale_decimal_point; |
134 | 0 | } |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | template <typename T> |
139 | | from_chars_result from_chars_strtod_impl(const char* first, const char* last, T& value, char* buffer) noexcept |
140 | | { |
141 | | // For strto(f/d) |
142 | | // Floating point value corresponding to the contents of str on success. |
143 | | // If the converted value falls out of range of corresponding return type, range error occurs and HUGE_VAL, HUGE_VALF or HUGE_VALL is returned. |
144 | | // If no conversion can be performed, 0 is returned and *str_end is set to str. |
145 | | |
146 | | std::memcpy(buffer, first, static_cast<std::size_t>(last - first)); |
147 | | buffer[last - first] = '\0'; |
148 | | convert_string_locale(buffer); |
149 | | |
150 | | char* str_end; |
151 | | T return_value {}; |
152 | | from_chars_result r {nullptr, std::errc()}; |
153 | | |
154 | | BOOST_IF_CONSTEXPR (std::is_same<T, float>::value) |
155 | | { |
156 | | return_value = std::strtof(buffer, &str_end); |
157 | | |
158 | | #ifndef __INTEL_LLVM_COMPILER |
159 | | if (return_value == HUGE_VALF) |
160 | | #else |
161 | | if (return_value >= (std::numeric_limits<T>::max)()) |
162 | | #endif |
163 | | { |
164 | | r = {last, std::errc::result_out_of_range}; |
165 | | } |
166 | | } |
167 | | else BOOST_IF_CONSTEXPR (std::is_same<T, double>::value) |
168 | | { |
169 | | return_value = std::strtod(buffer, &str_end); |
170 | | |
171 | | #ifndef __INTEL_LLVM_COMPILER |
172 | | if (return_value == HUGE_VAL) |
173 | | #else |
174 | | if (return_value >= (std::numeric_limits<T>::max)()) |
175 | | #endif |
176 | | { |
177 | | r = {last, std::errc::result_out_of_range}; |
178 | | } |
179 | | } |
180 | | else BOOST_IF_CONSTEXPR (std::is_same<T, long double>::value) |
181 | | { |
182 | | return_value = std::strtold(buffer, &str_end); |
183 | | |
184 | | #ifndef __INTEL_LLVM_COMPILER |
185 | | if (return_value == HUGE_VALL) |
186 | | #else |
187 | | if (return_value >= (std::numeric_limits<T>::max)()) |
188 | | #endif |
189 | | { |
190 | | r = {last, std::errc::result_out_of_range}; |
191 | | } |
192 | | } |
193 | | |
194 | | // Since this is a fallback routine we are safe to check for 0 |
195 | | if (return_value == 0 && str_end == last) |
196 | | { |
197 | | r = {first, std::errc::result_out_of_range}; |
198 | | } |
199 | | |
200 | | if (r) |
201 | | { |
202 | | value = return_value; |
203 | | r = {first + (str_end - buffer), std::errc()}; |
204 | | } |
205 | | |
206 | | return r; |
207 | | } |
208 | | |
209 | | template <typename T> |
210 | | inline from_chars_result from_chars_strtod(const char* first, const char* last, T& value) noexcept |
211 | | { |
212 | | if (last - first < 1024) |
213 | | { |
214 | | char buffer[1024]; |
215 | | return from_chars_strtod_impl(first, last, value, buffer); |
216 | | } |
217 | | |
218 | | // If the string to be parsed does not fit into the 1024 byte static buffer than we have to allocate a buffer. |
219 | | // malloc is used here because it does not throw on allocation failure. |
220 | | |
221 | | char* buffer = static_cast<char*>(std::malloc(static_cast<std::size_t>(last - first + 1))); |
222 | | if (buffer == nullptr) |
223 | | { |
224 | | return {first, std::errc::not_enough_memory}; |
225 | | } |
226 | | |
227 | | auto r = from_chars_strtod_impl(first, last, value, buffer); |
228 | | std::free(buffer); |
229 | | |
230 | | return r; |
231 | | } |
232 | | |
233 | | #ifdef BOOST_MSVC |
234 | | # pragma warning(pop) |
235 | | #elif defined(__GNUC__) && __GNUC__ >= 5 |
236 | | # pragma GCC diagnostic pop |
237 | | #elif defined(__clang__) |
238 | | # pragma clang diagnostic pop |
239 | | #endif |
240 | | |
241 | | } //namespace detail |
242 | | } //namespace charconv |
243 | | } //namespace boost |
244 | | |
245 | | #endif //BOOST_FALLBACK_ROUTINES_HPP |