DAW JSON Link
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"
19 #include "daw_json_type_options.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 
31 namespace 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;
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<(
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 =
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 = [&] {
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:
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<(
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.
std::bool_constant< is_zero_terminated_string_v< T > > is_zero_terminated_string
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:25