// // json_common.h // json // // Created by Sam Jaffe on 4/22/16. // #pragma once #include #include #include #include #include #include #include #include "json_exception.h" 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; template struct numeric_limits { static constexpr const T max{std::numeric_limits::max()}; static constexpr const T min{std::numeric_limits::min()}; static constexpr const uint_jt over{uint_jt{max}+1}; }; namespace { const constexpr int_jt INT_JT_MAX = std::numeric_limits::max(); const constexpr int_jt INT_JT_MAX_LAST_DIGIT = (INT_JT_MAX % 10); const constexpr int_jt INT_JT_MIN = std::numeric_limits::min(); const constexpr uint_jt INT_JT_OVER = uint_jt(INT_JT_MAX) + 1; const constexpr uint_jt UINT_JT_MAX = uint_jt(0) - 1; // const constexpr uint_jt UINT_JT_MIN = 0; } } namespace json { namespace parser { enum options { allow_all = 0x00, disable_unknown_keys = 0x01, disable_missing_keys = 0x02, disable_concatenated_json_bodies = 0x04, disable_all = 0xFF, }; } } namespace json { namespace helper { const char get_next_element(char const*& data); enum numeric_state { DOUBLE, INTEGER }; struct numeric_token_info { enum parse_state { decimal, octal, hexadecimal }; numeric_token_info(char const * start); numeric_state parse_numeric(); uint_jt val; parse_state base; 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); std::string replace_all(std::string str, std::string const & from, std::string const & to); template void parse_string(T& json, char const * & data) { json::helper::get_next_element(data); json = parse_string(data); } template T parse_double_impl(char const * begin, char const * & end) { return std::strtod(begin, const_cast(&end)); } template <> inline float parse_double_impl(char const * begin, char const * & end) { return std::strtof(begin, const_cast(&end)); } template <> inline long double parse_double_impl(char const * begin, char const * & end) { return std::strtold(begin, const_cast(&end)); } template void parse_double(T& json, char const * & data) { json::helper::get_next_element(data); char const * begin = data; errno = 0; T tmp = parse_double_impl(begin, data); if (errno != 0) { throw json::json_numeric_width_exception{ "Number is out-of-range for floating-point type" }; } else if ( begin == data ) { throw json::json_numeric_exception("Expected numeric data"); } errno = 0; json = std::move(tmp); } template bool has_too_many_digits(numeric_token_info const & info) { return fls(static_cast(info.val)) > std::numeric_limits::digits + info.is_negative; } template void parse_numeric(J & json, char const * & data) { json::helper::get_next_element(data); numeric_token_info info = data; if ( info.is_negative && !std::is_signed::value ) { throw json_numeric_exception("Expected signed integer"); } else if ( info.is_double || info.parse_numeric() == DOUBLE ) { throw json_numeric_exception("Expected integer, got double"); } else if (info.base == numeric_token_info::decimal && (has_too_many_digits(info) || info.val > json::numeric_limits::over)) { throw json_numeric_width_exception{ "Integer width too small for parsed value" }; } else if (info.is_negative) { if (info.val == json::numeric_limits::over) { json = json::numeric_limits::min; } else { json = static_cast(-int_jt(info.val)); } } else if (info.val <= uint_jt(INT_JT_MAX)) { json = static_cast(int_jt(info.val)); } else if (std::is_signed::value && info.base == numeric_token_info::decimal) { throw json_numeric_exception("Expected unsigned integer"); } else { json = static_cast(info.val); } data = info.it; } } }