Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include <charconv>
19 : : #include <limits>
20 : : #include <system_error>
21 : :
22 : : #if __has_include(<fast_float/fast_float.h>)
23 : : # include <fast_float/fast_float.h>
24 : : #endif
25 : :
26 : : #include "pl_string.h"
27 : :
28 : : [[maybe_unused]]
29 : : static int ccStrPrintDouble( char *str, int bufsize, int decimals, double value );
30 : :
31 : : namespace {
32 : :
33 : : template <typename T>
34 : : struct has_std_to_chars_impl {
35 : : template <typename CT>
36 : : static auto _(CT s) -> decltype(std::to_chars(s, s, std::declval<T>()), std::true_type{});
37 : : static auto _(...) -> std::false_type;
38 : : static constexpr bool value = decltype(_((char *){}))::value;
39 : : };
40 : :
41 : : template <typename T>
42 : : constexpr bool has_std_to_chars = has_std_to_chars_impl<T>::value;
43 : :
44 : : template <typename T, typename... Args>
45 : : static inline int to_chars(char *buf, size_t len, T n, Args ...args)
46 : : {
47 : : if constexpr (has_std_to_chars<T>) {
48 : 138711 : auto [ptr, ec] = std::to_chars(buf, buf + len, n, args...);
49 [ + - - - : 138711 : return ec == std::errc() ? ptr - buf : 0;
+ - + - +
- + - +
- ]
50 : : } else {
51 : : static_assert(std::is_same_v<float, T> || std::is_same_v<double, T>,
52 : : "Not implemented!");
53 : : // FIXME: Fallback for GCC <= 10 currently required for MinGW-w64 on
54 : : // Ubuntu 22.04. Remove this when Ubuntu 24.04 is released, as it will
55 : : // provide newer MinGW-w64 GCC and it will be safe to require it.
56 : : return ccStrPrintDouble(buf, len, std::numeric_limits<T>::max_digits10, n);
57 : : }
58 : : }
59 : :
60 : : template <typename T>
61 : : struct has_std_from_chars_impl {
62 : : template <typename CT>
63 : : static auto _(CT s) -> decltype(std::from_chars(s, s, std::declval<T&>()), std::true_type{});
64 : : static auto _(...) -> std::false_type;
65 : : static constexpr bool value = decltype(_((const char *){}))::value;
66 : : };
67 : :
68 : : template <typename T>
69 : : constexpr bool has_std_from_chars = has_std_from_chars_impl<T>::value;
70 : :
71 : : template <typename T, typename... Args>
72 : : static inline bool from_chars(pl_str str, T &n, Args ...args)
73 : : {
74 : : if constexpr (has_std_from_chars<T>) {
75 : 365 : auto [ptr, ec] = std::from_chars((const char *) str.buf,
76 : : (const char *) str.buf + str.len,
77 : : n, args...);
78 : 365 : return ec == std::errc();
79 : : } else {
80 : : constexpr bool is_fp = std::is_same_v<float, T> || std::is_same_v<double, T>;
81 : : static_assert(is_fp, "Not implemented!");
82 : : #if !__has_include(<fast_float/fast_float.h>)
83 : : static_assert(!is_fp, "<fast_float/fast_float.h> is required, but not " \
84 : : "found. Please run `git submodule update --init`" \
85 : : " or provide <fast_float/fast_float.h>");
86 : : #else
87 : : // FIXME: Fallback for libc++, as it does not implement floating-point
88 : : // variant of std::from_chars. Remove this when appropriate.
89 : : auto [ptr, ec] = fast_float::from_chars((const char *) str.buf,
90 : : (const char *) str.buf + str.len,
91 : : n, args...);
92 : : return ec == std::errc();
93 : : #endif
94 : : }
95 : : }
96 : :
97 : : }
98 : :
99 : : #define CHAR_CONVERT(name, type, ...) \
100 : : int pl_str_print_##name(char *buf, size_t len, type n) \
101 : : { \
102 : : return to_chars(buf, len, n __VA_OPT__(,) __VA_ARGS__); \
103 : : } \
104 : : bool pl_str_parse_##name(pl_str str, type *n) \
105 : : { \
106 : : return from_chars(str, *n __VA_OPT__(,) __VA_ARGS__); \
107 : : }
108 : :
109 : 54117 : CHAR_CONVERT(hex, unsigned short, 16)
110 : 77937 : CHAR_CONVERT(int, int)
111 : 1940 : CHAR_CONVERT(uint, unsigned int)
112 : 4 : CHAR_CONVERT(int64, int64_t)
113 : 1292 : CHAR_CONVERT(uint64, uint64_t)
114 : 300 : CHAR_CONVERT(float, float)
115 : 3486 : CHAR_CONVERT(double, double)
116 : :
117 : : /* *****************************************************************************
118 : : *
119 : : * Copyright (c) 2007-2016 Alexis Naveros.
120 : : * Modified for use with libplacebo by Niklas Haas
121 : : * Changes include:
122 : : * - Removed a CC_MIN macro dependency by equivalent logic
123 : : * - Removed CC_ALWAYSINLINE
124 : : * - Fixed (!seq) check to (!seqlength)
125 : : * - Added support for scientific notation (e.g. 1.0e10) in ccSeqParseDouble
126 : : *
127 : : * Permission is granted to anyone to use this software for any purpose,
128 : : * including commercial applications, and to alter it and redistribute it
129 : : * freely, subject to the following restrictions:
130 : : *
131 : : * 1. The origin of this software must not be misrepresented; you must not
132 : : * claim that you wrote the original software. If you use this software
133 : : * in a product, an acknowledgment in the product documentation would be
134 : : * appreciated but is not required.
135 : : * 2. Altered source versions must be plainly marked as such, and must not be
136 : : * misrepresented as being the original software.
137 : : * 3. This notice may not be removed or altered from any source distribution.
138 : : *
139 : : * -----------------------------------------------------------------------------
140 : : */
141 : :
142 : : static int ccStrPrintDouble( char *str, int bufsize, int decimals, double value )
143 : : {
144 : : int size, offset, index;
145 : : int32_t frac, accumsub;
146 : : double muldec;
147 : : uint32_t u32;
148 : : uint64_t u64;
149 : :
150 : : size = 0;
151 : : if( value < 0.0 )
152 : : {
153 : : size = 1;
154 : : *str++ = '-';
155 : : bufsize--;
156 : : value = -value;
157 : : }
158 : :
159 : : if( value < 4294967296.0 )
160 : : {
161 : : u32 = (uint32_t)value;
162 : : offset = pl_str_print_uint( str, bufsize, u32 );
163 : : if (!offset)
164 : : goto error;
165 : : size += offset;
166 : : bufsize -= size;
167 : : value -= (double)u32;
168 : : }
169 : : else if( value < 18446744073709551616.0 )
170 : : {
171 : : u64 = (uint64_t)value;
172 : : offset = pl_str_print_uint64( str, bufsize, u64 );
173 : : if (!offset)
174 : : goto error;
175 : : size += offset;
176 : : bufsize -= size;
177 : : value -= (double)u64;
178 : : }
179 : : else
180 : : goto error;
181 : :
182 : : if (decimals > bufsize - 2)
183 : : decimals = bufsize - 2;
184 : : if( decimals <= 0 )
185 : : return size;
186 : :
187 : : muldec = 10.0;
188 : : accumsub = 0;
189 : : str += offset;
190 : :
191 : : for( index = 0 ; index < decimals ; index++ )
192 : : {
193 : : // Skip printing insignificant decimal digits
194 : : if (value * muldec - accumsub <= std::numeric_limits<double>::epsilon())
195 : : break;
196 : : if (index == 0) {
197 : : size += 1;
198 : : *str++ = '.';
199 : : }
200 : : frac = (int32_t)( value * muldec ) - accumsub;
201 : : frac = PL_CLAMP(frac, 0, 9); // FIXME: why is this needed?
202 : : str[index] = '0' + (char)frac;
203 : : accumsub += frac;
204 : : accumsub = ( accumsub << 3 ) + ( accumsub << 1 );
205 : : if( muldec < 10000000 )
206 : : muldec *= 10.0;
207 : : else
208 : : {
209 : : value *= 10000000.0;
210 : : value -= (int32_t)value;
211 : : muldec = 10.0;
212 : : accumsub = 0;
213 : : }
214 : : }
215 : : // Round up the last decimal digit
216 : : if ( str[ index - 1 ] < '9' && (int32_t)( value * muldec ) - accumsub >= 5 )
217 : : str[ index - 1 ]++;
218 : : str[ index ] = 0;
219 : : size += index;
220 : : return size;
221 : :
222 : : error:
223 : : if( bufsize < 4 )
224 : : *str = 0;
225 : : else
226 : : {
227 : : str[0] = 'E';
228 : : str[1] = 'R';
229 : : str[2] = 'R';
230 : : str[3] = 0;
231 : : }
232 : : return 0;
233 : : }
|