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_json_assert.h"
12 #include "daw_json_parse_policy.h"
14 
15 #include <daw/daw_cxmath.h>
16 
17 #include <ciso646>
18 #include <cstddef>
19 #include <cstdint>
20 #include <limits>
21 #include <type_traits>
22 
23 namespace daw::json::json_details {
24  namespace {
25  inline constexpr double dpow10_tbl[] = {
26  1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
27  1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
28  1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
29  1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
30  1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
31  1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
32  1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
33  1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
34  1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89,
35  1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99,
36  1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
37  1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
38  1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
39  1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
40  1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
41  1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
42  1e160, 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169,
43  1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179,
44  1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189,
45  1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199,
46  1e200, 1e201, 1e202, 1e203, 1e204, 1e205, 1e206, 1e207, 1e208, 1e209,
47  1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216, 1e217, 1e218, 1e219,
48  1e220, 1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228, 1e229,
49  1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239,
50  1e240, 1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249,
51  1e250, 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259,
52  1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, 1e269,
53  1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, 1e278, 1e279,
54  1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288, 1e289,
55  1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299,
56  1e300, 1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308 };
57  }
58 
59  template<typename Result, typename Unsigned>
60  DAW_ATTRIBUTE_FLATTEN static inline constexpr Result
61  power10( constexpr_exec_tag const &, Result result, Unsigned p ) {
62  // We only have a double table, of which float is a subset. Long double
63  // will be calculated in terms of that
64  constexpr int max_dbl_exp = std::numeric_limits<double>::max_exponent10;
65  constexpr int max_exp =
66  std::is_same_v<Result, float>
67  ? std::min( max_dbl_exp, std::numeric_limits<float>::max_exponent10 )
68  : max_dbl_exp;
69  constexpr Result max_v = static_cast<Result>( dpow10_tbl[max_exp] );
70 
71  if( DAW_JSON_UNLIKELY( p > max_exp ) ) {
72  Result exp2 = max_v;
73  p -= max_exp;
74  while( p > max_exp ) {
75  exp2 *= max_v;
76  p -= max_exp;
77  }
78  return static_cast<Result>( result ) *
79  ( exp2 * static_cast<Result>(
80  dpow10_tbl[static_cast<std::size_t>( p )] ) );
81  } else if( DAW_JSON_UNLIKELY( p < -max_exp ) ) {
82  Result exp2 = max_v;
83  p += max_exp;
84  while( p < -max_exp ) {
85  result /= max_v;
86  p += max_exp;
87  }
88  return ( static_cast<Result>( result ) /
89  static_cast<Result>(
90  dpow10_tbl[static_cast<std::size_t>( -p )] ) ) /
91  exp2;
92  }
93  if( p < 0 ) {
94  return static_cast<Result>( result ) /
95  static_cast<Result>( dpow10_tbl[static_cast<std::size_t>( -p )] );
96  }
97  return static_cast<Result>( result ) *
98  static_cast<Result>( dpow10_tbl[static_cast<std::size_t>( p )] );
99  }
100 
101  template<typename Result, typename Unsigned>
102  DAW_ATTRIBUTE_FLATTEN static inline constexpr Result
103  power10( runtime_exec_tag const &, Result result, Unsigned p ) {
104  if constexpr( std::is_same_v<Result, double> or
105  std::is_same_v<Result, float> ) {
106  return power10( constexpr_exec_tag{ }, result,
107  static_cast<std::int32_t>( p ) );
108  } else {
109  // For long double and others fallback to the slower std::pow
110  using std::pow;
111  return result * pow( static_cast<Result>( 10.0 ), p );
112  }
113  }
114 
115  template<typename Result>
116  DAW_ATTRIBUTE_FLATTEN static inline constexpr Result
117  copy_sign( constexpr_exec_tag const &, Result to, Result from ) {
118  return daw::cxmath::copy_sign( to, from );
119  }
120 
121  template<typename Result>
122  DAW_ATTRIBUTE_FLATTEN static inline Result
123  copy_sign( runtime_exec_tag const &, Result to, Result from ) {
124  return std::copysign( to, from );
125  }
126 
127  template<typename Value>
128  DAW_ATTRIBUTE_FLATTEN static inline constexpr void
129  parse_digits( char const *first, char const *const last, Value &v ) {
130  Value value = v;
131  while( DAW_JSON_LIKELY( first < last ) ) {
132  value *= 10U;
133  value += parse_digit( *first );
134  ++first;
135  }
136  v = value;
137  }
138 
139  template<bool is_unchecked_input, typename Unsigned>
140  DAW_ATTRIBUTE_FLATTEN static inline constexpr char const *
141  parse_real_digits_while_number( char const *first, char const *const last,
142  Unsigned &v ) {
143  auto value = v;
144  unsigned dig = 0;
145  while( ( is_unchecked_input or DAW_JSON_LIKELY( first < last ) ) and
146  ( dig = parse_digit( *first ) ) < 10 ) {
147  value *= 10U;
148  value += dig;
149  ++first;
150  }
151  v = value;
152  return first;
153  }
154 
155  template<typename Result, bool KnownRange, typename Range,
156  std::enable_if_t<KnownRange, std::nullptr_t> = nullptr>
157  [[nodiscard]] DAW_ATTRIBUTE_FLATTEN static inline constexpr Result
158  parse_real( Range &rng ) {
159  // [-]WHOLE[.FRACTION][(e|E)[+|-]EXPONENT]
161  rng.has_more( ) and parse_policy_details::is_number_start( rng.front( ) ),
162  ErrorReason::InvalidNumberStart, rng );
163 
164  char const *whole_first = rng.first;
165  char const *whole_last = rng.class_first ? rng.class_first : rng.class_last;
166  char const *fract_first = rng.class_first ? rng.class_first + 1 : nullptr;
167  char const *fract_last = rng.class_last;
168  char const *exp_first = rng.class_last ? rng.class_last + 1 : nullptr;
169  char const *const exp_last = rng.last;
170 
171  if( rng.class_first == nullptr ) {
172  if( rng.class_last == nullptr ) {
173  whole_last = rng.last;
174  } else {
175  whole_last = rng.class_last;
176  }
177  } else if( rng.class_last == nullptr ) {
178  fract_last = rng.last;
179  }
180 
181  bool const sign = [&] {
182  if( *whole_first == '-' ) {
183  ++whole_first;
184  return true;
185  }
186  return false;
187  }( );
188  constexpr auto max_storage_digits = static_cast<std::ptrdiff_t>(
189  daw::numeric_limits<std::uint64_t>::digits10 );
190  constexpr auto max_exponent = static_cast<std::ptrdiff_t>(
191  daw::numeric_limits<Result>::max_digits10 + 1 );
192  using unsigned_t = std::conditional_t<max_storage_digits >= max_exponent,
193  std::uint64_t, Result>;
194 
195  std::ptrdiff_t whole_exponent_available = whole_last - whole_first;
196  std::ptrdiff_t fract_exponent_available =
197  fract_first ? fract_last - fract_first : 0;
198  std::ptrdiff_t exponent = 0;
199  if( whole_exponent_available > max_exponent ) {
200  whole_last = whole_first + max_exponent;
201  whole_exponent_available -= max_exponent;
202  fract_exponent_available = 0;
203  fract_first = nullptr;
204  exponent = whole_exponent_available;
205  } else {
206  whole_exponent_available = max_exponent - whole_exponent_available;
207  fract_exponent_available =
208  std::min( fract_exponent_available, whole_exponent_available );
209  exponent = -fract_exponent_available;
210  fract_last = fract_first + fract_exponent_available;
211  }
212 
213  unsigned_t significant_digits = 0;
214  parse_digits( whole_first, whole_last, significant_digits );
215  if( fract_first ) {
216  parse_digits( fract_first, fract_last, significant_digits );
217  }
218 
219  if( exp_first and ( exp_last - exp_first ) > 0 ) {
220  int const exp_sign = [&] {
221  switch( *exp_first ) {
222  case '-':
223  ++exp_first;
224  return -1;
225  case '+':
226  ++exp_first;
227  return 1;
228  default:
229  return 1;
230  }
231  }( );
232  exponent += exp_sign * [&] {
233  std::ptrdiff_t r = 0;
234  while( exp_first < exp_last ) {
235  r *= static_cast<std::ptrdiff_t>( 10 );
236  r += static_cast<std::ptrdiff_t>( parse_digit( *exp_first ) );
237  ++exp_first;
238  }
239  return r;
240  }( );
241  }
242  if( sign ) {
243  return -power10<Result>(
244  Range::exec_tag, static_cast<Result>( significant_digits ), exponent );
245  }
246  return power10<Result>(
247  Range::exec_tag, static_cast<Result>( significant_digits ), exponent );
248  }
249 
250  template<typename Result, bool KnownRange, typename Range,
251  std::enable_if_t<not KnownRange, std::nullptr_t> = nullptr>
252  [[nodiscard]] DAW_ATTRIBUTE_FLATTEN inline constexpr Result
253  parse_real( Range &rng ) {
254  // [-]WHOLE[.FRACTION][(e|E)[+|-]EXPONENT]
256  rng.has_more( ) and parse_policy_details::is_number_start( rng.front( ) ),
257  ErrorReason::InvalidNumberStart, rng );
258 
259  Result const sign = [&] {
260  if( rng.front( ) == '-' ) {
261  rng.remove_prefix( );
262  return static_cast<Result>( -1 );
263  }
264  return static_cast<Result>( 1 );
265  }( );
266  constexpr auto max_storage_digits = static_cast<std::ptrdiff_t>(
267  daw::numeric_limits<std::uint64_t>::digits10 );
268  constexpr auto max_exponent = static_cast<std::ptrdiff_t>(
269  daw::numeric_limits<Result>::max_digits10 + 1 );
270  using unsigned_t = std::conditional_t<max_storage_digits >= max_exponent,
271  std::uint64_t, Result>;
272  using signed_t =
273  std::conditional_t<max_storage_digits >= max_exponent, int, Result>;
274 
275  char const *first = rng.first;
276  char const *const whole_last =
277  rng.first + std::min( rng.last - rng.first, max_exponent );
278 
279  unsigned_t significant_digits = 0;
280  char const *last_char =
281  parse_real_digits_while_number<Range::is_unchecked_input>(
282  first, whole_last, significant_digits );
283 
284  signed_t exponent = [&] {
285  if( last_char >= whole_last ) {
286  // We have sig digits we cannot parse because there isn't enough room in
287  // a std::uint64_t
288  char const *ptr =
289  skip_digits<Range::is_unchecked_input>( last_char, rng.last );
290  auto const diff = ptr - last_char;
291  last_char = ptr;
292  return static_cast<signed_t>( diff );
293  }
294  return static_cast<signed_t>( 0 );
295  }( );
296  if( significant_digits == 0 ) {
297  exponent = 0;
298  }
299  first = last_char;
300  if( ( ( Range::is_unchecked_input or
301  DAW_JSON_LIKELY( first < rng.last ) ) and
302  *first == '.' ) ) {
303  ++first;
304  if( exponent != 0 ) {
305  first = skip_digits<Range::is_unchecked_input>( first, rng.last );
306  } else {
307  char const *fract_last =
308  first + std::min( rng.last - first,
309  static_cast<std::ptrdiff_t>(
310  max_exponent - ( first - rng.first ) ) );
311 
312  last_char = parse_real_digits_while_number<Range::is_unchecked_input>(
313  first, fract_last, significant_digits );
314  exponent -= static_cast<signed_t>( last_char - first );
315  first = last_char;
316  if( first >= fract_last ) {
317  first = skip_digits<Range::is_unchecked_input>( first, rng.last );
318  }
319  }
320  }
321 
322  exponent += [&] {
323  if( ( Range::is_unchecked_input or first < rng.last ) and
324  ( ( *first | 0x20 ) == 'e' ) ) {
325  ++first;
326  bool const exp_sign = [&] {
327  daw_json_assert_weak( first < rng.last,
328  ErrorReason::UnexpectedEndOfData,
329  rng.copy( first ) );
330  switch( *first ) {
331  case '+':
332  ++first;
333  return false;
334  case '-':
335  ++first;
336  return true;
337  default:
338  return false;
339  }
340  }( );
341  daw_json_assert_weak( rng.has_more( ), ErrorReason::UnexpectedEndOfData,
342  rng );
343  unsigned_t exp_tmp = 0;
344  last_char = parse_real_digits_while_number<Range::is_unchecked_input>(
345  first, rng.last, exp_tmp );
346  first = last_char;
347  if( exp_sign ) {
348  return -static_cast<signed_t>( exp_tmp );
349  }
350  return static_cast<signed_t>( exp_tmp );
351  }
352  return static_cast<signed_t>( 0 );
353  }( );
354  rng.first = first;
355  return sign * power10<Result>( Range::exec_tag,
356  static_cast<Result>( significant_digits ),
357  exponent );
358  }
359 } // namespace daw::json::json_details
daw_json_assert_weak
#define daw_json_assert_weak(Bool,...)
Definition: daw_json_assert.h:206
daw_json_parse_unsigned_int.h
DAW_JSON_UNLIKELY
#define DAW_JSON_UNLIKELY(Bool)
Definition: daw_json_assert.h:35
daw_json_parse_policy.h
daw_json_assert.h
DAW_JSON_LIKELY
#define DAW_JSON_LIKELY(Bool)
Definition: daw_json_assert.h:34