// // json_common.h // json // // Created by Sam Jaffe on 4/22/16. // #pragma once #include #include #include #include namespace json { using string_jt = std::string; using double_jt = double; using int_jt = int32_t; using uint_jt = uint32_t; using bool_jt = bool; class value; class malformed_json_exception : public std::domain_error { using std::domain_error::domain_error; }; class unterminated_json_exception : public malformed_json_exception { 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::max(); const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (INT_JT_MAX % 10); const constexpr json::int_jt INT_JT_MIN = std::numeric_limits::min(); const constexpr json::uint_jt INT_JT_OVER = json::uint_jt(INT_JT_MAX) + 1; const constexpr json::uint_jt UINT_JT_MAX = json::uint_jt(0) - 1; // const constexpr json::uint_jt UINT_JT_MIN = 0; } } namespace json { namespace parser { enum options { allow_all = 0x00, disable_unknown_keys = 0x01, disable_concatenated_json_bodies = 0x02, disable_all = 0xFF, }; } } namespace json { namespace helper { const char get_next_element(char const*& data); enum numeric_state { DOUBLE, INTEGER }; struct numeric_token_info { numeric_token_info(char const * start); numeric_state parse_numeric(); uint_jt val; char const * it; char const * end; bool is_double; bool is_negative; }; numeric_token_info get_numeric_token_info(char const * it); /** * @throws json::malformed_json_exception */ void advance_to_boundary(char const endtok, char const *& data); /** * @throws json::malformed_json_exception */ std::string parse_string(char const * & data); template void parse_string(T& json, char const * & data) { json = parse_string(data); } template 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 & json, char const * begin, char const * end) { json = std::stof(std::string(begin, end)); } template <> inline void parse_double_impl(long double & json, char const * begin, char const * end) { json = std::stold(std::string(begin, end)); } template 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 void parse_numeric(J & json, char const * & data) { if (std::is_floating_point::value) { helper::parse_double(json, data); return; } numeric_token_info info = data; if ( info.is_double || info.parse_numeric() == DOUBLE ) { if (std::is_integral::value) { throw json::json_numeric_exception{"Expected integer, got double"}; } helper::parse_double(json, data); } else { uint_jt const val = info.val; if (fls(val) > std::numeric_limits::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::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 = -int_jt(val); } } else if (val <= uint_jt(INT_JT_MAX)) { json = int_jt(val); } else if (std::is_signed::value) { throw json::json_numeric_exception{"Expected unsigned integer"}; } else { json = val; } data = info.it; } } } }