json_common.hpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 <errno.h>
  11. #include <limits>
  12. #include <stdexcept>
  13. #include <string>
  14. #include <strings.h>
  15. #include "json_exception.h"
  16. namespace json {
  17. using string_jt = std::string;
  18. using double_jt = double;
  19. using int_jt = int32_t;
  20. using uint_jt = uint32_t;
  21. using bool_jt = bool;
  22. class value;
  23. template <typename T = json::int_jt> struct numeric_limits {
  24. static constexpr const T max{std::numeric_limits<T>::max()};
  25. static constexpr const T min{std::numeric_limits<T>::min()};
  26. static constexpr const uint_jt over{uint_jt{max} + 1};
  27. };
  28. namespace {
  29. const constexpr int_jt INT_JT_MAX = std::numeric_limits<int_jt>::max();
  30. const constexpr int_jt INT_JT_MAX_LAST_DIGIT = (INT_JT_MAX % 10);
  31. const constexpr int_jt INT_JT_MIN = std::numeric_limits<int_jt>::min();
  32. const constexpr uint_jt INT_JT_OVER = uint_jt(INT_JT_MAX) + 1;
  33. const constexpr uint_jt UINT_JT_MAX = uint_jt(0) - 1;
  34. // const constexpr uint_jt UINT_JT_MIN = 0;
  35. }
  36. }
  37. namespace json { namespace parser {
  38. enum options {
  39. allow_all = 0x00,
  40. disable_unknown_keys = 0x01,
  41. disable_missing_keys = 0x02,
  42. disable_concatenated_json_bodies = 0x04,
  43. disable_all = 0xFF,
  44. };
  45. }}
  46. namespace json { namespace helper {
  47. const char get_next_element(char const *& data);
  48. enum numeric_state { DOUBLE, INTEGER };
  49. struct numeric_token_info {
  50. enum parse_state { decimal, octal, hexadecimal };
  51. numeric_token_info(char const * start);
  52. numeric_state parse_numeric();
  53. uint_jt val;
  54. parse_state base;
  55. char const * it;
  56. char const * end;
  57. bool is_double;
  58. bool is_negative;
  59. };
  60. numeric_token_info get_numeric_token_info(char const * it);
  61. /**
  62. * @throws json::malformed_json_exception
  63. */
  64. void advance_to_boundary(char const endtok, char const *& data);
  65. /**
  66. * @throws json::malformed_json_exception
  67. */
  68. std::string parse_string(char const *& data);
  69. std::string replace_all(std::string str, std::string const & from,
  70. std::string const & to);
  71. template <typename T> void parse_string(T & json, char const *& data) {
  72. json::helper::get_next_element(data);
  73. json = parse_string(data);
  74. }
  75. template <typename T>
  76. T parse_double_impl(char const * begin, char const *& end) {
  77. return std::strtod(begin, const_cast<char **>(&end));
  78. }
  79. template <>
  80. inline float parse_double_impl<float>(char const * begin, char const *& end) {
  81. return std::strtof(begin, const_cast<char **>(&end));
  82. }
  83. template <>
  84. inline long double parse_double_impl<long double>(char const * begin,
  85. char const *& end) {
  86. return std::strtold(begin, const_cast<char **>(&end));
  87. }
  88. template <typename T> void parse_double(T & json, char const *& data) {
  89. json::helper::get_next_element(data);
  90. char const * begin = data;
  91. errno = 0;
  92. T tmp = parse_double_impl<T>(begin, data);
  93. if (errno != 0) {
  94. throw json::json_numeric_width_exception{
  95. "Number is out-of-range for floating-point type"};
  96. } else if (begin == data) {
  97. throw json::json_numeric_exception("Expected numeric data");
  98. }
  99. errno = 0;
  100. json = std::move(tmp);
  101. }
  102. template <typename J>
  103. bool has_too_many_digits(numeric_token_info const & info) {
  104. return fls(static_cast<int_jt>(info.val)) >
  105. std::numeric_limits<J>::digits + info.is_negative;
  106. }
  107. template <typename J> void parse_numeric(J & json, char const *& data) {
  108. json::helper::get_next_element(data);
  109. numeric_token_info info = data;
  110. if (info.is_negative && !std::is_signed<J>::value) {
  111. throw json_numeric_exception("Expected signed integer");
  112. } else if (info.is_double || info.parse_numeric() == DOUBLE) {
  113. throw json_numeric_exception("Expected integer, got double");
  114. } else if (info.base == numeric_token_info::decimal &&
  115. (has_too_many_digits<J>(info) ||
  116. info.val > json::numeric_limits<J>::over)) {
  117. throw json_numeric_width_exception{
  118. "Integer width too small for parsed value"};
  119. } else if (info.is_negative) {
  120. if (info.val == json::numeric_limits<J>::over) {
  121. json = json::numeric_limits<J>::min;
  122. } else {
  123. json = static_cast<J>(-int_jt(info.val));
  124. }
  125. } else if (info.val <= uint_jt(INT_JT_MAX)) {
  126. json = static_cast<J>(int_jt(info.val));
  127. } else if (std::is_signed<J>::value &&
  128. info.base == numeric_token_info::decimal) {
  129. throw json_numeric_exception("Expected unsigned integer");
  130. } else {
  131. json = static_cast<J>(info.val);
  132. }
  133. data = info.it;
  134. }
  135. }}