DAW JSON Link
Loading...
Searching...
No Matches
daw_json_parse_real.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
11#include "version.h"
12
13#include "daw_fp_fallback.h"
14#include "daw_json_assert.h"
18#include "daw_json_skip.h"
20
21#include <daw/daw_cxmath.h>
22#include <daw/daw_likely.h>
23#include <daw/daw_restrict.h>
24#include <daw/daw_utility.h>
25
26#include <cstddef>
27#include <cstdint>
28#include <limits>
29#include <type_traits>
30
31namespace daw::json {
32 inline namespace DAW_JSON_VER {
33 namespace json_details {
34 template<bool skip_end_check, typename Unsigned>
35 DAW_ATTRIB_NONNULL( )
36 DAW_ATTRIB_FLATINLINE inline constexpr void parse_digits_until_last(
37 char const *DAW_RESTRICT first, char const *const DAW_RESTRICT last,
38 Unsigned &DAW_RESTRICT v ) {
39 Unsigned value = v;
40 if constexpr( skip_end_check ) {
41 auto dig = parse_digit( *first );
42 while( dig < 10U ) {
43 value *= 10U;
44 value += dig;
45 ++first;
46 dig = parse_digit( *first );
47 }
48 } else {
49 while( DAW_LIKELY( first < last ) ) {
50 value *= 10U;
51 value += parse_digit( *first );
52 ++first;
53 }
54 }
55 v = value;
56 }
57
58 template<bool skip_end_check, typename Unsigned, typename CharT>
59 [[nodiscard]] DAW_ATTRIB_NONNULL( ) DAW_ATTRIB_FLATINLINE
60 inline constexpr CharT *parse_digits_while_number(
61 CharT *DAW_RESTRICT first, CharT *const DAW_RESTRICT last,
62 Unsigned &DAW_RESTRICT v ) {
63
64 // silencing gcc9 unused warning. last is used inside if constexpr
65 // blocks
66 (void)last;
67
68 Unsigned value = v;
69 if constexpr( skip_end_check ) {
70 for( auto dig = parse_digit( *first ); dig < 10U;
71 ++first, dig = parse_digit( *first ) ) {
72 value *= 10U;
73 value += dig;
74 }
75 } else {
76 if( first < last ) {
77 unsigned dig = parse_digit( *first );
78 while( dig < 10U ) {
79 ++first;
80 value *= 10U;
81 value += dig;
82 if( DAW_UNLIKELY( first == last ) ) {
83 break;
84 }
85 dig = parse_digit( *first );
86 }
87 }
88 }
89 v = value;
90 return first;
91 }
92
95 template<typename ParseState, typename Result,
96 typename max_storage_digits, typename CharT>
97 DAW_ATTRIB_NONNULL( ( 1, 2 ) )
98 [[nodiscard]] inline constexpr bool should_use_strtod(
99 CharT *whole_first, CharT *whole_last, CharT *fract_first,
100 CharT *fract_last ) {
101 if constexpr( std::is_floating_point_v<Result> and
102 ParseState::precise_ieee754 ) {
103 return DAW_UNLIKELY(
104 ( ( whole_last - whole_first ) +
105 ( fract_first ? fract_last - fract_first : 0 ) ) >
106 max_storage_digits::value );
107 } else {
108 // avoid -Wunused-but-set-parameter in gcc warnings
109 (void)whole_first;
110 (void)whole_last;
111 (void)fract_first;
112 (void)fract_last;
113 return false;
114 }
115 }
116
117 template<typename Result, typename ParseState>
118 [[nodiscard]] DAW_ATTRIB_INLINE static constexpr Result
119 parse_real_known( ParseState &parse_state ) {
120 using CharT = typename ParseState::CharT;
121 // [-]WHOLE[.FRACTION][(e|E)[+|-]EXPONENT]
123 parse_state.has_more( ) and
124 parse_policy_details::is_number_start( parse_state.front( ) ),
125 ErrorReason::InvalidNumberStart, parse_state );
126
127 CharT *whole_first = parse_state.first;
128 CharT *whole_last = parse_state.class_first ? parse_state.class_first
129 : parse_state.class_last;
130 CharT *fract_first =
131 parse_state.class_first ? parse_state.class_first + 1 : nullptr;
132 CharT *fract_last = parse_state.class_last;
133 CharT *exp_first =
134 parse_state.class_last ? parse_state.class_last + 1 : nullptr;
135 CharT *const exp_last = parse_state.last;
136
137 if( parse_state.class_first == nullptr ) {
138 if( parse_state.class_last == nullptr ) {
139 whole_last = parse_state.last;
140 } else {
141 whole_last = parse_state.class_last;
142 }
143 } else if( parse_state.class_last == nullptr ) {
144 fract_last = parse_state.last;
145 }
146
147 using max_storage_digits = daw::constant<static_cast<std::ptrdiff_t>(
148 daw::numeric_limits<std::uint64_t>::digits10 )>;
149
150 bool use_strtod =
151 should_use_strtod<ParseState, Result, max_storage_digits>(
152 whole_first, whole_last, fract_first, fract_last );
153
154 Result const sign = [&] {
155 if( *whole_first == '-' ) {
156 ++whole_first;
157 return static_cast<Result>( -1.0 );
158 }
159 return static_cast<Result>( 1.0 );
160 }( );
161 using max_exponent = daw::constant<static_cast<std::ptrdiff_t>(
162 daw::numeric_limits<Result>::max_digits10 + 1 )>;
163 using unsigned_t =
164 daw::conditional_t<max_storage_digits::value >= max_exponent::value,
165 std::uint64_t, Result>;
166
167 using signed_t =
168 typename daw::conditional_t<std::is_floating_point_v<unsigned_t>,
169 daw::traits::identity<unsigned_t>,
170 std::make_signed<unsigned_t>>::type;
171 std::intmax_t whole_exponent_available = whole_last - whole_first;
172 std::intmax_t fract_exponent_available =
173 fract_first ? fract_last - fract_first : 0;
174 signed_t exponent = 0;
175
176 if( whole_exponent_available > max_exponent::value ) {
177 whole_last = whole_first + max_exponent::value;
178 whole_exponent_available -= max_exponent::value;
179 fract_exponent_available = 0;
180 fract_first = nullptr;
181 exponent = whole_exponent_available;
182 } else {
183 whole_exponent_available =
184 max_exponent::value - whole_exponent_available;
185 if constexpr( ParseState::precise_ieee754 ) {
186 use_strtod |= DAW_UNLIKELY( fract_exponent_available >
187 whole_exponent_available );
188 }
189 if( whole_exponent_available < fract_exponent_available ) {
190 fract_exponent_available = whole_exponent_available;
191 }
192 exponent = -fract_exponent_available;
193 fract_last = fract_first + fract_exponent_available;
194 }
195
196 unsigned_t significant_digits = 0;
197 parse_digits_until_last<( ParseState::is_zero_terminated_string or
198 ParseState::is_unchecked_input )>(
199 whole_first, whole_last, significant_digits );
200 if( fract_first ) {
201 parse_digits_until_last<( ParseState::is_zero_terminated_string or
202 ParseState::is_unchecked_input )>(
203 fract_first, fract_last, significant_digits );
204 }
205
206 if( exp_first and ( exp_last - exp_first ) > 0 ) {
207 signed_t const exp_sign = [&] {
208 switch( *exp_first ) {
209 case '-':
210 ++exp_first;
211 daw_json_assert_weak( exp_first < exp_last,
212 ErrorReason::InvalidNumber );
213 return -1;
214 case '+':
215 daw_json_assert_weak( exp_first < exp_last,
216 ErrorReason::InvalidNumber );
217 ++exp_first;
218 return 1;
219 default:
220 return 1;
221 }
222 }( );
223 exponent += to_signed(
224 [&] {
225 unsigned_t exp_result = 0;
226 if constexpr( ParseState::is_zero_terminated_string ) {
227 auto dig = parse_digit( *exp_first );
228 while( dig < 10U ) {
229 ++exp_first;
230 exp_result *= 10U;
231 exp_result += dig;
232 dig = parse_digit( *exp_first );
233 }
234 } else {
235 if( exp_first < exp_last ) {
236 auto dig = parse_digit( *exp_first );
237 do {
238 if( dig >= 10U ) {
239 break;
240 }
241 ++exp_first;
242 exp_result *= 10U;
243 exp_result += dig;
244 if( exp_first >= exp_last ) {
245 break;
246 }
247 dig = parse_digit( *exp_first );
248 } while( true );
249 }
250 }
251 return exp_result;
252 }( ),
253 exp_sign );
254 }
255 if constexpr( std::is_floating_point_v<Result> and
256 ParseState::precise_ieee754 ) {
257 // On std floating point types, check for conditions that cannot be
258 // precisely calculated using the normal method and use the fallback
259 // method(usually strtod/from_chars)
260 use_strtod |= exponent > 22;
261 use_strtod |= exponent < -22;
262 use_strtod |= significant_digits > 9007199254740992ULL;
263 if constexpr( std::is_same_v<Result, long double> ) {
264 return json_details::parse_with_strtod<Result>( parse_state.first,
265 parse_state.last );
266 } else {
267 if( DAW_UNLIKELY( use_strtod ) ) {
268 return json_details::parse_with_strtod<Result>(
269 parse_state.first, parse_state.last );
270 }
271 }
272 }
273 return sign * power10<Result>(
274 ParseState::exec_tag,
275 static_cast<Result>( significant_digits ), exponent );
276 }
277
278 template<typename Result, typename ParseState>
279 [[nodiscard]] DAW_ATTRIB_INLINE static constexpr Result
280 parse_real_unknown( ParseState &parse_state ) {
281 // [-]WHOLE[.FRACTION][(e|E)[+|-]EXPONENT]
282 using CharT = typename ParseState::CharT;
284 parse_state.has_more( ) and
285 parse_policy_details::is_number_start( parse_state.front( ) ),
286 ErrorReason::InvalidNumberStart, parse_state );
287
288 CharT *const orig_first = parse_state.first;
289 CharT *const orig_last = parse_state.last;
290
291 // silencing gcc9 warning as these are only used when precise ieee is in
292 // play.
293 (void)orig_first;
294 (void)orig_last;
295
296 auto const sign = static_cast<Result>(
297 parse_policy_details::validate_signed_first( parse_state ) );
298
299 using max_storage_digits = daw::constant<static_cast<std::int64_t>(
300 daw::numeric_limits<std::uint64_t>::digits10 )>;
301 using max_exponent = daw::constant<static_cast<std::int64_t>(
302 daw::numeric_limits<Result>::max_digits10 + 1 )>;
303 using unsigned_t =
304 daw::conditional_t<max_storage_digits::value >= max_exponent::value,
305 std::uint64_t, Result>;
306 using signed_t =
307 daw::conditional_t<max_storage_digits::value >= max_exponent::value,
308 std::int64_t, Result>;
309
310 CharT *first = parse_state.first;
311 CharT *const whole_last =
312 parse_state.first +
313 ( std::min )( parse_state.last - parse_state.first,
314 static_cast<std::ptrdiff_t>( max_exponent::value ) );
315
316 unsigned_t significant_digits = 0;
317 CharT *last_char =
318 parse_digits_while_number<( ParseState::is_zero_terminated_string or
319 ParseState::is_unchecked_input )>(
320 first, whole_last, significant_digits );
321 std::ptrdiff_t sig_digit_count = last_char - parse_state.first;
322 bool use_strtod =
323 std::is_floating_point_v<Result> and ParseState::precise_ieee754 and
324 DAW_UNLIKELY( sig_digit_count > max_storage_digits::value );
325 signed_t exponent_p1 = [&] {
326 if( DAW_UNLIKELY( last_char >= whole_last ) ) {
327 if constexpr( std::is_floating_point_v<Result> and
328 ParseState::precise_ieee754 ) {
329 use_strtod = true;
330 }
331 // We have sig digits we cannot parse because there isn't enough
332 // room in a std::uint64_t
333 CharT *ptr = skip_digits<( ParseState::is_zero_terminated_string or
334 ParseState::is_unchecked_input )>(
335 last_char, parse_state.last );
336 auto const diff = ptr - last_char;
337
338 last_char = ptr;
339 if( significant_digits == 0 ) {
340 return signed_t{ 0 };
341 }
342 return static_cast<signed_t>( diff );
343 }
344 return signed_t{ 0 };
345 }( );
346
347 first = last_char;
348 if( ( ParseState::is_zero_terminated_string or
349 ParseState::is_unchecked_input or
350 DAW_LIKELY( first < parse_state.last ) ) and
351 *first == '.' ) {
352 ++first;
353 if( exponent_p1 != 0 ) {
354 if( first < parse_state.last ) {
355 first = skip_digits<( ParseState::is_zero_terminated_string or
356 ParseState::is_unchecked_input )>(
357 first, parse_state.last );
358 }
359 } else {
360 CharT *fract_last =
361 first + ( std::min )( parse_state.last - first,
362 static_cast<std::ptrdiff_t>(
363 max_exponent::value -
364 ( first - parse_state.first ) ) );
365
366 last_char = parse_digits_while_number<(
367 ParseState::is_zero_terminated_string or
368 ParseState::is_unchecked_input )>( first, fract_last,
369 significant_digits );
370 sig_digit_count += last_char - first;
371 exponent_p1 -= static_cast<signed_t>( last_char - first );
372 first = last_char;
373 if( ( first >= fract_last ) & ( first < parse_state.last ) ) {
374 auto new_first =
375 skip_digits<( ParseState::is_zero_terminated_string or
376 ParseState::is_unchecked_input )>(
377 first, parse_state.last );
378 if constexpr( std::is_floating_point_v<Result> and
379 ParseState::precise_ieee754 ) {
380 use_strtod |= new_first > first;
381 }
382 first = new_first;
383 }
384 }
385 }
386
387 signed_t const exponent_p2 = [&] {
388 if( ( ParseState::is_unchecked_input or first < parse_state.last ) and
389 ( ( *first | 0x20 ) == 'e' ) ) {
390 ++first;
391 signed_t const exp_sign = [&] {
392 daw_json_assert_weak( ( ParseState::is_zero_terminated_string or
393 first < parse_state.last ),
394 ErrorReason::UnexpectedEndOfData,
395 parse_state.copy( first ) );
396 switch( *first ) {
397 case '+':
398 ++first;
399 daw_json_assert_weak( ( first < parse_state.last ) and
400 ( parse_digit( *first ) < 10U ),
401 ErrorReason::InvalidNumber );
402 return signed_t{ 1 };
403 case '-':
404 ++first;
405 daw_json_assert_weak( first < parse_state.last and
406 parse_digit( *first ) < 10U,
407 ErrorReason::InvalidNumber );
408 return signed_t{ -1 };
409 default:
410 daw_json_assert_weak( parse_policy_details::is_number( *first ),
411 ErrorReason::InvalidNumber );
412 return signed_t{ 1 };
413 }
414 }( );
415 daw_json_assert_weak( first < parse_state.last,
416 ErrorReason::UnexpectedEndOfData,
417 parse_state );
418 unsigned_t exp_tmp = 0;
419 last_char = parse_digits_while_number<(
420 ParseState::is_zero_terminated_string or
421 ParseState::is_unchecked_input )>( first, parse_state.last,
422 exp_tmp );
423 first = last_char;
424 return to_signed( exp_tmp, exp_sign );
425 }
426 return signed_t{ 0 };
427 }( );
428 signed_t const exponent = [&] {
429 if constexpr( ParseState::is_unchecked_input or
430 not std::is_floating_point_v<Result> ) {
431 return exponent_p1 + exponent_p2;
432 } else {
433 if( bool matching_signs =
434 ( exponent_p1 < 0 ) == ( exponent_p2 < 0 );
435 not matching_signs ) {
436
437 return exponent_p1 + exponent_p2;
438 }
439 auto const s = exponent_p1 < 0 ? signed_t{ -1 } : signed_t{ 1 };
440 if( s < 0 ) {
441 if( DAW_UNLIKELY( ( daw::numeric_limits<signed_t>::min( ) -
442 exponent_p1 ) > exponent_p2 ) ) {
443 // We don't have inf, but we can just saturate it to min as it
444 // will be 0 anyways for the other result
445 return daw::numeric_limits<signed_t>::min( );
446 }
447 return exponent_p1 + exponent_p2;
448 }
449 auto r = static_cast<unsigned_t>( exponent_p1 ) +
450 static_cast<unsigned_t>( exponent_p2 );
451 if( DAW_UNLIKELY(
452 r > static_cast<unsigned_t>(
453 ( daw::numeric_limits<signed_t>::max )( ) ) ) ) {
454 return ( daw::numeric_limits<signed_t>::max )( );
455 }
456 return static_cast<signed_t>( r );
457 }
458 }( );
459 parse_state.first = first;
460
461 if constexpr( std::is_floating_point_v<Result> and
462 ParseState::precise_ieee754 ) {
463 use_strtod |= DAW_UNLIKELY( exponent > 22 );
464 use_strtod |= DAW_UNLIKELY( exponent < -22 );
465 use_strtod |=
466 DAW_UNLIKELY( significant_digits > 9007199254740992ULL );
467 if( DAW_UNLIKELY( use_strtod ) ) {
468 using json_details::parse_with_strtod;
469 auto result = parse_with_strtod<Result>( orig_first, orig_last );
470 /*auto x =
471 fallback_fp<Result>( result, sign, significant_digits, exponent );
472 (void)x;*/
473 return result;
474 }
475 }
476 return sign * power10<Result>(
477 ParseState::exec_tag,
478 static_cast<Result>( significant_digits ), exponent );
479 }
480
481 template<typename Result, bool KnownRange, typename ParseState>
482 [[nodiscard]] static constexpr Result
483 parse_real( ParseState &parse_state ) {
484 if constexpr( KnownRange ) {
485 return parse_real_known<Result>( parse_state );
486 } else {
487 return parse_real_unknown<Result>( parse_state );
488 }
489 }
490 } // namespace json_details
491 } // namespace DAW_JSON_VER
492} // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Assert that Bool is true when in Checked Input mode If false pass rest of args to daw_json_error.
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20