diff options
Diffstat (limited to 'include/jsoncons/detail/parse_number.hpp')
-rw-r--r-- | include/jsoncons/detail/parse_number.hpp | 1133 |
1 files changed, 1133 insertions, 0 deletions
diff --git a/include/jsoncons/detail/parse_number.hpp b/include/jsoncons/detail/parse_number.hpp new file mode 100644 index 0000000..7417ff2 --- /dev/null +++ b/include/jsoncons/detail/parse_number.hpp @@ -0,0 +1,1133 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_PARSE_NUMBER_HPP +#define JSONCONS_DETAIL_PARSE_NUMBER_HPP + +#include <system_error> +#include <stdexcept> +#include <string> +#include <vector> +#include <locale> +#include <string> +#include <limits> // std::numeric_limits +#include <type_traits> // std::enable_if +#include <exception> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons/json_exception.hpp> +#include <cctype> + +namespace jsoncons { namespace detail { + + enum class to_integer_errc : uint8_t {success=0, overflow, invalid_digit, invalid_number}; + + class to_integer_error_category_impl + : public std::error_category + { + public: + const char* name() const noexcept override + { + return "jsoncons/to_integer_unchecked"; + } + std::string message(int ev) const override + { + switch (static_cast<to_integer_errc>(ev)) + { + case to_integer_errc::overflow: + return "Integer overflow"; + case to_integer_errc::invalid_digit: + return "Invalid digit"; + case to_integer_errc::invalid_number: + return "Invalid number"; + default: + return "Unknown to_integer_unchecked error"; + } + } + }; + + inline + const std::error_category& to_integer_error_category() + { + static to_integer_error_category_impl instance; + return instance; + } + + inline + std::error_code make_error_code(to_integer_errc e) + { + return std::error_code(static_cast<int>(e),to_integer_error_category()); + } + +} // namespace detail +} // namespace jsoncons + +namespace std { + template<> + struct is_error_code_enum<jsoncons::detail::to_integer_errc> : public true_type + { + }; +} + +namespace jsoncons { namespace detail { + +template <class T,class CharT> +struct to_integer_result +{ + const CharT* ptr; + to_integer_errc ec; + constexpr to_integer_result(const CharT* ptr_) + : ptr(ptr_), ec(to_integer_errc()) + { + } + constexpr to_integer_result(const CharT* ptr_, to_integer_errc ec_) + : ptr(ptr_), ec(ec_) + { + } + + to_integer_result(const to_integer_result&) = default; + + to_integer_result& operator=(const to_integer_result&) = default; + + constexpr explicit operator bool() const noexcept + { + return ec == to_integer_errc(); + } + std::error_code error_code() const + { + return make_error_code(ec); + } +}; + +enum class integer_chars_format : uint8_t {decimal=1,hex}; +enum class integer_chars_state {initial,minus,integer,binary,octal,decimal,base16}; + +template <class CharT> +bool is_base10(const CharT* s, std::size_t length) +{ + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + for (;s < end; ++s) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + state = integer_chars_state::decimal; + break; + case '-': + state = integer_chars_state::minus; + break; + default: + return false; + } + break; + } + case integer_chars_state::minus: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + state = integer_chars_state::decimal; + break; + default: + return false; + } + break; + } + case integer_chars_state::decimal: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + break; + default: + return false; + } + break; + } + default: + break; + } + } + return state == integer_chars_state::decimal ? true : false; +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer_decimal(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + while (s < end) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0': + if (++s == end) + { + return (++s == end) ? to_integer_result<T,CharT>(s) : to_integer_result<T, CharT>(s, to_integer_errc()); + } + else + { + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be decimal + state = integer_chars_state::decimal; + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::decimal: + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = static_cast<T>(*s) - static_cast<T>('0'); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_10) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return (state == integer_chars_state::initial) ? to_integer_result<T,CharT>(s, to_integer_errc::invalid_number) : to_integer_result<T,CharT>(s, to_integer_errc()); +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer_decimal(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + if (length == 0) + { + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_number); + } + + bool is_negative = *s == '-' ? true : false; + if (is_negative) + { + ++s; + --length; + } + + using U = typename type_traits::make_unsigned<T>::type; + + U u; + auto ru = to_integer_decimal(s, length, u); + if (ru.ec != to_integer_errc()) + { + return to_integer_result<T,CharT>(ru.ptr, ru.ec); + } + if (is_negative) + { + if (u > static_cast<U>(-((type_traits::integer_limits<T>::lowest)()+T(1))) + U(1)) + { + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast<T>(U(0) - u); + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc()); + } + } + else + { + if (u > static_cast<U>((type_traits::integer_limits<T>::max)())) + { + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast<T>(u); + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc()); + } + } +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer_base16(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + while (s < end) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0': + if (++s == end) + { + return (++s == end) ? to_integer_result<T,CharT>(s) : to_integer_result<T, CharT>(s, to_integer_errc()); + } + else + { + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be base16 + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + state = integer_chars_state::base16; + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::base16: + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return (state == integer_chars_state::initial) ? to_integer_result<T,CharT>(s, to_integer_errc::invalid_number) : to_integer_result<T,CharT>(s, to_integer_errc()); +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer_base16(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + if (length == 0) + { + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_number); + } + + bool is_negative = *s == '-' ? true : false; + if (is_negative) + { + ++s; + --length; + } + + using U = typename type_traits::make_unsigned<T>::type; + + U u; + auto ru = to_integer_base16(s, length, u); + if (ru.ec != to_integer_errc()) + { + return to_integer_result<T,CharT>(ru.ptr, ru.ec); + } + if (is_negative) + { + if (u > static_cast<U>(-((type_traits::integer_limits<T>::lowest)()+T(1))) + U(1)) + { + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast<T>(U(0) - u); + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc()); + } + } + else + { + if (u > static_cast<U>((type_traits::integer_limits<T>::max)())) + { + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast<T>(u); + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc()); + } + } +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + while (s < end) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0': + state = integer_chars_state::integer; // Could be binary, octal, hex + ++s; + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be decimal + state = integer_chars_state::decimal; + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::integer: + { + switch(*s) + { + case 'b':case 'B': + { + state = integer_chars_state::binary; + ++s; + break; + } + case 'x':case 'X': + { + state = integer_chars_state::base16; + ++s; + break; + } + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + { + state = integer_chars_state::octal; + break; + } + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + break; + } + case integer_chars_state::binary: + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_2 = max_value / 2; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1': + x = static_cast<T>(*s) - static_cast<T>('0'); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_2) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 2; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + case integer_chars_state::octal: + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_8 = max_value / 8; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': + x = static_cast<T>(*s) - static_cast<T>('0'); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_8) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 8; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + case integer_chars_state::decimal: + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = static_cast<T>(*s) - static_cast<T>('0'); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_10) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n += x; + } + break; + } + case integer_chars_state::base16: + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + + n += x; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return (state == integer_chars_state::initial) ? to_integer_result<T,CharT>(s, to_integer_errc::invalid_number) : to_integer_result<T,CharT>(s, to_integer_errc()); +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + if (length == 0) + { + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_number); + } + + bool is_negative = *s == '-' ? true : false; + if (is_negative) + { + ++s; + --length; + } + + using U = typename type_traits::make_unsigned<T>::type; + + U u; + auto ru = to_integer(s, length, u); + if (ru.ec != to_integer_errc()) + { + return to_integer_result<T,CharT>(ru.ptr, ru.ec); + } + if (is_negative) + { + if (u > static_cast<U>(-((type_traits::integer_limits<T>::lowest)()+T(1))) + U(1)) + { + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast<T>(U(0) - u); + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc()); + } + } + else + { + if (u > static_cast<U>((type_traits::integer_limits<T>::max)())) + { + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow); + } + else + { + n = static_cast<T>(u); + return to_integer_result<T,CharT>(ru.ptr, to_integer_errc()); + } + } +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized,to_integer_result<T,CharT>>::type +to_integer(const CharT* s, T& n) +{ + return to_integer<T,CharT>(s, std::char_traits<CharT>::length(s), n); +} + +// Precondition: s satisfies + +// digit +// digit1-digits +// - digit +// - digit1-digits + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer_unchecked(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + const CharT* end = s + length; + if (*s == '-') + { + static constexpr T min_value = (type_traits::integer_limits<T>::lowest)(); + static constexpr T min_value_div_10 = min_value / 10; + ++s; + for (; s < end; ++s) + { + T x = (T)*s - (T)('0'); + if (n < min_value_div_10) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 10; + if (n < min_value + x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + + n -= x; + } + } + else + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = static_cast<T>(*s) - static_cast<T>('0'); + if (n > max_value_div_10) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + + n += x; + } + } + + return to_integer_result<T,CharT>(s, to_integer_errc()); +} + +// Precondition: s satisfies + +// digit +// digit1-digits +// - digit +// - digit1-digits + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +to_integer_unchecked(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + + const CharT* end = s + length; + if (*s == '-') + { + static constexpr T min_value = (type_traits::integer_limits<T>::lowest)(); + static constexpr T min_value_div_10 = min_value / 10; + ++s; + for (; s < end; ++s) + { + T x = (T)*s - (T)('0'); + if (n < min_value_div_10) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 10; + if (n < min_value + x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + + n -= x; + } + } + else + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = static_cast<T>(*s) - static_cast<T>('0'); + if (n > max_value_div_10) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 10; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + + n += x; + } + } + + return to_integer_result<T,CharT>(s, to_integer_errc()); +} + +// base16_to_integer + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +base16_to_integer(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + + const CharT* end = s + length; + if (*s == '-') + { + static constexpr T min_value = (type_traits::integer_limits<T>::lowest)(); + static constexpr T min_value_div_16 = min_value / 16; + ++s; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n < min_value_div_16) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 16; + if (n < min_value + x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n -= x; + } + } + else + { + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + + n += x; + } + } + + return to_integer_result<T,CharT>(s, to_integer_errc()); +} + +template <class T, class CharT> +typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type +base16_to_integer(const CharT* s, std::size_t length, T& n) +{ + static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + const CharT* end = s + length; + + static constexpr T max_value = (type_traits::integer_limits<T>::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = *s; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit); + } + if (n > max_value_div_16) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + n = n * 16; + if (n > max_value - x) + { + return to_integer_result<T,CharT>(s, to_integer_errc::overflow); + } + + n += x; + } + + return to_integer_result<T,CharT>(s, to_integer_errc()); +} + + +#if defined(JSONCONS_HAS_STD_FROM_CHARS) + +class chars_to +{ +public: + + char get_decimal_point() const + { + return '.'; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,char>::value,double>::type + operator()(const CharT* s, std::size_t len) const + { + double val = 0; + const auto res = std::from_chars(s, s+len, val); + if (res.ec != std::errc()) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert chars to double failed")); + } + return val; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type + operator()(const CharT* s, std::size_t len) const + { + std::string input(len,'0'); + for (size_t i = 0; i < len; ++i) + { + input[i] = static_cast<char>(s[i]); + } + + double val = 0; + const auto res = std::from_chars(input.data(), input.data() + len, val); + if (res.ec != std::errc()) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert chars to double failed")); + } + return val; + } +}; +#elif defined(JSONCONS_HAS_MSC_STRTOD_L) + +class chars_to +{ +private: + _locale_t locale_; +public: + chars_to() + { + locale_ = _create_locale(LC_NUMERIC, "C"); + } + ~chars_to() noexcept + { + _free_locale(locale_); + } + + chars_to(const chars_to&) + { + locale_ = _create_locale(LC_NUMERIC, "C"); + } + + chars_to& operator=(const chars_to&) + { + // Don't assign locale + return *this; + } + + char get_decimal_point() const + { + return '.'; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,char>::value,double>::type + operator()(const CharT* s, std::size_t) const + { + CharT *end = nullptr; + double val = _strtod_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed")); + } + return val; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type + operator()(const CharT* s, std::size_t) const + { + CharT *end = nullptr; + double val = _wcstod_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed")); + } + return val; + } +}; + +#elif defined(JSONCONS_HAS_STRTOLD_L) + +class chars_to +{ +private: + locale_t locale_; +public: + chars_to() + { + locale_ = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + } + ~chars_to() noexcept + { + freelocale(locale_); + } + + chars_to(const chars_to&) + { + locale_ = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + } + + chars_to& operator=(const chars_to&) + { + return *this; + } + + char get_decimal_point() const + { + return '.'; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,char>::value,double>::type + operator()(const CharT* s, std::size_t) const + { + char *end = nullptr; + double val = strtold_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed")); + } + return val; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type + operator()(const CharT* s, std::size_t) const + { + CharT *end = nullptr; + double val = wcstold_l(s, &end, locale_); + if (s == end) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed")); + } + return val; + } +}; + +#else +class chars_to +{ +private: + std::vector<char> buffer_; + char decimal_point_; +public: + chars_to() + : buffer_() + { + struct lconv * lc = localeconv(); + if (lc != nullptr && lc->decimal_point[0] != 0) + { + decimal_point_ = lc->decimal_point[0]; + } + else + { + decimal_point_ = '.'; + } + buffer_.reserve(100); + } + + chars_to(const chars_to&) = default; + chars_to& operator=(const chars_to&) = default; + + char get_decimal_point() const + { + return decimal_point_; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,char>::value,double>::type + operator()(const CharT* s, std::size_t /*length*/) const + { + CharT *end = nullptr; + double val = strtod(s, &end); + if (s == end) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed")); + } + return val; + } + + template <class CharT> + typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type + operator()(const CharT* s, std::size_t /*length*/) const + { + CharT *end = nullptr; + double val = wcstod(s, &end); + if (s == end) + { + JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed")); + } + return val; + } +}; +#endif + +}} + +#endif |