from_chars_integer_impl.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // Copyright 2023 Matt Borland
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // https://www.boost.org/LICENSE_1_0.txt
  4. #ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
  5. #define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
  6. #include <boost/json/detail/charconv/detail/config.hpp>
  7. #include <boost/json/detail/charconv/detail/from_chars_result.hpp>
  8. #include <boost/config.hpp>
  9. #include <system_error>
  10. #include <type_traits>
  11. #include <limits>
  12. #include <cstdlib>
  13. #include <cerrno>
  14. #include <cstddef>
  15. #include <cstdint>
  16. namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
  17. static constexpr unsigned char uchar_values[] =
  18. {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  19. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  20. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  21. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
  22. 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
  23. 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
  24. 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
  25. 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
  26. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  27. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  28. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  29. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  30. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  31. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  32. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  33. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
  34. static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 256 values of unsigned char");
  35. // Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255
  36. constexpr unsigned char digit_from_char(char val) noexcept
  37. {
  38. return uchar_values[static_cast<unsigned char>(val)];
  39. }
  40. #ifdef BOOST_MSVC
  41. # pragma warning(push)
  42. # pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned
  43. # pragma warning(disable: 4189) // 'is_negative': local variable is initialized but not referenced
  44. #elif defined(__clang__)
  45. # pragma clang diagnostic push
  46. # pragma clang diagnostic ignored "-Wconstant-conversion"
  47. #elif defined(__GNUC__) && (__GNUC__ < 7)
  48. # pragma GCC diagnostic push
  49. # pragma GCC diagnostic ignored "-Woverflow"
  50. #elif defined(__GNUC__) && (__GNUC__ >= 7)
  51. # pragma GCC diagnostic push
  52. # pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  53. #endif
  54. template <typename Integer, typename Unsigned_Integer>
  55. BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept
  56. {
  57. Unsigned_Integer result = 0;
  58. Unsigned_Integer overflow_value = 0;
  59. Unsigned_Integer max_digit = 0;
  60. // Check pre-conditions
  61. if (!((first <= last) && (base >= 2 && base <= 36)))
  62. {
  63. return {first, std::errc::invalid_argument};
  64. }
  65. Unsigned_Integer unsigned_base = static_cast<Unsigned_Integer>(base);
  66. // Strip sign if the type is signed
  67. // Negative sign will be appended at the end of parsing
  68. BOOST_ATTRIBUTE_UNUSED bool is_negative = false;
  69. auto next = first;
  70. #ifdef BOOST_HAS_INT128
  71. BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value || std::is_signed<Integer>::value)
  72. #else
  73. BOOST_IF_CONSTEXPR (std::is_signed<Integer>::value)
  74. #endif
  75. {
  76. if (next != last)
  77. {
  78. if (*next == '-')
  79. {
  80. is_negative = true;
  81. ++next;
  82. }
  83. else if (*next == '+')
  84. {
  85. return {next, std::errc::invalid_argument};
  86. }
  87. }
  88. #ifdef BOOST_HAS_INT128
  89. BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value)
  90. {
  91. overflow_value = BOOST_JSON_INT128_MAX;
  92. max_digit = BOOST_JSON_INT128_MAX;
  93. }
  94. else
  95. #endif
  96. {
  97. overflow_value = (std::numeric_limits<Integer>::max)();
  98. max_digit = (std::numeric_limits<Integer>::max)();
  99. }
  100. if (is_negative)
  101. {
  102. ++overflow_value;
  103. ++max_digit;
  104. }
  105. }
  106. else
  107. {
  108. if (next != last && (*next == '-' || *next == '+'))
  109. {
  110. return {first, std::errc::invalid_argument};
  111. }
  112. #ifdef BOOST_HAS_INT128
  113. BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::uint128_type>::value)
  114. {
  115. overflow_value = BOOST_JSON_UINT128_MAX;
  116. max_digit = BOOST_JSON_UINT128_MAX;
  117. }
  118. else
  119. #endif
  120. {
  121. overflow_value = (std::numeric_limits<Unsigned_Integer>::max)();
  122. max_digit = (std::numeric_limits<Unsigned_Integer>::max)();
  123. }
  124. }
  125. #ifdef BOOST_HAS_INT128
  126. BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value)
  127. {
  128. overflow_value /= unsigned_base;
  129. max_digit %= unsigned_base;
  130. overflow_value *= 2; // Overflow value would cause INT128_MIN in non-base10 to fail
  131. }
  132. else
  133. #endif
  134. {
  135. overflow_value /= unsigned_base;
  136. max_digit %= unsigned_base;
  137. }
  138. // If the only character was a sign abort now
  139. if (next == last)
  140. {
  141. return {first, std::errc::invalid_argument};
  142. }
  143. bool overflowed = false;
  144. std::ptrdiff_t nc = last - next;
  145. constexpr std::ptrdiff_t nd = std::numeric_limits<Integer>::digits10;
  146. {
  147. std::ptrdiff_t i = 0;
  148. for( ; i < nd && i < nc; ++i )
  149. {
  150. // overflow is not possible in the first nd characters
  151. const unsigned char current_digit = digit_from_char(*next);
  152. if (current_digit >= unsigned_base)
  153. {
  154. break;
  155. }
  156. result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
  157. ++next;
  158. }
  159. for( ; i < nc; ++i )
  160. {
  161. const unsigned char current_digit = digit_from_char(*next);
  162. if (current_digit >= unsigned_base)
  163. {
  164. break;
  165. }
  166. if (result < overflow_value || (result == overflow_value && current_digit <= max_digit))
  167. {
  168. result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
  169. }
  170. else
  171. {
  172. // Required to keep updating the value of next, but the result is garbage
  173. overflowed = true;
  174. }
  175. ++next;
  176. }
  177. }
  178. // Return the parsed value, adding the sign back if applicable
  179. // If we have overflowed then we do not return the result
  180. if (overflowed)
  181. {
  182. return {next, std::errc::result_out_of_range};
  183. }
  184. value = static_cast<Integer>(result);
  185. #ifdef BOOST_HAS_INT128
  186. BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value || std::is_signed<Integer>::value)
  187. #else
  188. BOOST_IF_CONSTEXPR (std::is_signed<Integer>::value)
  189. #endif
  190. {
  191. if (is_negative)
  192. {
  193. value = -(static_cast<Unsigned_Integer>(value));
  194. }
  195. }
  196. return {next, std::errc()};
  197. }
  198. #ifdef BOOST_MSVC
  199. # pragma warning(pop)
  200. #elif defined(__clang__) && defined(__APPLE__)
  201. # pragma clang diagnostic pop
  202. #elif defined(__GNUC__) && (__GNUC__ < 7 || __GNUC__ >= 9)
  203. # pragma GCC diagnostic pop
  204. #endif
  205. // Only from_chars for integer types is constexpr (as of C++23)
  206. template <typename Integer>
  207. BOOST_JSON_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, Integer& value, int base = 10) noexcept
  208. {
  209. using Unsigned_Integer = typename std::make_unsigned<Integer>::type;
  210. return detail::from_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
  211. }
  212. }}}}} // Namespaces
  213. #endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP