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