json_common.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. //
  2. // json_common.h
  3. // json
  4. //
  5. // Created by Sam Jaffe on 4/22/16.
  6. //
  7. #pragma once
  8. #include <cstdint>
  9. #include <cstdlib>
  10. #include <stdexcept>
  11. #include <string>
  12. namespace json {
  13. using string_jt = std::string;
  14. using double_jt = double;
  15. using int_jt = int32_t;
  16. using uint_jt = uint32_t;
  17. using bool_jt = bool;
  18. class value;
  19. class malformed_json_exception :
  20. public std::domain_error {
  21. using std::domain_error::domain_error;
  22. };
  23. class unterminated_json_exception :
  24. public malformed_json_exception {
  25. using malformed_json_exception::malformed_json_exception;
  26. };
  27. class json_numeric_exception :
  28. public std::domain_error {
  29. using std::domain_error::domain_error;
  30. };
  31. class json_numeric_width_exception :
  32. public json_numeric_exception {
  33. using json_numeric_exception::json_numeric_exception;
  34. };
  35. namespace {
  36. const constexpr json::int_jt INT_JT_MAX = std::numeric_limits<json::int_jt>::max();
  37. const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (INT_JT_MAX % 10);
  38. const constexpr json::int_jt INT_JT_MIN = std::numeric_limits<json::int_jt>::min();
  39. const constexpr json::uint_jt INT_JT_OVER = json::uint_jt(INT_JT_MAX) + 1;
  40. const constexpr json::uint_jt UINT_JT_MAX = json::uint_jt(0) - 1;
  41. // const constexpr json::uint_jt UINT_JT_MIN = 0;
  42. }
  43. }
  44. namespace json { namespace parser {
  45. enum options {
  46. allow_all = 0x00,
  47. disable_unknown_keys = 0x01,
  48. disable_concatenated_json_bodies = 0x02,
  49. disable_all = 0xFF,
  50. };
  51. } }
  52. namespace json { namespace helper {
  53. const char get_next_element(char const*& data);
  54. enum numeric_state {
  55. DOUBLE, INTEGER
  56. };
  57. struct numeric_token_info {
  58. numeric_token_info(char const * start);
  59. numeric_state parse_numeric();
  60. uint_jt val;
  61. char const * it;
  62. char const * end;
  63. bool is_double;
  64. bool is_negative;
  65. };
  66. numeric_token_info get_numeric_token_info(char const * it);
  67. /**
  68. * @throws json::malformed_json_exception
  69. */
  70. void advance_to_boundary(char const endtok, char const *& data);
  71. /**
  72. * @throws json::malformed_json_exception
  73. */
  74. std::string parse_string(char const * & data);
  75. template <typename T>
  76. void parse_string(T& json, char const * & data) {
  77. json = parse_string(data);
  78. }
  79. template <typename T>
  80. void parse_double_impl(T & json, char const * begin, char const * end) {
  81. json = std::stod(std::string(begin, end));
  82. }
  83. template <>
  84. inline void parse_double_impl<float>(float & json, char const * begin, char const * end) {
  85. json = std::stof(std::string(begin, end));
  86. }
  87. template <>
  88. inline void parse_double_impl<long double>(long double & json, char const * begin, char const * end) {
  89. json = std::stold(std::string(begin, end));
  90. }
  91. template <typename T>
  92. void parse_double(T& json, char const * & data) {
  93. char const * begin = data;
  94. if (*data == '-') { ++data; }
  95. while (isdigit(*data) || *data == '-') { ++data; }
  96. if (*data == '.') { ++data; }
  97. else if (begin != data && (*data == 'e' || *data == 'E')) {
  98. ++data;
  99. if (*data == '-') { ++data; }
  100. }
  101. while (isdigit(*data)) { ++data; }
  102. if ( begin == data ) {
  103. throw json::json_numeric_exception{"Expected numeric data"};
  104. }
  105. try {
  106. parse_double_impl(json, begin, data);
  107. } catch (std::domain_error const &) {
  108. throw json::json_numeric_exception{"Expected a numeric type"};
  109. } catch (std::out_of_range const &) {
  110. throw json::json_numeric_width_exception{"Number is out-of-range for floating-point type"};
  111. }
  112. }
  113. template <typename J>
  114. void parse_numeric(J & json, char const * & data) {
  115. if (std::is_floating_point<J>::value) {
  116. helper::parse_double(json, data);
  117. return;
  118. }
  119. numeric_token_info info = data;
  120. if ( info.is_double || info.parse_numeric() == DOUBLE ) {
  121. if (std::is_integral<J>::value) {
  122. throw json::json_numeric_exception{"Expected integer, got double"};
  123. }
  124. helper::parse_double(json, data);
  125. } else {
  126. uint_jt const val = info.val;
  127. if (fls(val) > std::numeric_limits<J>::digits + info.is_negative) {
  128. throw json::json_numeric_width_exception{"Integer width too small for parsed value"};
  129. }
  130. if (info.is_negative) {
  131. if (!std::is_signed<J>::value) {
  132. throw json::json_numeric_exception{"Expected signed integer"};
  133. }
  134. if (val == INT_JT_OVER) {
  135. json = (J) INT_JT_MIN; // Cast suppresses error message, narrowing check above makes safe
  136. } else {
  137. json = -int_jt(val);
  138. }
  139. } else if (val <= uint_jt(INT_JT_MAX)) {
  140. json = int_jt(val);
  141. } else if (std::is_signed<J>::value) {
  142. throw json::json_numeric_exception{"Expected unsigned integer"};
  143. } else {
  144. json = val;
  145. }
  146. data = info.it;
  147. }
  148. }
  149. } }