diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index ca2d630..e840d08 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -289,12 +289,16 @@ parse_number_string(UC const *p, UC const *pend, parsed_number_string_t answer; answer.valid = false; answer.too_many_digits = false; + // assume p < pend, so dereference without checks; answer.negative = (*p == UC('-')); #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default if ((*p == UC('-')) || (!uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) { #else - if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + if ((*p == UC('-')) || + (uint64_t(fmt & chars_format::allow_leading_plus) && + !uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) { #endif ++p; if (p == pend) { @@ -473,7 +477,11 @@ parse_number_string(UC const *p, UC const *pend, template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t -parse_int_string(UC const *p, UC const *pend, T &value, int base) { +parse_int_string(UC const *p, UC const *pend, T &value, + parse_options_t options) { + chars_format const fmt = options.format; + int const base = options.base; + from_chars_result_t answer; UC const *const first = p; @@ -487,7 +495,8 @@ parse_int_string(UC const *p, UC const *pend, T &value, int base) { #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default if ((*p == UC('-')) || (*p == UC('+'))) { #else - if (*p == UC('-')) { + if ((*p == UC('-')) || + (uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { #endif ++p; } diff --git a/include/fast_float/fast_float.h b/include/fast_float/fast_float.h index 42a3cdf..a812682 100644 --- a/include/fast_float/fast_float.h +++ b/include/fast_float/fast_float.h @@ -38,11 +38,13 @@ from_chars(UC const *first, UC const *last, T &value, /** * Like from_chars, but accepts an `options` argument to govern number parsing. + * Both for floating-point types and integer types. */ template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept; + /** * from_chars for integer types. */ diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index fbbc8ce..df77a3e 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -34,6 +34,8 @@ enum class chars_format : uint64_t { json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, general = fixed | scientific, + allow_leading_plus = 1 << 7, + skip_white_space = 1 << 8, }; template struct from_chars_result_t { @@ -44,13 +46,15 @@ using from_chars_result = from_chars_result_t; template struct parse_options_t { constexpr explicit parse_options_t(chars_format fmt = chars_format::general, - UC dot = UC('.')) - : format(fmt), decimal_point(dot) {} + UC dot = UC('.'), int b = 10) + : format(fmt), decimal_point(dot), base(b) {} /** Which number formats are accepted */ chars_format format; /** The character used as decimal point */ UC decimal_point; + /** The base used for integers */ + int base; }; using parse_options = parse_options_t; @@ -674,7 +678,6 @@ to_float(bool negative, adjusted_mantissa am, T &value) { #endif } -#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default template struct space_lut { static constexpr bool value[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -697,7 +700,6 @@ template constexpr bool space_lut::value[]; #endif inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; } -#endif template static constexpr uint64_t int_cmp_zeros() { static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 49fb3ab..872d23d 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -19,9 +19,9 @@ namespace detail { * strings a null-free and fixed. **/ template -from_chars_result_t FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, - UC const *last, - T &value) noexcept { +from_chars_result_t + FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, + T &value, chars_format fmt) noexcept { from_chars_result_t answer{}; answer.ptr = first; answer.ec = std::errc(); // be optimistic @@ -31,7 +31,9 @@ from_chars_result_t FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, if ((*first == UC('-')) || (*first == UC('+'))) { #else // C++17 20.19.3.(7.1) explicitly forbids '+' sign here - if (*first == UC('-')) { + if ((*first == UC('-')) || + (uint64_t(fmt & chars_format::allow_leading_plus) && + (*first == UC('+')))) { #endif ++first; } @@ -284,8 +286,8 @@ from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { template FASTFLOAT_CONSTEXPR20 from_chars_result_t -from_chars_advanced(UC const *first, UC const *last, T &value, - parse_options_t options) noexcept { +from_chars_float_advanced(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { static_assert(is_supported_float_type(), "only some floating-point types are supported"); @@ -295,9 +297,13 @@ from_chars_advanced(UC const *first, UC const *last, T &value, chars_format const fmt = options.format; from_chars_result_t answer; -#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default - while ((first != last) && fast_float::is_space(uint8_t(*first))) { - first++; +#ifndef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default + if (uint64_t(fmt & chars_format::skip_white_space)) { +#endif + while ((first != last) && fast_float::is_space(uint8_t(*first))) { + first++; + } +#ifndef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default } #endif if (first == last) { @@ -313,7 +319,7 @@ from_chars_advanced(UC const *first, UC const *last, T &value, answer.ptr = first; return answer; } else { - return detail::parse_infnan(first, last, value); + return detail::parse_infnan(first, last, value, fmt); } } @@ -324,13 +330,36 @@ from_chars_advanced(UC const *first, UC const *last, T &value, template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, int base) noexcept { + + static_assert(std::is_integral::value, "only integer types are supported"); static_assert(is_supported_char_type(), "only char, wchar_t, char16_t and char32_t are supported"); + parse_options_t options; + options.base = base; + return from_chars_advanced(first, last, value, options); +} + +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_int_advanced(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + + static_assert(std::is_integral::value, "only integer types are supported"); + static_assert(is_supported_char_type(), + "only char, wchar_t, char16_t and char32_t are supported"); + + chars_format const fmt = options.format; + int const base = options.base; + from_chars_result_t answer; -#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default - while ((first != last) && fast_float::is_space(uint8_t(*first))) { - first++; +#ifndef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default + if (uint64_t(fmt & chars_format::skip_white_space)) { +#endif + while ((first != last) && fast_float::is_space(uint8_t(*first))) { + first++; + } +#ifndef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default } #endif if (first == last || base < 2 || base > 36) { @@ -338,7 +367,34 @@ from_chars(UC const *first, UC const *last, T &value, int base) noexcept { answer.ptr = first; return answer; } - return parse_int_string(first, last, value, base); + + return parse_int_string(first, last, value, options); +} + +template struct from_chars_advanced_caller { + template + FASTFLOAT_CONSTEXPR20 static from_chars_result_t + call(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + return from_chars_float_advanced(first, last, value, options); + } +}; + +template <> struct from_chars_advanced_caller { + template + FASTFLOAT_CONSTEXPR20 static from_chars_result_t + call(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + return from_chars_int_advanced(first, last, value, options); + } +}; + +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_advanced(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + return from_chars_advanced_caller()>::call( + first, last, value, options); } } // namespace fast_float