Coverage Report

Created: 2026-11-12 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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