from_chars_integer_impl.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
  5. #define BOOST_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
  6. #include <boost/charconv/detail/apply_sign.hpp>
  7. #include <boost/charconv/detail/config.hpp>
  8. #include <boost/charconv/detail/from_chars_result.hpp>
  9. #include <boost/charconv/detail/emulated128.hpp>
  10. #include <boost/charconv/detail/type_traits.hpp>
  11. #include <boost/charconv/config.hpp>
  12. #include <boost/config.hpp>
  13. #include <system_error>
  14. #include <type_traits>
  15. #include <limits>
  16. #include <cstdlib>
  17. #include <cerrno>
  18. #include <cstddef>
  19. #include <cstdint>
  20. namespace boost { namespace charconv { namespace detail {
  21. static constexpr unsigned char uchar_values[] =
  22. {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  23. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  24. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  25. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
  26. 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
  27. 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
  28. 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
  29. 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 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. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  35. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  36. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  37. 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
  38. static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 256 values of unsigned char");
  39. static constexpr double log_2_table[] =
  40. {
  41. 0.0,
  42. 0.0,
  43. 1.0,
  44. 0.630929753571,
  45. 0.5,
  46. 0.430676558073,
  47. 0.386852807235,
  48. 0.356207187108,
  49. 0.333333333333,
  50. 0.315464876786,
  51. 0.301029995664,
  52. 0.289064826318,
  53. 0.278942945651,
  54. 0.270238154427,
  55. 0.262649535037,
  56. 0.255958024810,
  57. 0.25,
  58. 0.244650542118,
  59. 0.239812466568,
  60. 0.235408913367,
  61. 0.231378213160,
  62. 0.227670248697,
  63. 0.224243824218,
  64. 0.221064729458,
  65. 0.218104291986,
  66. 0.215338279037,
  67. 0.212746053553,
  68. 0.210309917857,
  69. 0.208014597677,
  70. 0.205846832460,
  71. 0.203795047091,
  72. 0.201849086582,
  73. 0.2,
  74. 0.198239863171,
  75. 0.196561632233,
  76. 0.194959021894,
  77. 0.193426403617
  78. };
  79. // Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255
  80. constexpr unsigned char digit_from_char(char val) noexcept
  81. {
  82. return uchar_values[static_cast<unsigned char>(val)];
  83. }
  84. #ifdef BOOST_MSVC
  85. # pragma warning(push)
  86. # pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned
  87. # pragma warning(disable: 4189) // 'is_negative': local variable is initialized but not referenced
  88. # pragma warning(disable: 4127) // Conditional expression is constant (if constexpr pre-c++17)
  89. #elif defined(__clang__)
  90. # pragma clang diagnostic push
  91. # pragma clang diagnostic ignored "-Wconstant-conversion"
  92. #elif defined(__GNUC__) && (__GNUC__ < 7)
  93. # pragma GCC diagnostic push
  94. # pragma GCC diagnostic ignored "-Woverflow"
  95. # pragma GCC diagnostic ignored "-Wconversion"
  96. # pragma GCC diagnostic ignored "-Wsign-conversion"
  97. #elif defined(__GNUC__) && (__GNUC__ >= 7)
  98. # pragma GCC diagnostic push
  99. # pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  100. # pragma GCC diagnostic ignored "-Wconversion"
  101. #endif
  102. template <typename Integer, typename Unsigned_Integer>
  103. BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept
  104. {
  105. Unsigned_Integer result = 0;
  106. Unsigned_Integer overflow_value = 0;
  107. Unsigned_Integer max_digit = 0;
  108. // Check pre-conditions
  109. if (!((first <= last) && (base >= 2 && base <= 36)))
  110. {
  111. return {first, std::errc::invalid_argument};
  112. }
  113. const auto unsigned_base = static_cast<Unsigned_Integer>(base);
  114. // Strip sign if the type is signed
  115. // Negative sign will be appended at the end of parsing
  116. BOOST_ATTRIBUTE_UNUSED bool is_negative = false;
  117. auto next = first;
  118. BOOST_CHARCONV_IF_CONSTEXPR (is_signed<Integer>::value)
  119. {
  120. if (next != last)
  121. {
  122. if (*next == '-')
  123. {
  124. is_negative = true;
  125. ++next;
  126. }
  127. else if (*next == '+' || *next == ' ')
  128. {
  129. return {next, std::errc::invalid_argument};
  130. }
  131. }
  132. #ifdef BOOST_CHARCONV_HAS_INT128
  133. BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value)
  134. {
  135. overflow_value = BOOST_CHARCONV_INT128_MAX;
  136. max_digit = BOOST_CHARCONV_INT128_MAX;
  137. }
  138. else
  139. #endif
  140. {
  141. overflow_value = (std::numeric_limits<Integer>::max)();
  142. max_digit = (std::numeric_limits<Integer>::max)();
  143. }
  144. if (is_negative)
  145. {
  146. ++overflow_value;
  147. ++max_digit;
  148. }
  149. }
  150. else
  151. {
  152. if (next != last && (*next == '-' || *next == '+' || *next == ' '))
  153. {
  154. return {first, std::errc::invalid_argument};
  155. }
  156. #ifdef BOOST_CHARCONV_HAS_INT128
  157. BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::uint128_type>::value)
  158. {
  159. overflow_value = BOOST_CHARCONV_UINT128_MAX;
  160. max_digit = BOOST_CHARCONV_UINT128_MAX;
  161. }
  162. else
  163. #endif
  164. {
  165. overflow_value = (std::numeric_limits<Unsigned_Integer>::max)();
  166. max_digit = (std::numeric_limits<Unsigned_Integer>::max)();
  167. }
  168. }
  169. BOOST_IF_CONSTEXPR ((std::numeric_limits<Integer>::is_signed && sizeof(Integer) == 16)
  170. #ifdef BOOST_CHARCONV_HAS_INT128
  171. || std::is_same<boost::int128_type, Integer>::value
  172. #endif
  173. )
  174. {
  175. overflow_value /= unsigned_base;
  176. max_digit %= unsigned_base;
  177. #ifndef __GLIBCXX_TYPE_INT_N_0
  178. if (base != 10)
  179. {
  180. // Overflow value would cause INT128_MIN in non-base10 to fail
  181. overflow_value *= static_cast<Unsigned_Integer>(2);
  182. }
  183. #endif
  184. }
  185. else
  186. {
  187. overflow_value /= unsigned_base;
  188. max_digit %= unsigned_base;
  189. }
  190. // If the only character was a sign abort now
  191. if (next == last)
  192. {
  193. return {first, std::errc::invalid_argument};
  194. }
  195. bool overflowed = false;
  196. const std::ptrdiff_t nc = last - next;
  197. // In non-GNU mode on GCC numeric limits may not be specialized
  198. #if defined(BOOST_CHARCONV_HAS_INT128) && !defined(__GLIBCXX_TYPE_INT_N_0)
  199. constexpr std::ptrdiff_t nd_2 = std::is_same<Integer, boost::int128_type>::value ? 127 :
  200. std::is_same<Integer, boost::uint128_type>::value ? 128 :
  201. std::numeric_limits<Integer>::digits;
  202. #else
  203. constexpr std::ptrdiff_t nd_2 = std::numeric_limits<Integer>::digits;
  204. #endif
  205. const auto nd = static_cast<std::ptrdiff_t>(nd_2 * log_2_table[static_cast<std::size_t>(unsigned_base)]);
  206. {
  207. // Check that the first character is valid before proceeding
  208. const unsigned char first_digit = digit_from_char(*next);
  209. if (first_digit >= unsigned_base)
  210. {
  211. return {first, std::errc::invalid_argument};
  212. }
  213. result = static_cast<Unsigned_Integer>(result * unsigned_base + first_digit);
  214. ++next;
  215. std::ptrdiff_t i = 1;
  216. for( ; i < nd && i < nc; ++i )
  217. {
  218. // overflow is not possible in the first nd characters
  219. const unsigned char current_digit = digit_from_char(*next);
  220. if (current_digit >= unsigned_base)
  221. {
  222. break;
  223. }
  224. result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
  225. ++next;
  226. }
  227. for( ; i < nc; ++i )
  228. {
  229. const unsigned char current_digit = digit_from_char(*next);
  230. if (current_digit >= unsigned_base)
  231. {
  232. break;
  233. }
  234. if (result < overflow_value || (result == overflow_value && current_digit <= max_digit))
  235. {
  236. result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
  237. }
  238. else
  239. {
  240. // Required to keep updating the value of next, but the result is garbage
  241. overflowed = true;
  242. }
  243. ++next;
  244. }
  245. }
  246. // Return the parsed value, adding the sign back if applicable
  247. // If we have overflowed then we do not return the result
  248. if (overflowed)
  249. {
  250. return {next, std::errc::result_out_of_range};
  251. }
  252. value = static_cast<Integer>(result);
  253. BOOST_IF_CONSTEXPR (is_signed<Integer>::value)
  254. {
  255. if (is_negative)
  256. {
  257. value = static_cast<Integer>(-(static_cast<Unsigned_Integer>(value)));
  258. }
  259. }
  260. return {next, std::errc()};
  261. }
  262. #ifdef BOOST_MSVC
  263. # pragma warning(pop)
  264. #elif defined(__clang__)
  265. # pragma clang diagnostic pop
  266. #elif defined(__GNUC__)
  267. # pragma GCC diagnostic pop
  268. #endif
  269. // Only from_chars for integer types is constexpr (as of C++23)
  270. template <typename Integer>
  271. BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, Integer& value, int base = 10) noexcept
  272. {
  273. using Unsigned_Integer = typename std::make_unsigned<Integer>::type;
  274. return detail::from_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
  275. }
  276. #ifdef BOOST_CHARCONV_HAS_INT128
  277. template <typename Integer>
  278. BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars128(const char* first, const char* last, Integer& value, int base = 10) noexcept
  279. {
  280. using Unsigned_Integer = boost::uint128_type;
  281. return detail::from_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
  282. }
  283. #endif
  284. BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars128(const char* first, const char* last, uint128& value, int base = 10) noexcept
  285. {
  286. return from_chars_integer_impl<uint128, uint128>(first, last, value, base);
  287. }
  288. }}} // Namespaces
  289. #endif // BOOST_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP