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