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 "daw_fp_fallback.h"
12 #include "daw_json_assert.h"
13 #include "daw_json_parse_policy.h"
16 #include "version.h"
17 
18 #include <daw/daw_cxmath.h>
19 #include <daw/daw_likely.h>
20 
21 #include <ciso646>
22 #include <cstddef>
23 #include <cstdint>
24 #include <limits>
25 #include <type_traits>
26 
27 namespace daw::json {
28  inline namespace DAW_JSON_VER {
29  namespace json_details {
30  template<bool skip_end_check, typename Unsigned>
31  DAW_ATTRIB_FLATINLINE inline constexpr void
32  parse_digits_until_last( char const *first, char const *const last,
33  Unsigned &v ) {
34  Unsigned value = v;
35  if constexpr( skip_end_check ) {
36  auto dig = parse_digit( *first );
37  while( dig < 10U ) {
38  value *= 10U;
39  value += dig;
40  ++first;
41  dig = parse_digit( *first );
42  }
43  } else {
44  while( DAW_LIKELY( first < last ) ) {
45  value *= 10U;
46  value += parse_digit( *first );
47  ++first;
48  }
49  }
50  v = value;
51  }
52 
53  template<bool skip_end_check, typename Unsigned, typename CharT>
54  DAW_ATTRIB_FLATINLINE inline constexpr CharT *
55  parse_digits_while_number( CharT *first, CharT *const last,
56  Unsigned &v ) {
57 
58  // silencing gcc9 unused warning. last is used inside if constexpr
59  // blocks
60  (void)last;
61 
62  Unsigned value = v;
63  if constexpr( skip_end_check ) {
64  auto dig = parse_digit( *first );
65  while( dig < 10U ) {
66  value *= 10U;
67  value += dig;
68  ++first;
69  dig = parse_digit( *first );
70  }
71  } else {
72  unsigned dig = 0;
73  while( DAW_LIKELY( first < last ) and
74  ( dig = parse_digit( *first ) ) < 10U ) {
75  value *= 10U;
76  value += dig;
77  ++first;
78  }
79  }
80  v = value;
81  return first;
82  }
83 
84  template<typename Result, bool KnownRange, typename ParseState,
85  std::enable_if_t<KnownRange, std::nullptr_t> = nullptr>
86  [[nodiscard]] DAW_ATTRIB_FLATINLINE inline constexpr Result
87  parse_real( ParseState &parse_state ) {
88  using CharT = typename ParseState::CharT;
89  // [-]WHOLE[.FRACTION][(e|E)[+|-]EXPONENT]
91  parse_state.has_more( ) and
93  ErrorReason::InvalidNumberStart, parse_state );
94 
95  CharT *whole_first = parse_state.first;
96  CharT *whole_last = parse_state.class_first ? parse_state.class_first
97  : parse_state.class_last;
98  CharT *fract_first =
99  parse_state.class_first ? parse_state.class_first + 1 : nullptr;
100  CharT *fract_last = parse_state.class_last;
101  CharT *exp_first =
102  parse_state.class_last ? parse_state.class_last + 1 : nullptr;
103  CharT *const exp_last = parse_state.last;
104 
105  if( parse_state.class_first == nullptr ) {
106  if( parse_state.class_last == nullptr ) {
107  whole_last = parse_state.last;
108  } else {
109  whole_last = parse_state.class_last;
110  }
111  } else if( parse_state.class_last == nullptr ) {
112  fract_last = parse_state.last;
113  }
114 
115  constexpr auto max_storage_digits = static_cast<std::ptrdiff_t>(
116  daw::numeric_limits<std::uint64_t>::digits10 );
117  bool use_strtod = [&] {
118  if constexpr( std::is_floating_point_v<Result> and
119  ParseState::precise_ieee754 ) {
120  return DAW_UNLIKELY( ( ( whole_last - whole_first ) +
121  ( fract_first ? fract_last - fract_first
122  : 0 ) ) > max_storage_digits );
123  } else {
124  return false;
125  }
126  }( );
127 
128  Result const sign = [&] {
129  if( *whole_first == '-' ) {
130  ++whole_first;
131  return static_cast<Result>( -1.0 );
132  }
133  return static_cast<Result>( 1.0 );
134  }( );
135  constexpr auto max_exponent = static_cast<std::ptrdiff_t>(
136  daw::numeric_limits<Result>::max_digits10 + 1 );
137  using unsigned_t =
138  std::conditional_t<max_storage_digits >= max_exponent, std::uint64_t,
139  Result>;
140 
141  std::ptrdiff_t whole_exponent_available = whole_last - whole_first;
142  std::ptrdiff_t fract_exponent_available =
143  fract_first ? fract_last - fract_first : 0;
144  std::ptrdiff_t exponent = 0;
145 
146  if( whole_exponent_available > max_exponent ) {
147  whole_last = whole_first + max_exponent;
148  whole_exponent_available -= max_exponent;
149  fract_exponent_available = 0;
150  fract_first = nullptr;
151  exponent = whole_exponent_available;
152  } else {
153  whole_exponent_available = max_exponent - whole_exponent_available;
154  if constexpr( ParseState::precise_ieee754 ) {
155  use_strtod |= DAW_UNLIKELY( fract_exponent_available >
156  whole_exponent_available );
157  }
158  fract_exponent_available =
159  std::min( fract_exponent_available, whole_exponent_available );
160  exponent = -fract_exponent_available;
161  fract_last = fract_first + fract_exponent_available;
162  }
163 
164  unsigned_t significant_digits = 0;
165  parse_digits_until_last<( ParseState::is_zero_terminated_string or
166  ParseState::is_unchecked_input )>(
167  whole_first, whole_last, significant_digits );
168  if( fract_first ) {
169  parse_digits_until_last<( ParseState::is_zero_terminated_string or
170  ParseState::is_unchecked_input )>(
171  fract_first, fract_last, significant_digits );
172  }
173 
174  if( exp_first and ( exp_last - exp_first ) > 0 ) {
175  int const exp_sign = [&] {
176  switch( *exp_first ) {
177  case '-':
178  ++exp_first;
179  daw_json_assert_weak( exp_first < exp_last,
180  ErrorReason::InvalidNumber );
181  return -1;
182  case '+':
183  daw_json_assert_weak( exp_first < exp_last,
184  ErrorReason::InvalidNumber );
185  ++exp_first;
186  return 1;
187  default:
188  return 1;
189  }
190  }( );
191  exponent += exp_sign * [&] {
192  std::ptrdiff_t r = 0;
193  // TODO use zstringopt
194  if constexpr( ParseState::is_zero_terminated_string ) {
195  auto dig = parse_digit( *exp_first );
196  while( dig < 10U ) {
197  ++exp_first;
198  r *= 10U;
199  r += dig;
200  dig = parse_digit( *exp_first );
201  }
202  } else {
203  if( exp_first < exp_last ) {
204  auto dig = parse_digit( *exp_first );
205  do {
206  if( dig >= 10U ) {
207  break;
208  }
209  ++exp_first;
210  r *= 10U;
211  r += dig;
212  if( exp_first >= exp_last ) {
213  break;
214  }
215  dig = parse_digit( *exp_first );
216  } while( true );
217  }
218  }
219  return r;
220  }( );
221  }
222  if constexpr( std::is_floating_point_v<Result> and
223  ParseState::precise_ieee754 ) {
224  use_strtod |= DAW_UNLIKELY( exponent > 22 );
225  use_strtod |= DAW_UNLIKELY( exponent < -22 );
226  use_strtod |= DAW_UNLIKELY( significant_digits > 9007199254740992 );
227  if( DAW_UNLIKELY( use_strtod ) ) {
228  return json_details::parse_with_strtod<Result>( parse_state.first,
229  parse_state.last );
230  }
231  }
232  return sign * power10<Result>(
233  ParseState::exec_tag,
234  static_cast<Result>( significant_digits ), exponent );
235  }
236 
237  template<typename Result, bool KnownRange, typename ParseState,
238  std::enable_if_t<not KnownRange, std::nullptr_t> = nullptr>
239  [[nodiscard]] DAW_ATTRIB_FLATINLINE inline constexpr Result
240  parse_real( ParseState &parse_state ) {
241  // [-]WHOLE[.FRACTION][(e|E)[+|-]EXPONENT]
242  using CharT = typename ParseState::CharT;
244  parse_state.has_more( ) and
246  ErrorReason::InvalidNumberStart, parse_state );
247 
248  CharT *const orig_first = parse_state.first;
249  CharT *const orig_last = parse_state.last;
250 
251  // silencing gcc9 warning as these are only used when precise ieee is in
252  // play.
253  (void)orig_first;
254  (void)orig_last;
255 
256  auto const sign = static_cast<Result>(
258 
259  constexpr auto max_storage_digits = static_cast<std::ptrdiff_t>(
260  daw::numeric_limits<std::uint64_t>::digits10 );
261  constexpr auto max_exponent = static_cast<std::ptrdiff_t>(
262  daw::numeric_limits<Result>::max_digits10 + 1 );
263  using unsigned_t =
264  std::conditional_t<max_storage_digits >= max_exponent, std::uint64_t,
265  Result>;
266  using signed_t =
267  std::conditional_t<max_storage_digits >= max_exponent, int, Result>;
268 
269  CharT *first = parse_state.first;
270  CharT *const whole_last =
271  parse_state.first +
272  std::min( parse_state.last - parse_state.first, max_exponent );
273 
274  unsigned_t significant_digits = 0;
275  CharT *last_char =
276  parse_digits_while_number<( ParseState::is_zero_terminated_string or
277  ParseState::is_unchecked_input )>(
278  first, whole_last, significant_digits );
279  std::ptrdiff_t sig_digit_count = last_char - parse_state.first;
280  bool use_strtod = std::is_floating_point_v<Result> and
281  ParseState::precise_ieee754 and
282  DAW_UNLIKELY( sig_digit_count > max_storage_digits );
283  signed_t exponent = [&] {
284  if( DAW_UNLIKELY( last_char >= whole_last ) ) {
285  if constexpr( std::is_floating_point_v<Result> and
286  ParseState::precise_ieee754 ) {
287  use_strtod = true;
288  }
289  // We have sig digits we cannot parse because there isn't enough
290  // room in a std::uint64_t
291  CharT *ptr = skip_digits<( ParseState::is_zero_terminated_string or
292  ParseState::is_unchecked_input )>(
293  last_char, parse_state.last );
294  auto const diff = ptr - last_char;
295 
296  last_char = ptr;
297  return static_cast<signed_t>( diff );
298  }
299  return static_cast<signed_t>( 0 );
300  }( );
301  if( significant_digits == 0 ) {
302  exponent = 0;
303  }
304  first = last_char;
305  if( ( ParseState::is_zero_terminated_string or
306  ParseState::is_unchecked_input or
307  DAW_LIKELY( first < parse_state.last ) ) and
308  *first == '.' ) {
309  ++first;
310  if( exponent != 0 ) {
311  if( first < parse_state.last ) {
312  first = skip_digits<( ParseState::is_zero_terminated_string or
313  ParseState::is_unchecked_input )>(
314  first, parse_state.last );
315  }
316  } else {
317  CharT *fract_last =
318  first +
319  std::min( parse_state.last - first,
320  static_cast<std::ptrdiff_t>(
321  max_exponent - ( first - parse_state.first ) ) );
322 
323  last_char = parse_digits_while_number<(
324  ParseState::is_zero_terminated_string or
325  ParseState::is_unchecked_input )>( first, fract_last,
326  significant_digits );
327  sig_digit_count += last_char - first;
328  exponent -= static_cast<signed_t>( last_char - first );
329  first = last_char;
330  if( ( first >= fract_last ) & ( first < parse_state.last ) ) {
331  auto new_first =
332  skip_digits<( ParseState::is_zero_terminated_string or
333  ParseState::is_unchecked_input )>(
334  first, parse_state.last );
335  if constexpr( std::is_floating_point_v<Result> and
336  ParseState::precise_ieee754 ) {
337  use_strtod |= new_first > first;
338  }
339  first = new_first;
340  }
341  }
342  }
343 
344  exponent += [&] {
345  if( ( ParseState::is_unchecked_input or first < parse_state.last ) and
346  ( ( *first | 0x20 ) == 'e' ) ) {
347  ++first;
348  bool const exp_sign = [&] {
349  daw_json_assert_weak( ( ParseState::is_zero_terminated_string or
350  first < parse_state.last ),
351  ErrorReason::UnexpectedEndOfData,
352  parse_state.copy( first ) );
353  switch( *first ) {
354  case '+':
355  ++first;
356  daw_json_assert_weak( first < parse_state.last and
357  parse_digit( *first ) < 10U,
358  ErrorReason::InvalidNumber );
359  return false;
360  case '-':
361  ++first;
362  daw_json_assert_weak( first < parse_state.last and
363  parse_digit( *first ) < 10U,
364  ErrorReason::InvalidNumber );
365  return true;
366  default:
368  ErrorReason::InvalidNumber );
369  return false;
370  }
371  }( );
372  daw_json_assert_weak( first < parse_state.last,
373  ErrorReason::UnexpectedEndOfData,
374  parse_state );
375  unsigned_t exp_tmp = 0;
376  last_char = parse_digits_while_number<(
377  ParseState::is_zero_terminated_string or
378  ParseState::is_unchecked_input )>( first, parse_state.last,
379  exp_tmp );
380  first = last_char;
381  if( exp_sign ) {
382  return -static_cast<signed_t>( exp_tmp );
383  }
384  return static_cast<signed_t>( exp_tmp );
385  }
386  return static_cast<signed_t>( 0 );
387  }( );
388  parse_state.first = first;
389 
390  if constexpr( std::is_floating_point_v<Result> and
391  ParseState::precise_ieee754 ) {
392  use_strtod |= DAW_UNLIKELY( exponent > 22 );
393  use_strtod |= DAW_UNLIKELY( exponent < -22 );
394  use_strtod |=
395  DAW_UNLIKELY( significant_digits > 9007199254740992ULL );
396  if( DAW_UNLIKELY( use_strtod ) ) {
397  return json_details::parse_with_strtod<Result>( orig_first,
398  orig_last );
399  }
400  }
401  return sign * power10<Result>(
402  ParseState::exec_tag,
403  static_cast<Result>( significant_digits ), exponent );
404  }
405  } // namespace json_details
406  } // namespace DAW_JSON_VER
407 } // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Definition: daw_json_assert.h:189
ParseState & parse_state
Definition: daw_json_parse_class.h:201
constexpr DAW_ATTRIB_FLATINLINE Result parse_real(ParseState &parse_state)
Definition: daw_json_parse_real.h:87
constexpr DAW_ATTRIB_FLATINLINE void parse_digits_until_last(char const *first, char const *const last, Unsigned &v)
Definition: daw_json_parse_real.h:32
constexpr DAW_ATTRIB_FLATINLINE CharT * skip_digits(CharT *first, CharT *const last)
Definition: daw_json_skip.h:133
constexpr DAW_ATTRIB_FLATINLINE CharT * parse_digits_while_number(CharT *first, CharT *const last, Unsigned &v)
Definition: daw_json_parse_real.h:55
static constexpr DAW_ATTRIB_FLATINLINE unsigned parse_digit(char c)
Definition: daw_json_parse_digit.h:19
constexpr DAW_ATTRIB_FLATINLINE int validate_signed_first(ParseState &parse_state)
Definition: daw_json_parse_policy_policy_details.h:73
constexpr DAW_ATTRIB_FLATINLINE bool is_number(char c)
Definition: daw_json_parse_policy_policy_details.h:34
constexpr DAW_ATTRIB_FLATINLINE bool is_number_start(char c)
Definition: daw_json_parse_policy_policy_details.h:106
Definition: daw_from_json.h:22
#define DAW_JSON_VER
Definition: version.h:11