Revision control
++ /*
++ classify_scalar, version 1.1.0
++
++ MIT License
++
++ Copyright (c) 2026 Vincent La
++
++ Permission is hereby granted, free of charge, to any person obtaining a copy
++ of this software and associated documentation files (the "Software"), to deal
++ in the Software without restriction, including without limitation the rights
++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ copies of the Software, and to permit persons to whom the Software is
++ furnished to do so, subject to the following conditions:
++
++ The above copyright notice and this permission notice shall be included in all
++ copies or substantial portions of the Software.
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ SOFTWARE.
++ */
++
++ #pragma once
++
++ #if defined(CLASSIFY_SCALAR_VERSION)
++ #if CLASSIFY_SCALAR_VERSION >= 10100
++ #define CLASSIFY_SCALAR_SKIP_HEADER
++ #else
++ #error "A newer classify_scalar.hpp was included after an older copy. Include the newest copy first."
++ #endif
++ #else
++ #define CLASSIFY_SCALAR_VERSION_MAJOR 1
++ #define CLASSIFY_SCALAR_VERSION_MINOR 1
++ #define CLASSIFY_SCALAR_VERSION_PATCH 0
++ #define CLASSIFY_SCALAR_VERSION 10100
++ #endif
++
++ #ifndef CLASSIFY_SCALAR_SKIP_HEADER
++
++ #if defined(_MSC_VER)
++ #pragma warning(push)
++ #pragma warning(disable : 4127)
++ #endif
++
++ #include <array>
++ #include <cassert>
++ #include <cmath>
++ #include <cstddef>
++ #include <cstdint>
++ #include <cstring>
++ #include <limits>
++ #include <tuple>
++ #include <type_traits>
++
++ #if defined(_MSVC_LANG)
++ #define CLASSIFY_SCALAR_CPLUSPLUS _MSVC_LANG
++ #else
++ #define CLASSIFY_SCALAR_CPLUSPLUS __cplusplus
++ #endif
++
++ #if CLASSIFY_SCALAR_CPLUSPLUS >= 202002L
++ #define CLASSIFY_SCALAR_HAS_CXX20
++ #endif
++
++ #if CLASSIFY_SCALAR_CPLUSPLUS >= 201703L
++ #define CLASSIFY_SCALAR_HAS_CXX17
++ #endif
++
++ #if CLASSIFY_SCALAR_CPLUSPLUS >= 201402L
++ #define CLASSIFY_SCALAR_HAS_CXX14
++ #endif
++
++ #if defined(_WIN32) || defined(__LITTLE_ENDIAN__) \
++ || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
++ #define CLASSIFY_SCALAR_TRUE_U32 0x65757274U
++ #define CLASSIFY_SCALAR_FALSE_PREFIX_U32 0x736c6166U
++ #else
++ #define CLASSIFY_SCALAR_TRUE_U32 0x74727565U
++ #define CLASSIFY_SCALAR_FALSE_PREFIX_U32 0x66616c73U
++ #endif
++
++ #if defined(__clang__) || defined(__GNUC__)
++ #define CLASSIFY_SCALAR_CONST __attribute__((__const__))
++ #else
++ #define CLASSIFY_SCALAR_CONST
++ #endif
++
++ #if defined(CLASSIFY_SCALAR_DISABLE_FORCE_INLINE)
++ #define CLASSIFY_SCALAR_FORCE_INLINE inline
++ #elif defined(_MSC_VER)
++ #define CLASSIFY_SCALAR_FORCE_INLINE __forceinline
++ #elif defined(__clang__) || defined(__GNUC__)
++ #define CLASSIFY_SCALAR_FORCE_INLINE inline __attribute__((__always_inline__))
++ #else
++ #define CLASSIFY_SCALAR_FORCE_INLINE inline
++ #endif
++
++ #ifdef CLASSIFY_SCALAR_HAS_CXX14
++ #define CLASSIFY_SCALAR_CONSTEXPR_14 constexpr
++ #define CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 constexpr
++ #define CLASSIFY_SCALAR_LOCAL_TABLE_VALUE_14 constexpr
++ #else
++ #define CLASSIFY_SCALAR_CONSTEXPR_14 inline
++ #define CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 const
++ // C++11 cannot constexpr-build the lookup tables below, so local statics must
++ // stay mutable while their runtime initializer writes table entries.
++ #define CLASSIFY_SCALAR_LOCAL_TABLE_VALUE_14
++ #endif
++
++ #ifdef CLASSIFY_SCALAR_HAS_CXX17
++ #define IF_CONSTEXPR if constexpr
++ #define CLASSIFY_SCALAR_CONSTEXPR_17 constexpr
++ #define CLASSIFY_SCALAR_CONSTEXPR_VALUE_17 constexpr
++ #else
++ #define IF_CONSTEXPR if
++ #define CLASSIFY_SCALAR_CONSTEXPR_17 inline
++ #define CLASSIFY_SCALAR_CONSTEXPR_VALUE_17 const
++ #endif
++
++ #ifdef CLASSIFY_SCALAR_HAS_CXX20
++ #include <concepts>
++ #endif
++
++ #if CLASSIFY_SCALAR_CPLUSPLUS >= 201703L
++ #include <charconv>
++ #include <string_view>
++ #include <system_error>
++ #endif
++
++ #if defined(CLASSIFY_SCALAR_HAS_CXX17) && !defined(_LIBCPP_VERSION) && !defined(CLASSIFY_SCALAR_DISABLE_STD_FLOAT_FROM_CHARS)
++ #define CLASSIFY_SCALAR_HAS_STD_FLOAT_FROM_CHARS
++ #endif
++
++ namespace classify_scalar {
++
++ /// Built-in scalar classification ids returned by classify_scalar().
++ enum ScalarKind : int {
++ /// Empty input after optional ASCII trimming.
++ scalar_null = 0,
++ /// Input did not match any enabled scalar policy.
++ scalar_string = 1,
++ /// Case-insensitive "true" or "false".
++ scalar_bool = 2,
++ /// Signed 8-bit integer, including 0x-prefixed hexadecimal.
++ scalar_int8 = 3,
++ /// Reserved for future unsigned 8-bit integer classification.
++ scalar_uint8 = 4,
++ /// Signed 16-bit integer, including 0x-prefixed hexadecimal.
++ scalar_int16 = 5,
++ /// Reserved for future unsigned 16-bit integer classification.
++ scalar_uint16 = 6,
++ /// Signed 32-bit integer, including 0x-prefixed hexadecimal.
++ scalar_int32 = 7,
++ /// Reserved for future unsigned 32-bit integer classification.
++ scalar_uint32 = 8,
++ /// Signed 64-bit integer, including 0x-prefixed hexadecimal.
++ scalar_int64 = 9,
++ /// Reserved for future unsigned 64-bit integer classification.
++ scalar_uint64 = 10,
++ /// Well-formed decimal integer outside the int64 range.
++ scalar_bigint = 11,
++ /// Floating-point literal parsed as finite double.
++ scalar_float = 12,
++ /// Well-formed floating-point literal outside the built-in finite double conversion envelope.
++ scalar_bigfloat = 13,
++ /// Conservative ISO date/date-time value, stored as UTC unix milliseconds when parsed.
++ scalar_timestamp = 14,
++ /// Reserved sentinel for invalid integration results.
++ scalar_invalid = -2,
++ /// First id available for user-defined scalar kinds.
++ scalar_custom_begin = 1024
++ };
++
++ /// Include these entries at the top of a custom scalar enum.
++ #define CLASSIFY_SCALAR_BUILTINS \
++ scalar_null = ::classify_scalar::scalar_null, \
++ scalar_string = ::classify_scalar::scalar_string, \
++ scalar_bool = ::classify_scalar::scalar_bool, \
++ scalar_int8 = ::classify_scalar::scalar_int8, \
++ scalar_uint8 = ::classify_scalar::scalar_uint8, \
++ scalar_int16 = ::classify_scalar::scalar_int16, \
++ scalar_uint16 = ::classify_scalar::scalar_uint16, \
++ scalar_int32 = ::classify_scalar::scalar_int32, \
++ scalar_uint32 = ::classify_scalar::scalar_uint32, \
++ scalar_int64 = ::classify_scalar::scalar_int64, \
++ scalar_uint64 = ::classify_scalar::scalar_uint64, \
++ scalar_bigint = ::classify_scalar::scalar_bigint, \
++ scalar_float = ::classify_scalar::scalar_float, \
++ scalar_bigfloat = ::classify_scalar::scalar_bigfloat, \
++ scalar_timestamp = ::classify_scalar::scalar_timestamp, \
++ scalar_invalid = ::classify_scalar::scalar_invalid, \
++ scalar_custom_begin = ::classify_scalar::scalar_custom_begin - 1
++
++ /// Non-owning half-open character span [first, last).
++ struct scalar_span {
++ scalar_span() noexcept : first(nullptr), last(nullptr) {}
++ scalar_span(const char* first_, const char* last_) noexcept : first(first_), last(last_) {}
++
++ const char* first;
++ const char* last;
++ };
++
++ /// Output object used when only the scalar kind is needed.
++ struct classify_only_output {
++ template<ScalarKind, typename T>
++ void set(T) const noexcept {}
++ };
++
++ namespace detail {
++ namespace integer {
++
++ /// Mapping of integer kinds to their rank for easy comparison.
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 std::array<unsigned, 9> kind_rank_table = {{
++ 1U, // scalar_int8
++ 1U, // scalar_uint8
++ 2U, // scalar_int16
++ 2U, // scalar_uint16
++ 3U, // scalar_int32
++ 3U, // scalar_uint32
++ 4U, // scalar_int64
++ 4U, // scalar_uint64
++ 5U // scalar_bigint
++ }};
++
++ /// True when kind is one of the built-in signed integer widths.
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 bool is_signed_integer_kind(ScalarKind kind) noexcept {
++ return kind == scalar_int8 || kind == scalar_int16 || kind == scalar_int32 || kind == scalar_int64;
++ }
++
++ /// True when kind is one of the reserved unsigned integer widths.
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 bool is_unsigned_integer_kind(ScalarKind kind) noexcept {
++ return kind == scalar_uint8 || kind == scalar_uint16 || kind == scalar_uint32 || kind == scalar_uint64;
++ }
++
++ /// True when kind is any built-in integer or bigint classification.
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 bool is_integer_kind(ScalarKind kind) noexcept {
++ return is_signed_integer_kind(kind) || is_unsigned_integer_kind(kind) || kind == scalar_bigint;
++ }
++
++ /// Rank integer widths from smallest to largest; non-integers return 0.
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 unsigned integer_kind_rank(ScalarKind kind) noexcept {
++ constexpr auto offset = static_cast<unsigned>(scalar_int8);
++ return kind >= scalar_int8 && kind <= scalar_bigint ?
++ kind_rank_table[static_cast<unsigned>(kind) - offset] : 0U;
++ }
++
++ /// True when parsed_integer_kind can be stored by target_integer_kind.
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 bool integer_kind_fits_in(
++ ScalarKind parsed_integer_kind,
++ ScalarKind target_integer_kind) noexcept {
++ return is_signed_integer_kind(parsed_integer_kind)
++ && is_signed_integer_kind(target_integer_kind)
++ && integer_kind_rank(parsed_integer_kind) <= integer_kind_rank(target_integer_kind);
++ }
++
++ /// Return the narrowest signed integer scalar kind that can store value.
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 ScalarKind classify_integer_kind(std::int64_t value) noexcept {
++ if (value >= 0) {
++ return value <= static_cast<std::int64_t>(std::numeric_limits<std::int8_t>::max())
++ ? scalar_int8
++ : value <= static_cast<std::int64_t>(std::numeric_limits<std::int16_t>::max())
++ ? scalar_int16
++ : value <= static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max())
++ ? scalar_int32
++ : scalar_int64;
++ }
++
++ return value >= static_cast<std::int64_t>(std::numeric_limits<std::int8_t>::min())
++ ? scalar_int8
++ : value >= static_cast<std::int64_t>(std::numeric_limits<std::int16_t>::min())
++ ? scalar_int16
++ : value >= static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::min())
++ ? scalar_int32
++ : scalar_int64;
++ }
++
++ } // namespace integer
++ } // namespace detail
++
++ /// Built-in output adapter for numeric, bool, and timestamp storage.
++ struct builtin_output_refs {
++ builtin_output_refs(long double& number_, std::int64_t& integer_, bool& boolean_) noexcept
++ : number(number_), integer(integer_), boolean(boolean_), timestamp(nullptr) {}
++
++ builtin_output_refs(
++ long double& number_,
++ std::int64_t& integer_,
++ bool& boolean_,
++ std::uint64_t& timestamp_) noexcept
++ : number(number_),
++ integer(integer_),
++ boolean(boolean_),
++ timestamp(×tamp_) {}
++
++ template<ScalarKind Kind>
++ typename std::enable_if<Kind >= scalar_int8 && Kind <= scalar_int64 && ((Kind - scalar_int8) % 2 == 0), void>::type
++ set(std::int64_t value) const noexcept {
++ integer = value;
++ }
++
++ template<ScalarKind Kind>
++ typename std::enable_if<Kind == scalar_float, void>::type set(long double value) const noexcept {
++ number = value;
++ }
++
++ template<ScalarKind Kind>
++ typename std::enable_if<Kind == scalar_bool, void>::type set(bool value) const noexcept {
++ boolean = value;
++ }
++
++ template<ScalarKind Kind>
++ typename std::enable_if<Kind == scalar_timestamp, void>::type set(std::uint64_t value) const noexcept {
++ if (timestamp)
++ *timestamp = value;
++ }
++
++ long double& number;
++ std::int64_t& integer;
++ bool& boolean;
++ std::uint64_t* timestamp;
++ };
++
++ /// Create the standard built-in output adapter.
++ CLASSIFY_SCALAR_FORCE_INLINE builtin_output_refs output_refs(long double& number, std::int64_t& integer, bool& boolean) noexcept {
++ return builtin_output_refs(number, integer, boolean);
++ }
++
++ enum class ParseFlag : unsigned char {
++ /// Any byte with no scalar-classification meaning in the active parse table.
++ other,
++ /// ASCII digit bytes '0' through '9'.
++ digit,
++ /// Active decimal separator byte, '.' by default.
++ decimal
++ };
++
++ namespace detail {
++
++ template<typename Enum>
++ struct has_custom_scalar_begin {
++ template<typename T>
++ static char test(decltype(T::scalar_custom_begin)*);
++
++ template<typename>
++ static long test(...);
++
++ enum { value = sizeof(test<Enum>(nullptr)) == sizeof(char) };
++ };
++
++ template<typename Enum, bool HasCustomBegin>
++ struct custom_scalar_enum_guard {
++ static_assert(HasCustomBegin, "custom scalar enums should include CLASSIFY_SCALAR_BUILTINS");
++
++ static constexpr int cast_to_int(Enum value) noexcept {
++ return static_cast<int>(value);
++ }
++
++ static constexpr int cast_scalar_kind(ScalarKind value) noexcept {
++ return static_cast<int>(value);
++ }
++ };
++
++ template<typename Enum>
++ struct custom_scalar_enum_guard<Enum, true> {
++ static_assert(
++ static_cast<int>(Enum::scalar_custom_begin) == scalar_custom_begin - 1,
++ "custom scalar enum has an invalid scalar_custom_begin sentinel");
++
++ static constexpr int cast_to_int(Enum value) noexcept {
++ return static_cast<int>(value);
++ }
++
++ static constexpr int cast_scalar_kind(ScalarKind value) noexcept {
++ return static_cast<int>(value);
++ }
++ };
++
++ template<typename Kind, bool IsScalarKind>
++ struct scalar_kind_cast_impl;
++
++ template<typename Kind>
++ struct scalar_kind_cast_impl<Kind, false> {
++ static_assert(std::is_enum<Kind>::value, "custom scalar kind type must be an enum");
++
++ static constexpr Kind from_scalar_kind(ScalarKind value) noexcept {
++ return static_cast<Kind>(custom_scalar_enum_guard<
++ Kind,
++ has_custom_scalar_begin<Kind>::value>::cast_scalar_kind(value));
++ }
++
++ template<typename Enum>
++ static constexpr Kind from_enum(Enum value) noexcept {
++ return static_cast<Kind>(custom_scalar_enum_guard<
++ Enum,
++ has_custom_scalar_begin<Enum>::value>::cast_to_int(value));
++ }
++ };
++
++ template<typename Kind>
++ struct scalar_kind_cast_impl<Kind, true> {
++ static constexpr ScalarKind from_scalar_kind(ScalarKind value) noexcept {
++ return value;
++ }
++
++ template<typename Enum>
++ static constexpr ScalarKind from_enum(Enum value) noexcept {
++ return static_cast<ScalarKind>(custom_scalar_enum_guard<
++ Enum,
++ has_custom_scalar_begin<Enum>::value>::cast_to_int(value));
++ }
++ };
++
++ template<typename Kind>
++ constexpr Kind scalar_kind_cast(ScalarKind value) noexcept {
++ return scalar_kind_cast_impl<
++ Kind,
++ std::is_same<Kind, ScalarKind>::value>::from_scalar_kind(value);
++ }
++
++ template<typename Kind, typename Enum>
++ constexpr typename std::enable_if<std::is_enum<Enum>::value && !std::is_same<Enum, ScalarKind>::value, Kind>::type
++ scalar_kind_cast(Enum value) noexcept {
++ return scalar_kind_cast_impl<
++ Kind,
++ std::is_same<Kind, ScalarKind>::value>::from_enum(value);
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool is_ascii_space(const char c) noexcept {
++ static CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 bool table[256] = {
++ false, false, false, false, false, false, false, false,
++ false, true, true, true, true, true, false, false,
++ false, false, false, false, false, false, false, false,
++ false, false, false, false, false, false, false, false,
++ true
++ };
++ return table[static_cast<unsigned char>(c)];
++ }
++
++ template<char DecimalSymbol>
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 ParseFlag classify_ascii_char(const unsigned char c) noexcept {
++ return c >= '0' && c <= '9' ? ParseFlag::digit
++ : c == static_cast<unsigned char>(DecimalSymbol) ? ParseFlag::decimal
++ : ParseFlag::other;
++ }
++
++ template<std::size_t... Indexes>
++ struct index_sequence {};
++
++ template<std::size_t Count, std::size_t... Indexes>
++ struct make_index_sequence_impl : make_index_sequence_impl<Count - 1, Count - 1, Indexes...> {};
++
++ template<std::size_t... Indexes>
++ struct make_index_sequence_impl<0, Indexes...> {
++ typedef index_sequence<Indexes...> type;
++ };
++
++ template<std::size_t Count>
++ struct make_index_sequence {
++ typedef typename make_index_sequence_impl<Count>::type type;
++ };
++
++ struct parse_table_type {
++ ParseFlag values[256];
++
++ CLASSIFY_SCALAR_CONSTEXPR_14 ParseFlag operator[](unsigned char value) const noexcept {
++ return values[value];
++ }
++ };
++
++ struct dispatch_table_type {
++ unsigned char values[256];
++
++ CLASSIFY_SCALAR_CONSTEXPR_14 unsigned char operator[](unsigned char value) const noexcept {
++ return values[value];
++ }
++ };
++
++ struct parse_state {
++ enum Sign : unsigned char {
++ no_sign,
++ positive_sign,
++ negative_sign
++ };
++
++ parse_state(const char* first_, const char* last_) noexcept
++ : first(first_),
++ last(last_),
++ current(first_),
++ numeric_first(first_),
++ sign(no_sign) {}
++
++ const char* first;
++ const char* last;
++ const char* current;
++ const char* numeric_first;
++ Sign sign;
++ };
++
++ #ifdef CLASSIFY_SCALAR_HAS_CXX20
++ template<typename Policy>
++ concept scalar_policy = requires(
++ unsigned char c,
++ const Policy& policy,
++ parse_state& state,
++ classify_only_output& output) {
++ { Policy::matches_leading(c) } -> std::convertible_to<bool>;
++ policy.on_dispatch(state, output);
++ };
++ #endif
++
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 unsigned char no_dispatch_policy = 255U;
++
++ template<unsigned char Index, typename... Policies>
++ struct dispatch_index_impl;
++
++ template<unsigned char Index>
++ struct dispatch_index_impl<Index> {
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 static unsigned char value(unsigned char) noexcept {
++ return no_dispatch_policy;
++ }
++ };
++
++ template<unsigned char Index, typename First, typename... Rest>
++ struct dispatch_index_impl<Index, First, Rest...> {
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 static unsigned char value(unsigned char c) noexcept {
++ return First::matches_leading(c)
++ ? Index
++ : dispatch_index_impl<static_cast<unsigned char>(Index + 1U), Rest...>::value(c);
++ }
++ };
++
++ template<std::size_t Index, std::size_t Count>
++ struct policy_dispatch_impl {
++ template<typename Kind, typename Tuple, typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE static Kind call(
++ const unsigned char policy_index,
++ const Tuple& policies,
++ parse_state& state,
++ Output& output) noexcept {
++ typedef typename std::remove_reference<Tuple>::type tuple_type;
++ typedef typename std::tuple_element<Index, tuple_type>::type policy_type;
++
++ if (policy_index > Index)
++ return policy_dispatch_impl<Index + 1U, Count>::template call<Kind>(policy_index, policies, state, output);
++
++ if (policy_index == Index || policy_type::matches_leading(static_cast<unsigned char>(*state.first))) {
++ const Kind kind = scalar_kind_cast<Kind>(std::get<Index>(policies).on_dispatch(state, output));
++ if (kind != scalar_kind_cast<Kind>(scalar_string))
++ return kind;
++ }
++
++ return policy_dispatch_impl<Index + 1U, Count>::template call<Kind>(policy_index, policies, state, output);
++ }
++ };
++
++ template<std::size_t Count>
++ struct policy_dispatch_impl<Count, Count> {
++ template<typename Kind, typename Tuple, typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE static Kind call(
++ unsigned char,
++ const Tuple&,
++ parse_state&,
++ Output&) noexcept {
++ return scalar_kind_cast<Kind>(scalar_string);
++ }
++ };
++
++ template<typename... Policies>
++ #ifdef CLASSIFY_SCALAR_HAS_CXX20
++ requires (scalar_policy<Policies> && ...)
++ #endif
++ struct policy_pack {
++ typedef std::tuple<Policies...> tuple_type;
++
++ policy_pack() noexcept : policies() {}
++
++ explicit policy_pack(Policies... policies_) noexcept
++ : policies(policies_...) {}
++
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 static unsigned char dispatch_index(unsigned char c) noexcept {
++ return dispatch_index_impl<0U, Policies...>::value(c);
++ }
++
++ template<typename Kind = ScalarKind, typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE Kind dispatch(
++ const unsigned char policy_index,
++ parse_state& state,
++ Output& output) const noexcept {
++ return policy_dispatch_impl<0U, sizeof...(Policies)>::template call<Kind>(policy_index, policies, state, output);
++ }
++
++ tuple_type policies;
++ };
++
++ template<char DecimalSymbol, std::size_t... Indexes>
++ CLASSIFY_SCALAR_CONSTEXPR_14 parse_table_type build_parse_table(index_sequence<Indexes...>) noexcept {
++ return parse_table_type{{classify_ascii_char<DecimalSymbol>(static_cast<unsigned char>(Indexes))...}};
++ }
++
++ template<char DecimalSymbol>
++ CLASSIFY_SCALAR_FORCE_INLINE const parse_table_type& parse_table() noexcept {
++ static CLASSIFY_SCALAR_LOCAL_TABLE_VALUE_14 parse_table_type table =
++ build_parse_table<DecimalSymbol>(typename make_index_sequence<256>::type());
++ return table;
++ }
++
++ template<typename PolicyPack, std::size_t... Indexes>
++ CLASSIFY_SCALAR_CONSTEXPR_14 dispatch_table_type build_dispatch_table(index_sequence<Indexes...>) noexcept {
++ return dispatch_table_type{{PolicyPack::dispatch_index(static_cast<unsigned char>(Indexes))...}};
++ }
++
++ template<typename PolicyPack>
++ CLASSIFY_SCALAR_FORCE_INLINE const dispatch_table_type& dispatch_table() noexcept {
++ static CLASSIFY_SCALAR_LOCAL_TABLE_VALUE_14 dispatch_table_type table =
++ build_dispatch_table<PolicyPack>(typename make_index_sequence<256>::type());
++ return table;
++ }
++
++ CLASSIFY_SCALAR_CONSTEXPR_17 std::array<char, 256> create_ascii_lower_table() noexcept {
++ std::array<char, 256> table = {};
++ for (std::size_t i = 0; i < table.size(); ++i) {
++ table[i] = static_cast<char>(i);
++ }
++ for (unsigned char i = 0; i < 26; ++i) {
++ table[static_cast<unsigned char>('A' + i)] = static_cast<char>('a' + i);
++ }
++ return table;
++ }
++
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_17 std::array<char, 256> ascii_lower_chars = create_ascii_lower_table();
++
++ CLASSIFY_SCALAR_CONSTEXPR_17 std::array<bool, 256> create_ascii_digits_table() noexcept {
++ std::array<bool, 256> table = {};
++ for (unsigned char i = 0; i < 10; ++i) {
++ table[static_cast<unsigned char>('0' + i)] = true;
++ }
++ return table;
++ }
++
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_17 std::array<bool, 256> ascii_digits = create_ascii_digits_table();
++
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 unsigned char invalid_digit_value = 255U;
++
++ CLASSIFY_SCALAR_CONSTEXPR_17 std::array<unsigned char, 256> create_digit_values_table() noexcept {
++ std::array<unsigned char, 256> table = {};
++ for (std::size_t i = 0; i < table.size(); ++i) {
++ table[i] = invalid_digit_value;
++ }
++ for (unsigned char i = 0; i < 10; ++i) {
++ table[static_cast<unsigned char>('0' + i)] = i;
++ }
++ for (unsigned char i = 0; i < 26; ++i) {
++ table[static_cast<unsigned char>('A' + i)] = static_cast<unsigned char>(10U + i);
++ table[static_cast<unsigned char>('a' + i)] = static_cast<unsigned char>(10U + i);
++ }
++ return table;
++ }
++
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_17 std::array<unsigned char, 256> digit_values = create_digit_values_table();
++
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 std::int64_t int64_min_value = std::numeric_limits<std::int64_t>::min();
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 std::int64_t int64_max_value = std::numeric_limits<std::int64_t>::max();
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 std::uint64_t int64_positive_limit = static_cast<std::uint64_t>(int64_max_value);
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 std::uint64_t int64_negative_limit = int64_positive_limit + 1U;
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 std::uint64_t signed_integer_limits[3] = {
++ int64_positive_limit,
++ int64_positive_limit,
++ int64_negative_limit
++ };
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 long double int64_min_long_double = static_cast<long double>(int64_min_value);
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 long double int64_max_long_double = static_cast<long double>(int64_max_value);
++
++ namespace parsing {
++
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 parse_state::Sign parse_sign(unsigned char c) noexcept {
++ return c == static_cast<unsigned char>('+') ? parse_state::positive_sign :
++ c == static_cast<unsigned char>('-') ? parse_state::negative_sign :
++ parse_state::no_sign;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE const char* apply_leading_sign(parse_state& state) noexcept {
++ const unsigned char first_char = static_cast<unsigned char>(*state.first);
++ const parse_state::Sign sign = parse_sign(first_char);
++ if (sign != parse_state::no_sign) {
++ state.sign = sign;
++ // Keep '-' visible to floating parsers, but skip '+' because from_chars
++ // implementations commonly reject a leading plus for floating input.
++ state.numeric_first = state.first + (sign == parse_state::positive_sign ? 1 : 0);
++ return state.first + 1;
++ }
++
++ return state.first;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE std::uint32_t load_u32(const char* value) noexcept {
++ std::uint32_t word = 0;
++ std::memcpy(&word, value, sizeof(word));
++ return word;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE unsigned char decimal_digit_value(const unsigned char c) noexcept {
++ // The digit_values[] lookup is useful for generic-base parsing, but benchmarked slower in the hot decimal scanner.
++ return static_cast<unsigned char>(c - static_cast<unsigned char>('0'));
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE scalar_span trim_ascii(const char* first, const char* last) noexcept {
++ while (first != last && is_ascii_space(*first))
++ ++first;
++
++ while (first != last && is_ascii_space(*(last - 1)))
++ --last;
++
++ return scalar_span{first, last};
++ }
++
++ template<bool TrimAsciiWhitespace>
++ CLASSIFY_SCALAR_FORCE_INLINE scalar_span trim_span(const char* first, const char* last) noexcept {
++ if (!first || !last || last < first)
++ return scalar_span();
++
++ IF_CONSTEXPR(TrimAsciiWhitespace)
++ return trim_ascii(first, last);
++ else
++ return scalar_span(first, last);
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_true(const char* first, const char* last, bool* out) noexcept {
++ if (last - first != 4)
++ return false;
++
++ const std::uint32_t lowered = load_u32(first) | 0x20202020U;
++ if (lowered != CLASSIFY_SCALAR_TRUE_U32)
++ return false;
++
++ if (out)
++ *out = true;
++
++ return true;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_false(const char* first, const char* last, bool* out) noexcept {
++ if (last - first != 5)
++ return false;
++
++ const std::uint32_t lowered = load_u32(first) | 0x20202020U;
++ if (lowered != CLASSIFY_SCALAR_FALSE_PREFIX_U32 || (static_cast<unsigned char>(first[4]) | 0x20U) != 'e')
++ return false;
++
++ if (out)
++ *out = false;
++
++ return true;
++ }
++
++ } // namespace parsing
++
++ namespace parsing_timestamp {
++
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 bool is_leap_year(const int year) noexcept {
++ return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
++ }
++
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 int common_days_in_month[13] = {
++ 0,
++ 31, 28, 31, 30, 31, 30,
++ 31, 31, 30, 31, 30, 31
++ };
++
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 int days_in_month(const int year, const int month) noexcept {
++ return month == 2 && is_leap_year(year) ? 29 : common_days_in_month[month];
++ }
++
++ template<std::size_t Count>
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_digits(const char* value, int& out) noexcept {
++ int parsed = 0;
++ for (std::size_t i = 0; i < Count; ++i) {
++ const unsigned char c = static_cast<unsigned char>(value[i]);
++ if (!ascii_digits[c])
++ return false;
++
++ parsed = (parsed * 10) + (value[i] - '0');
++ }
++ out = parsed;
++ return true;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool valid_iso_date(const int year, const int month, const int day) noexcept {
++ return (month < 1 || month > 12) ? false :
++ day >= 1 && day <= days_in_month(year, month);
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE std::int64_t days_from_civil(int year, const int month, const int day) noexcept {
++ year -= month <= 2;
++ const std::int64_t era = (year >= 0 ? year : year - 399) / 400;
++ const unsigned yoe = static_cast<unsigned>(year - era * 400);
++ const unsigned doy = (153U * static_cast<unsigned>(month + (month > 2 ? -3 : 9)) + 2U) / 5U
++ + static_cast<unsigned>(day) - 1U;
++ const unsigned doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
++ return era * 146097 + static_cast<std::int64_t>(doe) - 719468;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_iso_timestamp(
++ const char* first,
++ const char* last,
++ std::uint64_t* out = nullptr) noexcept {
++ if (last - first < 10)
++ return false;
++
++ if (first[4] != '-' || first[7] != '-')
++ return false;
++
++ int year = 0;
++ int month = 0;
++ int day = 0;
++ if (!parse_digits<4>(first, year) || !parse_digits<2>(first + 5, month) || !parse_digits<2>(first + 8, day))
++ return false;
++
++ if (!valid_iso_date(year, month, day))
++ return false;
++
++ int hour = 0;
++ int minute = 0;
++ int second = 0;
++ int millisecond = 0;
++ int timezone_sign = 0;
++ int timezone_hour = 0;
++ int timezone_minute = 0;
++ const char* current = first + 10;
++ if (current != last) {
++ if (ascii_lower_chars[static_cast<unsigned char>(*current)] != 't')
++ return false;
++
++ ++current;
++ if (current + 5 > last || current[2] != ':')
++ return false;
++
++ if (!parse_digits<2>(current, hour) || !parse_digits<2>(current + 3, minute))
++ return false;
++
++ if (hour > 23 || minute > 59)
++ return false;
++
++ current += 5;
++ if (current != last && *current == ':') {
++ ++current;
++ if (current + 2 > last)
++ return false;
++
++ if (!parse_digits<2>(current, second) || second > 59)
++ return false;
++
++ current += 2;
++ if (current != last && *current == '.') {
++ ++current;
++ const char* fraction_first = current;
++ while (current != last && ascii_digits[static_cast<unsigned char>(*current)]) {
++ if (current - fraction_first < 3)
++ millisecond = millisecond * 10 + (*current - '0');
++ ++current;
++ }
++
++ if (current == fraction_first)
++ return false;
++
++ for (std::ptrdiff_t digits = current - fraction_first; digits < 3; ++digits)
++ millisecond *= 10;
++ }
++ }
++
++ if (current != last) {
++ if (ascii_lower_chars[static_cast<unsigned char>(*current)] == 'z') {
++ ++current;
++ } else {
++ if (*current != '+' && *current != '-')
++ return false;
++
++ timezone_sign = *current == '+' ? 1 : -1;
++ if (current + 6 == last && current[3] == ':') {
++ if (!parse_digits<2>(current + 1, timezone_hour) || !parse_digits<2>(current + 4, timezone_minute))
++ return false;
++ current = last;
++ } else if (current + 5 == last) {
++ if (!parse_digits<2>(current + 1, timezone_hour) || !parse_digits<2>(current + 3, timezone_minute))
++ return false;
++ current = last;
++ } else {
++ return false;
++ }
++
++ if (timezone_hour > 23 || timezone_minute > 59)
++ return false;
++ }
++ }
++
++ if (current != last)
++ return false;
++ }
++
++ if (!out)
++ return true;
++
++ const std::int64_t timezone_offset_milliseconds =
++ static_cast<std::int64_t>(timezone_sign)
++ * (static_cast<std::int64_t>(timezone_hour) * 60 + timezone_minute)
++ * 60 * 1000;
++ const std::int64_t timestamp =
++ days_from_civil(year, month, day) * 86400000
++ + static_cast<std::int64_t>(hour) * 3600000
++ + static_cast<std::int64_t>(minute) * 60000
++ + static_cast<std::int64_t>(second) * 1000
++ + millisecond
++ - timezone_offset_milliseconds;
++
++ if (timestamp < 0)
++ return false;
++
++ *out = static_cast<std::uint64_t>(timestamp);
++ return true;
++ }
++
++ } // namespace parsing_timestamp
++
++ namespace parsing {
++
++ enum integer_parse_result {
++ integer_parse_invalid,
++ integer_parse_valid,
++ integer_parse_overflow
++ };
++
++ CLASSIFY_SCALAR_FORCE_INLINE integer_parse_result finish_signed_integer(
++ const parse_state& state,
++ const std::uint64_t magnitude,
++ const std::uint64_t limit,
++ std::int64_t* out) noexcept {
++ if (magnitude > limit)
++ return integer_parse_overflow;
++
++ if (out) {
++ if (state.sign == parse_state::negative_sign) {
++ *out = magnitude == limit
++ ? int64_min_value
++ : -static_cast<std::int64_t>(magnitude);
++ } else {
++ *out = static_cast<std::int64_t>(magnitude);
++ }
++ }
++
++ return integer_parse_valid;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE integer_parse_result parse_integer_digits(
++ const parse_state& state,
++ const char* first,
++ const char* last,
++ const unsigned base,
++ std::int64_t* out) noexcept {
++ assert(first != last);
++
++ const std::uint64_t limit = signed_integer_limits[state.sign];
++ std::uint64_t acc = 0;
++ for (const char* current = first; current != last; ++current) {
++ const unsigned char digit = digit_values[static_cast<unsigned char>(*current)];
++ if (digit >= base)
++ return integer_parse_invalid;
++
++ // Precomputing cutoff/cutlim looked cleaner but was measurably slower on MSVC.
++ if (acc > (limit - digit) / base)
++ return integer_parse_overflow;
++
++ acc = (acc * base) + digit;
++ }
++
++ return finish_signed_integer(state, acc, limit, out);
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_hex_integer(
++ const parse_state& state,
++ std::int64_t* out) noexcept {
++ const char* current = state.sign == parse_state::no_sign ? state.first : state.first + 1;
++ const char* last = state.last;
++ assert(current != last);
++
++ if (current + 2 > last || current[0] != '0' || state.current != current + 1)
++ return false;
++
++ assert(current[1] == 'x' || current[1] == 'X');
++ current += 2;
++
++ assert(current != last);
++
++ return parse_integer_digits(state, current, last, 16U, out) == integer_parse_valid;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_bare_hex_integer(
++ const parse_state& state,
++ std::int64_t* out) noexcept {
++ const char* current = state.sign == parse_state::no_sign ? state.first : state.first + 1;
++ const char* last = state.last;
++ assert(current != last);
++ return parse_integer_digits(state, current, last, 16U, out) == integer_parse_valid;
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind finish_integer(
++ const std::int64_t parsed_integer,
++ Output& output) noexcept {
++ output.template set<scalar_int64>(parsed_integer);
++ return integer::classify_integer_kind(parsed_integer);
++ }
++
++ } // namespace parsing
++
++ namespace floating {
++
++ enum { powers_of_10_count = 19 };
++
++ // 10^0..10^18 are exact on common x86 extended long double and cover the
++ // precision envelope where the C++11 fallback parser tries to match legacy
++ // csv-parser data_type() / from_chars-style double results.
++ CLASSIFY_SCALAR_CONSTEXPR_VALUE_14 std::array<long double, powers_of_10_count> POWERS_OF_10 = {{
++ 1.0L,
++ 10.0L,
++ 100.0L,
++ 1000.0L,
++ 10000.0L,
++ 100000.0L,
++ 1000000.0L,
++ 10000000.0L,
++ 100000000.0L,
++ 1000000000.0L,
++ 10000000000.0L,
++ 100000000000.0L,
++ 1000000000000.0L,
++ 10000000000000.0L,
++ 100000000000000.0L,
++ 1000000000000000.0L,
++ 10000000000000000.0L,
++ 100000000000000000.0L,
++ 1000000000000000000.0L,
++ }};
++
++ CLASSIFY_SCALAR_FORCE_INLINE long double pow10_positive_integer(const int exponent) noexcept {
++ if (exponent < powers_of_10_count)
++ return POWERS_OF_10[static_cast<std::size_t>(exponent)];
++
++ long double value = POWERS_OF_10[powers_of_10_count - 1];
++ for (int i = powers_of_10_count - 1; i < exponent; ++i)
++ value *= 10.0L;
++
++ return value;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE long double pow10_integer(const int exponent) noexcept {
++ const int abs_exponent = exponent >= 0 ? exponent : -exponent;
++ const long double value = pow10_positive_integer(abs_exponent);
++ return exponent >= 0 ? value : 1.0L / value;
++ }
++
++ enum class floating_parse_status {
++ invalid,
++ parsed,
++ bigfloat
++ };
++
++ CLASSIFY_SCALAR_FORCE_INLINE floating_parse_status parse_floating_exponent(
++ const char*& current,
++ const char* last,
++ int* out) noexcept {
++ assert(current != last);
++ assert(*current == 'e' || *current == 'E');
++
++ ++current;
++ if (current == last)
++ return floating_parse_status::invalid;
++
++ bool exponent_negative = false;
++ const parse_state::Sign exponent_sign = parsing::parse_sign(static_cast<unsigned char>(*current));
++ if (exponent_sign != parse_state::no_sign) {
++ exponent_negative = exponent_sign == parse_state::negative_sign;
++ ++current;
++ if (current == last)
++ return floating_parse_status::invalid;
++ }
++
++ int exponent = 0;
++ while (current != last && ascii_digits[static_cast<unsigned char>(*current)]) {
++ if (exponent <= 500)
++ exponent = (exponent * 10) + (*current - '0');
++
++ ++current;
++ }
++
++ if (current != last)
++ return floating_parse_status::invalid;
++
++ if (exponent > 500)
++ return floating_parse_status::bigfloat;
++
++ *out = exponent_negative ? -exponent : exponent;
++ return floating_parse_status::parsed;
++ }
++
++ template<char DecimalSymbol>
++ CLASSIFY_SCALAR_FORCE_INLINE floating_parse_status parse_floating_ascii(
++ const parse_state& state,
++ double* out) noexcept {
++ const char* current = state.numeric_first;
++ const char* last = state.last;
++ assert(current != last);
++
++ if (state.sign == parse_state::negative_sign)
++ ++current;
++
++ long double integral_part = 0;
++ long double decimal_part = 0;
++ unsigned places_after_decimal = 0;
++ int exponent = 0;
++ bool has_digit = false;
++
++ // Keep this split integral/fractional accumulation shape. A simpler
++ // mantissa * pow10(exponent) implementation is mathematically equivalent,
++ // but it can round to the neighboring double for user-visible CHECK_EQ
++ // cases that legacy csv-parser data_type() handled exactly.
++
++ while (current != last && ascii_digits[static_cast<unsigned char>(*current)]) {
++ const unsigned char digit = static_cast<unsigned char>(*current - '0');
++ integral_part = (integral_part * 10.0L) + digit;
++ has_digit = true;
++ ++current;
++ }
++
++ if (current != last && static_cast<unsigned char>(*current) == static_cast<unsigned char>(DecimalSymbol)) {
++ ++current;
++ while (current != last && ascii_digits[static_cast<unsigned char>(*current)]) {
++ const unsigned char digit = static_cast<unsigned char>(*current - '0');
++ decimal_part += digit / pow10_integer(static_cast<int>(++places_after_decimal));
++ has_digit = true;
++ ++current;
++ }
++ }
++
++ if (!has_digit)
++ return floating_parse_status::invalid;
++
++ if (current != last && (*current == 'e' || *current == 'E')) {
++ const floating_parse_status exponent_status = parse_floating_exponent(current, last, &exponent);
++ if (exponent_status != floating_parse_status::parsed)
++ return exponent_status;
++ }
++
++ if (current != last)
++ return floating_parse_status::invalid;
++
++ if (exponent > 308 || exponent < -308)
++ return floating_parse_status::bigfloat;
++
++ long double parsed = (integral_part + decimal_part) * pow10_integer(exponent);
++ if (state.sign == parse_state::negative_sign)
++ parsed = -parsed;
++
++ const double as_double = static_cast<double>(parsed);
++ if (!std::isfinite(as_double))
++ return floating_parse_status::bigfloat;
++
++ if (out)
++ *out = as_double;
++
++ return floating_parse_status::parsed;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE floating_parse_status parse_floating_dot(
++ const parse_state& state,
++ double* out) noexcept {
++ const char* first = state.numeric_first;
++ const char* last = state.last;
++ const std::size_t size = static_cast<std::size_t>(last - first);
++ assert(first != last);
++ if (size > 4096)
++ return floating_parse_status::bigfloat;
++
++ #ifdef CLASSIFY_SCALAR_HAS_STD_FLOAT_FROM_CHARS
++ double parsed = 0;
++ const std::from_chars_result result = std::from_chars(first, last, parsed);
++ if (result.ptr != last)
++ return floating_parse_status::invalid;
++ if (result.ec == std::errc::result_out_of_range || !std::isfinite(parsed))
++ return floating_parse_status::bigfloat;
++ if (result.ec != std::errc())
++ return floating_parse_status::invalid;
++
++ if (out)
++ *out = parsed;
++
++ return floating_parse_status::parsed;
++ #else
++ return parse_floating_ascii<'.'>(state, out);
++ #endif
++ }
++
++ #ifdef CLASSIFY_SCALAR_HAS_STD_FLOAT_FROM_CHARS
++ template<char DecimalSymbol>
++ CLASSIFY_SCALAR_FORCE_INLINE floating_parse_status normalize_floating_point_separator(
++ const char* first,
++ const char* last,
++ char* buffer) noexcept {
++ std::size_t i = 0;
++ for (const char* current = first; current != last; ++current, ++i) {
++ const unsigned char c = static_cast<unsigned char>(*current);
++ if (is_ascii_space(static_cast<char>(c)))
++ return floating_parse_status::invalid;
++ if (c == '.')
++ return floating_parse_status::invalid;
++
++ buffer[i] = c == static_cast<unsigned char>(DecimalSymbol) ? '.' : static_cast<char>(c);
++ }
++
++ buffer[static_cast<std::size_t>(last - first)] = '\0';
++ return floating_parse_status::parsed;
++ }
++ #endif
++
++ template<char DecimalSymbol>
++ CLASSIFY_SCALAR_FORCE_INLINE floating_parse_status parse_floating_with_decimal(
++ const parse_state& state,
++ double* out) noexcept {
++ const char* first = state.numeric_first;
++ const char* last = state.last;
++ const std::size_t size = static_cast<std::size_t>(last - first);
++ assert(first != last);
++ if (size > 4096)
++ return floating_parse_status::bigfloat;
++
++ #ifdef CLASSIFY_SCALAR_HAS_STD_FLOAT_FROM_CHARS
++ char buffer[4097];
++ const floating_parse_status normalize_status =
++ normalize_floating_point_separator<DecimalSymbol>(first, last, buffer);
++ if (normalize_status != floating_parse_status::parsed)
++ return normalize_status;
++
++ double parsed = 0;
++ const std::from_chars_result result = std::from_chars(buffer, buffer + size, parsed);
++ if (result.ptr != buffer + size)
++ return floating_parse_status::invalid;
++ if (result.ec == std::errc::result_out_of_range || !std::isfinite(parsed))
++ return floating_parse_status::bigfloat;
++ if (result.ec != std::errc())
++ return floating_parse_status::invalid;
++
++ if (out)
++ *out = parsed;
++
++ return floating_parse_status::parsed;
++ #else
++ return parse_floating_ascii<DecimalSymbol>(state, out);
++ #endif
++ }
++
++ template<char DecimalSymbol>
++ struct floating_parser {
++ CLASSIFY_SCALAR_FORCE_INLINE static floating_parse_status parse(
++ const parse_state& state,
++ double* out) noexcept {
++ return parse_floating_with_decimal<DecimalSymbol>(state, out);
++ }
++ };
++
++ template<>
++ struct floating_parser<'.'> {
++ CLASSIFY_SCALAR_FORCE_INLINE static floating_parse_status parse(
++ const parse_state& state,
++ double* out) noexcept {
++ return parse_floating_dot(state, out);
++ }
++ };
++
++ template<char DecimalSymbol>
++ CLASSIFY_SCALAR_FORCE_INLINE floating_parse_status parse_floating(
++ const parse_state& state,
++ double* out) noexcept {
++ return floating_parser<DecimalSymbol>::parse(state, out);
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE bool floating_is_integral(const double value, std::int64_t* out) noexcept {
++ if (value < int64_min_long_double || value > int64_max_long_double)
++ return false;
++
++ const std::int64_t integer = static_cast<std::int64_t>(value);
++ // Exact round-trip test: we need to know whether the parsed double is
++ // precisely representable as int64, not whether two measured floats are close.
++ if (static_cast<double>(integer) != value)
++ return false;
++
++ if (out)
++ *out = integer;
++
++ return true;
++ }
++
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind finish_floating(
++ std::true_type,
++ const double parsed_float,
++ classify_only_output&) noexcept {
++ std::int64_t parsed_integer = 0;
++ if (floating_is_integral(parsed_float, &parsed_integer))
++ return integer::classify_integer_kind(parsed_integer);
++
++ return scalar_float;
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind finish_floating(
++ std::true_type,
++ const double parsed_float,
++ Output& output) noexcept {
++ std::int64_t parsed_integer = 0;
++ if (floating_is_integral(parsed_float, &parsed_integer))
++ return parsing::finish_integer(parsed_integer, output);
++
++ output.template set<scalar_float>(parsed_float);
++ return scalar_float;
++ }
++
++ template<bool IntegralFloatingAsInteger, typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind finish_floating(
++ const double parsed_float,
++ Output& output) noexcept {
++ IF_CONSTEXPR (IntegralFloatingAsInteger) {
++ return finish_floating(std::true_type(), parsed_float, output);
++ } else {
++ output.template set<scalar_float>(parsed_float);
++ return scalar_float;
++ }
++ }
++
++ } // namespace floating
++
++ template<char DecimalSymbol = '.', bool IntegralFloatingAsInteger = true>
++ struct builtin_numeric_policy {
++ static_assert(DecimalSymbol != 'e' && DecimalSymbol != 'E', "decimal symbol cannot be an exponent marker");
++ static_assert(DecimalSymbol != 'x' && DecimalSymbol != 'X', "decimal symbol cannot be a hexadecimal prefix marker");
++ static_assert(DecimalSymbol < '0' || DecimalSymbol > '9', "decimal symbol cannot be an ASCII digit");
++
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 static bool matches_leading(unsigned char c) noexcept {
++ return (c >= '0' && c <= '9') || c == static_cast<unsigned char>(DecimalSymbol) || c == '+' || c == '-';
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind on_dispatch(
++ parse_state& state,
++ Output& output) const noexcept {
++ return on_number(state, output);
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind on_decimal(
++ parse_state& state,
++ Output& output) const noexcept {
++ double parsed_float = 0;
++ const floating::floating_parse_status status = floating::parse_floating<DecimalSymbol>(state, &parsed_float);
++ if (status == floating::floating_parse_status::parsed)
++ return floating::finish_floating<IntegralFloatingAsInteger>(parsed_float, output);
++
++ return status == floating::floating_parse_status::bigfloat
++ ? scalar_bigfloat
++ : scalar_string;
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind scan_short_number(
++ parse_state& state,
++ const char* value_first,
++ Output& output) const noexcept {
++ std::uint64_t acc = 0;
++
++ for (const char* current = value_first; current != state.last; ++current) {
++ state.current = current;
++ const unsigned char c = static_cast<unsigned char>(*current);
++
++ const ParseFlag flag = parse_table<DecimalSymbol>()[c];
++ switch (flag) {
++ case ParseFlag::digit:
++ acc = (acc * 10U) + parsing::decimal_digit_value(c);
++ continue;
++ case ParseFlag::decimal:
++ return on_decimal(state, output);
++ case ParseFlag::other:
++ default:
++ if (ascii_lower_chars[c] == 'e')
++ return on_decimal(state, output);
++ return scalar_string;
++ }
++ }
++
++ state.current = state.last;
++ std::int64_t parsed_integer = state.sign == parse_state::negative_sign
++ ? -static_cast<std::int64_t>(acc)
++ : static_cast<std::int64_t>(acc);
++ return parsing::finish_integer(parsed_integer, output);
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind scan_checked_number(
++ parse_state& state,
++ const char* value_first,
++ Output& output) const noexcept {
++ std::uint64_t acc = 0;
++ const std::uint64_t limit = signed_integer_limits[state.sign];
++ bool overflow = false;
++
++ for (const char* current = value_first; current != state.last; ++current) {
++ state.current = current;
++ const unsigned char c = static_cast<unsigned char>(*current);
++
++ const ParseFlag flag = parse_table<DecimalSymbol>()[c];
++ switch (flag) {
++ case ParseFlag::digit: {
++ const unsigned char digit = parsing::decimal_digit_value(c);
++ if (!overflow) {
++ // Precomputing cutoff/cutlim, digit-count gating, and cold overflow helpers all benchmarked slower.
++ if (acc > (limit - digit) / 10U)
++ overflow = true;
++ else
++ acc = (acc * 10U) + digit;
++ }
++ continue;
++ }
++ case ParseFlag::decimal:
++ return on_decimal(state, output);
++ case ParseFlag::other:
++ default:
++ if (ascii_lower_chars[c] == 'e')
++ return on_decimal(state, output);
++ return scalar_string;
++ }
++ }
++
++ state.current = state.last;
++ if (overflow)
++ return scalar_bigint;
++
++ std::int64_t parsed_integer = 0;
++ parsing::finish_signed_integer(state, acc, limit, &parsed_integer);
++ return parsing::finish_integer(parsed_integer, output);
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind scan_number(
++ parse_state& state,
++ const char* value_first,
++ Output& output) const noexcept {
++ // Use overflow checks for numbers with 19 or more digits, which can exceed 64-bit limits.
++ // Shorter numbers are common enough that it's worth skipping the checks for them.
++ // Testing note: yes this was a significant optimization.
++ return state.last - value_first < 19
++ ? scan_short_number(state, value_first, output)
++ : scan_checked_number(state, value_first, output);
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind on_number(
++ parse_state& state,
++ Output& output) const noexcept {
++ const char* value_first = parsing::apply_leading_sign(state);
++ if (value_first == state.last)
++ return scalar_string;
++
++ const unsigned char value_first_char = static_cast<unsigned char>(*value_first);
++ if (!ascii_digits[value_first_char] && value_first_char != static_cast<unsigned char>(DecimalSymbol))
++ return scalar_string;
++
++ if (value_first_char == '0' && value_first + 1 != state.last) {
++ const unsigned char second_char = static_cast<unsigned char>(value_first[1]);
++ if (ascii_lower_chars[second_char] == 'x') {
++ if (value_first + 2 == state.last)
++ return scalar_string;
++
++ state.current = value_first + 1;
++ std::int64_t parsed_integer = 0;
++ return parsing::parse_hex_integer(state, &parsed_integer)
++ ? parsing::finish_integer(parsed_integer, output)
++ : scalar_string;
++ }
++ }
++
++ return scan_number(state, value_first, output);
++ }
++ };
++
++ struct builtin_bool_policy {
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 static bool matches_leading(unsigned char c) noexcept {
++ return c == 't' || c == 'T' || c == 'f' || c == 'F';
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind on_dispatch(
++ parse_state& state,
++ Output& output) const noexcept {
++ bool parsed = false;
++ if (!(parsing::parse_true(state.first, state.last, &parsed) || parsing::parse_false(state.first, state.last, &parsed)))
++ return scalar_string;
++
++ output.template set<scalar_bool>(parsed);
++ return scalar_bool;
++ }
++ };
++
++ struct builtin_timestamp_policy {
++ CLASSIFY_SCALAR_CONST CLASSIFY_SCALAR_CONSTEXPR_14 static bool matches_leading(unsigned char c) noexcept {
++ return c >= '0' && c <= '9';
++ }
++
++ template<typename Output>
++ CLASSIFY_SCALAR_FORCE_INLINE ScalarKind on_dispatch(
++ parse_state& state,
++ Output& output) const noexcept {
++ std::uint64_t timestamp = 0;
++ if (!parsing_timestamp::parse_iso_timestamp(state.first, state.last, ×tamp))
++ return scalar_string;
++
++ output.template set<scalar_timestamp>(timestamp);
++ return scalar_timestamp;
++ }
++ };
++
++ template<ScalarKind Kind>
++ struct scalar_home;
++
++ template<typename T>
++ struct signed_integer_scalar_home {
++ typedef T type;
++ typedef policy_pack<builtin_numeric_policy<> > policy;
++
++ static T get(long double, std::int64_t integer, std::uint64_t, bool) noexcept {
++ return static_cast<T>(integer);
++ }
++ };
++
++ template<>
++ struct scalar_home<scalar_bool> {
++ typedef bool type;
++ typedef policy_pack<builtin_bool_policy> policy;
++
++ static bool get(long double, std::int64_t, std::uint64_t, bool boolean) noexcept {
++ return boolean;
++ }
++ };
++
++ template<>
++ struct scalar_home<scalar_int8> : signed_integer_scalar_home<std::int8_t> {};
++
++ template<>
++ struct scalar_home<scalar_int16> : signed_integer_scalar_home<std::int16_t> {};
++
++ template<>
++ struct scalar_home<scalar_int32> : signed_integer_scalar_home<std::int32_t> {};
++
++ template<>
++ struct scalar_home<scalar_int64> : signed_integer_scalar_home<std::int64_t> {};
++
++ template<>
++ struct scalar_home<scalar_float> {
++ typedef double type;
++ typedef policy_pack<builtin_numeric_policy<> > policy;
++
++ static double get(long double number, std::int64_t, std::uint64_t, bool) noexcept {
++ return static_cast<double>(number);
++ }
++ };
++
++ template<>
++ struct scalar_home<scalar_timestamp> {
++ typedef std::uint64_t type;
++ typedef policy_pack<builtin_timestamp_policy> policy;
++
++ static std::uint64_t get(long double, std::int64_t, std::uint64_t timestamp, bool) noexcept {
++ return timestamp;
++ }
++ };
++
++ template<char DecimalSymbol, bool TrimAsciiWhitespace>
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_float_with_decimal(
++ const char* first,
++ const char* last,
++ double& out) noexcept {
++ const scalar_span span = parsing::trim_span<TrimAsciiWhitespace>(first, last);
++ if (span.first == span.last)
++ return false;
++
++ parse_state state(span.first, span.last);
++ const char* value_first = parsing::apply_leading_sign(state);
++ if (value_first == state.last)
++ return false;
++
++ return floating::parse_floating<DecimalSymbol>(state, &out) == floating::floating_parse_status::parsed;
++ }
++
++ } // namespace detail
++
++ typedef detail::parse_state parse_state;
++
++ /// Ordered set of scalar policies; earlier policies have higher priority.
++ template<typename... Policies>
++ using policy_pack = detail::policy_pack<Policies...>;
++
++ /// Built-in numeric recognizer for int, float, bigint, and 0x-prefixed hex.
++ template<char DecimalSymbol = '.', bool IntegralFloatingAsInteger = true>
++ using builtin_numeric_policy = detail::builtin_numeric_policy<DecimalSymbol, IntegralFloatingAsInteger>;
++
++ /// Built-in case-insensitive true/false recognizer.
++ typedef detail::builtin_bool_policy builtin_bool_policy;
++
++ /// Built-in conservative ISO date/date-time recognizer.
++ typedef detail::builtin_timestamp_policy builtin_timestamp_policy;
++
++ /// Default policy pack: numeric, timestamp, then bool.
++ typedef detail::policy_pack<
++ detail::builtin_numeric_policy<>,
++ detail::builtin_timestamp_policy,
++ detail::builtin_bool_policy> builtin_policy_pack;
++
++ /// Alias for the default built-in policy pack.
++ typedef builtin_policy_pack default_policy_pack;
++
++ /// Numeric and bool policy pack with timestamp recognition disabled.
++ typedef detail::policy_pack<
++ detail::builtin_numeric_policy<>,
++ detail::builtin_bool_policy> numeric_bool_policy_pack;
++
++ /// Numeric-only policy pack for null/string/int/float/bigint inference.
++ typedef detail::policy_pack<
++ detail::builtin_numeric_policy<> > numeric_policy_pack;
++
++ /// Classify a pointer span using the selected policy pack and output adapter.
++ template<
++ typename Kind = ScalarKind,
++ bool TrimAsciiWhitespace = true,
++ typename Output = classify_only_output,
++ typename Policy = default_policy_pack>
++ CLASSIFY_SCALAR_FORCE_INLINE Kind classify_scalar(
++ const char* first,
++ const char* last,
++ Output output = Output(),
++ Policy policy = Policy()) noexcept {
++ const scalar_span span = detail::parsing::trim_span<TrimAsciiWhitespace>(first, last);
++ if (span.first == span.last)
++ return !first || !last || last < first
++ ? detail::scalar_kind_cast<Kind>(scalar_string)
++ : detail::scalar_kind_cast<Kind>(scalar_null);
++
++ detail::parse_state state(span.first, span.last);
++ return policy.template dispatch<Kind>(
++ detail::dispatch_table<Policy>()[static_cast<unsigned char>(*span.first)],
++ state,
++ output);
++ }
++
++ /// Classify a string literal using the selected policy pack and output adapter.
++ template<
++ typename Kind = ScalarKind,
++ bool TrimAsciiWhitespace = true,
++ typename Output = classify_only_output,
++ typename Policy = default_policy_pack,
++ std::size_t N>
++ CLASSIFY_SCALAR_FORCE_INLINE Kind classify_scalar(
++ const char (&value)[N],
++ Output output = Output(),
++ Policy policy = Policy()) noexcept {
++ return classify_scalar<Kind, TrimAsciiWhitespace>(
++ value,
++ value + (N ? N - 1 : 0),
++ output,
++ policy);
++ }
++
++ #ifdef CLASSIFY_SCALAR_HAS_CXX17
++ /// Classify a string_view using the selected policy pack and output adapter.
++ template<
++ typename Kind = ScalarKind,
++ bool TrimAsciiWhitespace = true,
++ typename Output = classify_only_output,
++ typename Policy = default_policy_pack>
++ CLASSIFY_SCALAR_FORCE_INLINE Kind classify_scalar(
++ std::string_view value,
++ Output output = Output(),
++ Policy policy = Policy()) noexcept {
++ if (value.empty())
++ return detail::scalar_kind_cast<Kind>(scalar_null);
++
++ return classify_scalar<Kind, TrimAsciiWhitespace>(
++ value.data(),
++ value.data() + value.size(),
++ output,
++ policy);
++ }
++
++ #endif
++
++ /// Parse an explicit hexadecimal integer, accepting either bare hex or 0x-prefixed hex.
++ template<bool TrimAsciiWhitespace = true, typename IntegerType>
++ CLASSIFY_SCALAR_FORCE_INLINE typename std::enable_if<
++ std::is_integral<IntegerType>::value
++ && !std::is_same<IntegerType, bool>::value,
++ bool>::type parse_hex(
++ const char* first,
++ const char* last,
++ IntegerType& out) noexcept {
++ const scalar_span span = detail::parsing::trim_span<TrimAsciiWhitespace>(first, last);
++ if (span.first == span.last)
++ return false;
++
++ detail::parse_state state(span.first, span.last);
++ const char* current = detail::parsing::apply_leading_sign(state);
++ if (current == state.last)
++ return false;
++
++ std::int64_t parsed = 0;
++ if (current + 2 <= state.last && current[0] == '0' && (current[1] == 'x' || current[1] == 'X')) {
++ if (current + 2 == state.last)
++ return false;
++
++ state.current = current + 1;
++ if (!detail::parsing::parse_hex_integer(state, &parsed))
++ return false;
++ } else if (!detail::parsing::parse_bare_hex_integer(state, &parsed))
++ return false;
++
++ if (parsed < static_cast<std::int64_t>(std::numeric_limits<IntegerType>::min()))
++ return false;
++
++ if (parsed >= 0
++ && static_cast<std::uint64_t>(parsed) > static_cast<std::uint64_t>(std::numeric_limits<IntegerType>::max()))
++ return false;
++
++ out = static_cast<IntegerType>(parsed);
++ return true;
++ }
++
++ /// String-literal overload for parse_hex().
++ template<bool TrimAsciiWhitespace = true, std::size_t Size, typename IntegerType>
++ CLASSIFY_SCALAR_FORCE_INLINE typename std::enable_if<
++ std::is_integral<IntegerType>::value
++ && !std::is_same<IntegerType, bool>::value,
++ bool>::type parse_hex(
++ const char (&value)[Size],
++ IntegerType& out) noexcept {
++ return parse_hex<TrimAsciiWhitespace>(value, value + Size - 1, out);
++ }
++
++ /// Parse an explicit floating-point value with runtime '.' or ',' decimal selection.
++ template<bool TrimAsciiWhitespace = true>
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_float(
++ const char* first,
++ const char* last,
++ double& out,
++ const char decimal_symbol = '.') noexcept {
++ switch (decimal_symbol) {
++ case '.':
++ return detail::parse_float_with_decimal<'.', TrimAsciiWhitespace>(first, last, out);
++ case ',':
++ return detail::parse_float_with_decimal<',', TrimAsciiWhitespace>(first, last, out);
++ default:
++ return false;
++ }
++ }
++
++ /// Parse one built-in scalar kind directly and store its natural C++ value.
++ template<ScalarKind Kind, bool TrimAsciiWhitespace = true>
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_scalar(
++ const char* first,
++ const char* last,
++ typename detail::scalar_home<Kind>::type& out) noexcept {
++ long double number = 0;
++ std::int64_t integer = 0;
++ std::uint64_t timestamp = 0;
++ bool boolean = false;
++
++ const ScalarKind kind = classify_scalar<ScalarKind, TrimAsciiWhitespace>(
++ first,
++ last,
++ builtin_output_refs(number, integer, boolean, timestamp),
++ typename detail::scalar_home<Kind>::policy());
++ if (kind != Kind
++ && !(Kind == scalar_float && detail::integer::is_signed_integer_kind(kind))
++ && !detail::integer::integer_kind_fits_in(kind, Kind))
++ return false;
++
++ out = detail::scalar_home<Kind>::get(number, integer, timestamp, boolean);
++ return true;
++ }
++
++ /// String-literal overload for built-in scalar parse_scalar().
++ template<ScalarKind Kind, bool TrimAsciiWhitespace = true, std::size_t Size>
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_scalar(
++ const char (&value)[Size],
++ typename detail::scalar_home<Kind>::type& out) noexcept {
++ return parse_scalar<Kind, TrimAsciiWhitespace>(value, value + Size - 1, out);
++ }
++
++ /// Parse a signed integer directly into the requested C++ integer type.
++ template<typename IntegerType, bool TrimAsciiWhitespace = true>
++ CLASSIFY_SCALAR_FORCE_INLINE typename std::enable_if<
++ std::is_integral<IntegerType>::value
++ && std::is_signed<IntegerType>::value
++ && !std::is_same<IntegerType, bool>::value,
++ bool>::type parse_scalar(
++ const char* first,
++ const char* last,
++ IntegerType& out) noexcept {
++ long double number = 0;
++ std::int64_t integer = 0;
++ bool boolean = false;
++
++ const ScalarKind kind = classify_scalar<ScalarKind, TrimAsciiWhitespace>(
++ first,
++ last,
++ output_refs(number, integer, boolean),
++ numeric_policy_pack());
++ if (!detail::integer::is_signed_integer_kind(kind))
++ return false;
++
++ if (integer < static_cast<std::int64_t>(std::numeric_limits<IntegerType>::min())
++ || integer > static_cast<std::int64_t>(std::numeric_limits<IntegerType>::max()))
++ return false;
++
++ out = static_cast<IntegerType>(integer);
++ return true;
++ }
++
++ /// String-literal overload for signed integer parse_scalar<T>().
++ template<typename IntegerType, bool TrimAsciiWhitespace = true, std::size_t Size>
++ CLASSIFY_SCALAR_FORCE_INLINE typename std::enable_if<
++ std::is_integral<IntegerType>::value
++ && std::is_signed<IntegerType>::value
++ && !std::is_same<IntegerType, bool>::value,
++ bool>::type parse_scalar(
++ const char (&value)[Size],
++ IntegerType& out) noexcept {
++ return parse_scalar<IntegerType, TrimAsciiWhitespace>(value, value + Size - 1, out);
++ }
++
++ #ifdef CLASSIFY_SCALAR_HAS_CXX17
++ /// string_view overload for parse_hex().
++ template<bool TrimAsciiWhitespace = true, typename IntegerType>
++ CLASSIFY_SCALAR_FORCE_INLINE typename std::enable_if<
++ std::is_integral<IntegerType>::value
++ && !std::is_same<IntegerType, bool>::value,
++ bool>::type parse_hex(
++ std::string_view value,
++ IntegerType& out) noexcept {
++ return parse_hex<TrimAsciiWhitespace>(value.data(), value.data() + value.size(), out);
++ }
++
++ /// string_view overload for parse_float().
++ template<bool TrimAsciiWhitespace = true>
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_float(
++ std::string_view value,
++ double& out,
++ const char decimal_symbol = '.') noexcept {
++ return parse_float<TrimAsciiWhitespace>(value.data(), value.data() + value.size(), out, decimal_symbol);
++ }
++
++ /// string_view overload for parse_scalar().
++ template<ScalarKind Kind, bool TrimAsciiWhitespace = true>
++ CLASSIFY_SCALAR_FORCE_INLINE bool parse_scalar(
++ std::string_view value,
++ typename detail::scalar_home<Kind>::type& out) noexcept {
++ return parse_scalar<Kind, TrimAsciiWhitespace>(value.data(), value.data() + value.size(), out);
++ }
++
++ /// string_view overload for signed integer parse_scalar<T>().
++ template<typename IntegerType, bool TrimAsciiWhitespace = true>
++ CLASSIFY_SCALAR_FORCE_INLINE typename std::enable_if<
++ std::is_integral<IntegerType>::value
++ && std::is_signed<IntegerType>::value
++ && !std::is_same<IntegerType, bool>::value,
++ bool>::type parse_scalar(
++ std::string_view value,
++ IntegerType& out) noexcept {
++ return parse_scalar<IntegerType, TrimAsciiWhitespace>(value.data(), value.data() + value.size(), out);
++ }
++ #endif
++
++ } // namespace classify_scalar
++
++ #if defined(_MSC_VER)
++ #pragma warning(pop)
++ #endif
++
++ #endif // CLASSIFY_SCALAR_SKIP_HEADER