|
|
@@ -31,6 +31,16 @@ namespace json {
|
|
|
using malformed_json_exception::malformed_json_exception;
|
|
|
};
|
|
|
|
|
|
+ class json_numeric_exception :
|
|
|
+ public std::domain_error {
|
|
|
+ using std::domain_error::domain_error;
|
|
|
+ };
|
|
|
+
|
|
|
+ class json_numeric_width_exception :
|
|
|
+ public json_numeric_exception {
|
|
|
+ using json_numeric_exception::json_numeric_exception;
|
|
|
+ };
|
|
|
+
|
|
|
namespace {
|
|
|
const constexpr json::int_jt INT_JT_MAX = std::numeric_limits<json::int_jt>::max();
|
|
|
const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (INT_JT_MAX % 10);
|
|
|
@@ -84,45 +94,77 @@ namespace json { namespace helper {
|
|
|
template <typename T>
|
|
|
void parse_string(T& json, char const * & data) {
|
|
|
json = parse_string(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename T>
|
|
|
+ void parse_double_impl(T & json, char const * begin, char const * end) {
|
|
|
+ json = std::stod(std::string(begin, end));
|
|
|
+ }
|
|
|
+ template <>
|
|
|
+ inline void parse_double_impl<float>(float & json, char const * begin, char const * end) {
|
|
|
+ json = std::stof(std::string(begin, end));
|
|
|
+ }
|
|
|
+ template <>
|
|
|
+ inline void parse_double_impl<long double>(long double & json, char const * begin, char const * end) {
|
|
|
+ json = std::stold(std::string(begin, end));
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename T>
|
|
|
+ void parse_double(T& json, char const * & data) {
|
|
|
+ char const * begin = data;
|
|
|
+ if (*data == '-') { ++data; }
|
|
|
+ while (isdigit(*data) || *data == '-') { ++data; }
|
|
|
+ if (*data == '.') { ++data; }
|
|
|
+ else if (begin != data && (*data == 'e' || *data == 'E')) {
|
|
|
+ ++data;
|
|
|
+ if (*data == '-') { ++data; }
|
|
|
}
|
|
|
+ while (isdigit(*data)) { ++data; }
|
|
|
+ if ( begin == data ) {
|
|
|
+ throw json::json_numeric_exception{"Expected numeric data"};
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ parse_double_impl(json, begin, data);
|
|
|
+ } catch (std::domain_error const &) {
|
|
|
+ throw json::json_numeric_exception{"Expected a numeric type"};
|
|
|
+ } catch (std::out_of_range const &) {
|
|
|
+ throw json::json_numeric_width_exception{"Number is out-of-range for floating-point type"};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename J>
|
|
|
+ void parse_numeric(J & json, char const * & data) {
|
|
|
+ numeric_token_info info = data;
|
|
|
|
|
|
- template <typename T>
|
|
|
- void parse_double(T& json, char const * & data) {
|
|
|
- json = atof(data);
|
|
|
- while (isdigit(*data)) { ++data; }
|
|
|
- if (*data == '.' || *data == 'e') { ++data; }
|
|
|
- while (isdigit(*data)) { ++data; }
|
|
|
- }
|
|
|
-
|
|
|
-// template <typename T>
|
|
|
-// void parse_integer(T& json, char const * & data) {
|
|
|
-// json = atoi(data);
|
|
|
-// while (isdigit(*data)) { ++data; }
|
|
|
-// }
|
|
|
-
|
|
|
- template <typename J>
|
|
|
- void parse_numeric(J & json, char const * & data) {
|
|
|
- numeric_token_info info = data;
|
|
|
+ if ( info.is_double || info.parse_numeric() == DOUBLE ) {
|
|
|
+ if (std::is_integral<J>::value) {
|
|
|
+ throw json::json_numeric_exception{"Expected integer, got double"};
|
|
|
+ }
|
|
|
+ helper::parse_double(json, data);
|
|
|
+ } else {
|
|
|
+ uint_jt const val = info.val;
|
|
|
|
|
|
- if ( info.is_double ) {
|
|
|
- helper::parse_double(json, data);
|
|
|
- } else if ( info.parse_numeric() == DOUBLE ) {
|
|
|
- helper::parse_double(json, data);
|
|
|
- } else {
|
|
|
- uint_jt const val = info.val;
|
|
|
- if (info.is_negative) {
|
|
|
- if (val == INT_JT_OVER) {
|
|
|
- json = INT_JT_MIN;
|
|
|
- } else {
|
|
|
- json = -int_jt(val);
|
|
|
- }
|
|
|
- } else if (val <= uint_jt(INT_JT_MAX)) {
|
|
|
- json = int_jt(val);
|
|
|
+ if (fls(val) > std::numeric_limits<J>::digits + info.is_negative) {
|
|
|
+ throw json::json_numeric_width_exception{"Integer width too small for parsed value"};
|
|
|
+ }
|
|
|
+
|
|
|
+ if (info.is_negative) {
|
|
|
+ if (!std::is_signed<J>::value) {
|
|
|
+ throw json::json_numeric_exception{"Expected signed integer"};
|
|
|
+ }
|
|
|
+ if (val == INT_JT_OVER) {
|
|
|
+ json = (J) INT_JT_MIN; // Cast suppresses error message, narrowing check above makes safe
|
|
|
} else {
|
|
|
- json = val;
|
|
|
+ json = -int_jt(val);
|
|
|
}
|
|
|
- data = info.it;
|
|
|
+ } else if (val <= uint_jt(INT_JT_MAX)) {
|
|
|
+ json = int_jt(val);
|
|
|
+ } else if (std::is_signed<J>::value) {
|
|
|
+ throw json::json_numeric_exception{"Expected unsigned integer"};
|
|
|
+ } else {
|
|
|
+ json = val;
|
|
|
}
|
|
|
+ data = info.it;
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
+} }
|