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