fallback_routines.hpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // Copyright 2024 Matt Borland
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // https://www.boost.org/LICENSE_1_0.txt
  4. #ifndef BOOST_FALLBACK_ROUTINES_HPP
  5. #define BOOST_FALLBACK_ROUTINES_HPP
  6. #include <boost/charconv/detail/to_chars_integer_impl.hpp>
  7. #include <boost/charconv/detail/dragonbox/floff.hpp>
  8. #include <boost/charconv/detail/config.hpp>
  9. #include <boost/charconv/detail/from_chars_result.hpp>
  10. #include <boost/charconv/chars_format.hpp>
  11. #include <system_error>
  12. #include <type_traits>
  13. #include <locale>
  14. #include <clocale>
  15. #include <cstring>
  16. #include <cstdio>
  17. namespace boost {
  18. namespace charconv {
  19. namespace detail {
  20. #ifdef BOOST_CHARCONV_HAS_FLOAT128
  21. inline int print_val(char* first, std::size_t size, char* format, __float128 value) noexcept
  22. {
  23. return quadmath_snprintf(first, size, format, value);
  24. }
  25. #endif
  26. template <typename T>
  27. inline int print_val(char* first, std::size_t size, char* format, T value) noexcept
  28. {
  29. return std::snprintf(first, size, format, value);
  30. }
  31. template <typename T>
  32. to_chars_result to_chars_printf_impl(char* first, char* last, T value, chars_format fmt, int precision)
  33. {
  34. // v % + . + num_digits(INT_MAX) + specifier + null terminator
  35. // 1 + 1 + 10 + 1 + 1
  36. char format[14] {};
  37. std::memcpy(format, "%", 1); // NOLINT : No null terminator is purposeful
  38. std::size_t pos = 1;
  39. // precision of -1 is unspecified
  40. if (precision != -1 && fmt != chars_format::fixed)
  41. {
  42. format[pos] = '.';
  43. ++pos;
  44. const auto unsigned_precision = static_cast<std::uint32_t>(precision);
  45. if (unsigned_precision < 10)
  46. {
  47. boost::charconv::detail::print_1_digit(unsigned_precision, format + pos);
  48. ++pos;
  49. }
  50. else if (unsigned_precision < 100)
  51. {
  52. boost::charconv::detail::print_2_digits(unsigned_precision, format + pos);
  53. pos += 2;
  54. }
  55. else
  56. {
  57. boost::charconv::detail::to_chars_int(format + pos, format + sizeof(format), precision);
  58. pos = std::strlen(format);
  59. }
  60. }
  61. else if (fmt == chars_format::fixed)
  62. {
  63. // Force 0 decimal places
  64. std::memcpy(format + pos, ".0", 2); // NOLINT : No null terminator is purposeful
  65. pos += 2;
  66. }
  67. // Add the type identifier
  68. #ifdef BOOST_CHARCONV_HAS_FLOAT128
  69. BOOST_CHARCONV_IF_CONSTEXPR (std::is_same<T, __float128>::value || std::is_same<T, long double>::value)
  70. {
  71. format[pos] = std::is_same<T, __float128>::value ? 'Q' : 'L';
  72. ++pos;
  73. }
  74. #else
  75. BOOST_CHARCONV_IF_CONSTEXPR (std::is_same<T, long double>::value)
  76. {
  77. format[pos] = 'L';
  78. ++pos;
  79. }
  80. #endif
  81. // Add the format character
  82. switch (fmt)
  83. {
  84. case boost::charconv::chars_format::general:
  85. format[pos] = 'g';
  86. break;
  87. case boost::charconv::chars_format::scientific:
  88. format[pos] = 'e';
  89. break;
  90. case boost::charconv::chars_format::fixed:
  91. format[pos] = 'f';
  92. break;
  93. case boost::charconv::chars_format::hex:
  94. format[pos] = 'a';
  95. break;
  96. }
  97. const auto rv = print_val(first, static_cast<std::size_t>(last - first), format, value);
  98. if (rv <= 0)
  99. {
  100. return {last, static_cast<std::errc>(errno)};
  101. }
  102. return {first + rv, std::errc()};
  103. }
  104. #ifdef BOOST_MSVC
  105. # pragma warning(push)
  106. # pragma warning(disable: 4244) // Implict converion when BOOST_IF_CONSTEXPR expands to if
  107. #elif defined(__GNUC__) && __GNUC__ >= 5
  108. # pragma GCC diagnostic push
  109. # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
  110. # pragma GCC diagnostic ignored "-Wfloat-conversion"
  111. #elif defined(__clang__) && __clang_major__ > 7
  112. # pragma clang diagnostic push
  113. # pragma clang diagnostic ignored "-Wimplicit-float-conversion"
  114. #elif defined(__clang__) && __clang_major__ <= 7
  115. # pragma clang diagnostic push
  116. # pragma clang diagnostic ignored "-Wconversion"
  117. #endif
  118. // We know that the string is in the "C" locale because it would have previously passed through our parser.
  119. // Convert the string into the current locale so that the strto* family of functions
  120. // works correctly for the given locale.
  121. //
  122. // We are operating on our own copy of the buffer, so we are free to modify it.
  123. inline void convert_string_locale(char* buffer) noexcept
  124. {
  125. const auto locale_decimal_point = *std::localeconv()->decimal_point;
  126. if (locale_decimal_point != '.')
  127. {
  128. auto p = std::strchr(buffer, '.');
  129. if (p != nullptr)
  130. {
  131. *p = locale_decimal_point;
  132. }
  133. }
  134. }
  135. template <typename T>
  136. from_chars_result from_chars_strtod_impl(const char* first, const char* last, T& value, char* buffer) noexcept
  137. {
  138. // For strto(f/d)
  139. // Floating point value corresponding to the contents of str on success.
  140. // If the converted value falls out of range of corresponding return type, range error occurs and HUGE_VAL, HUGE_VALF or HUGE_VALL is returned.
  141. // If no conversion can be performed, 0 is returned and *str_end is set to str.
  142. std::memcpy(buffer, first, static_cast<std::size_t>(last - first));
  143. buffer[last - first] = '\0';
  144. convert_string_locale(buffer);
  145. char* str_end;
  146. T return_value {};
  147. from_chars_result r {nullptr, std::errc()};
  148. BOOST_IF_CONSTEXPR (std::is_same<T, float>::value)
  149. {
  150. return_value = std::strtof(buffer, &str_end);
  151. #ifndef __INTEL_LLVM_COMPILER
  152. if (return_value == HUGE_VALF)
  153. #else
  154. if (return_value >= std::numeric_limits<T>::max())
  155. #endif
  156. {
  157. r = {last, std::errc::result_out_of_range};
  158. }
  159. }
  160. else BOOST_IF_CONSTEXPR (std::is_same<T, double>::value)
  161. {
  162. return_value = std::strtod(buffer, &str_end);
  163. #ifndef __INTEL_LLVM_COMPILER
  164. if (return_value == HUGE_VAL)
  165. #else
  166. if (return_value >= std::numeric_limits<T>::max())
  167. #endif
  168. {
  169. r = {last, std::errc::result_out_of_range};
  170. }
  171. }
  172. else BOOST_IF_CONSTEXPR (std::is_same<T, long double>::value)
  173. {
  174. return_value = std::strtold(buffer, &str_end);
  175. #ifndef __INTEL_LLVM_COMPILER
  176. if (return_value == HUGE_VALL)
  177. #else
  178. if (return_value >= std::numeric_limits<T>::max())
  179. #endif
  180. {
  181. r = {last, std::errc::result_out_of_range};
  182. }
  183. }
  184. #ifdef BOOST_CHARCONV_HAS_FLOAT128
  185. else
  186. {
  187. return_value = strtoflt128(buffer, &str_end);
  188. if (return_value == HUGE_VALQ)
  189. {
  190. r = {last, std::errc::result_out_of_range};
  191. }
  192. }
  193. #endif
  194. // Since this is a fallback routine we are safe to check for 0
  195. if (return_value == 0 && str_end == last)
  196. {
  197. r = {first, std::errc::result_out_of_range};
  198. }
  199. if (r)
  200. {
  201. value = return_value;
  202. r = {first + (str_end - buffer), std::errc()};
  203. }
  204. return r;
  205. }
  206. template <typename T>
  207. inline from_chars_result from_chars_strtod(const char* first, const char* last, T& value) noexcept
  208. {
  209. if (last - first < 1024)
  210. {
  211. char buffer[1024];
  212. return from_chars_strtod_impl(first, last, value, buffer);
  213. }
  214. // If the string to be parsed does not fit into the 1024 byte static buffer than we have to allocate a buffer.
  215. // malloc is used here because it does not throw on allocation failure.
  216. char* buffer = static_cast<char*>(std::malloc(static_cast<std::size_t>(last - first + 1)));
  217. if (buffer == nullptr)
  218. {
  219. return {first, std::errc::not_enough_memory};
  220. }
  221. auto r = from_chars_strtod_impl(first, last, value, buffer);
  222. std::free(buffer);
  223. return r;
  224. }
  225. #ifdef BOOST_MSVC
  226. # pragma warning(pop)
  227. #elif defined(__GNUC__) && __GNUC__ >= 5
  228. # pragma GCC diagnostic pop
  229. #elif defined(__clang__)
  230. # pragma clang diagnostic pop
  231. #endif
  232. } //namespace detail
  233. } //namespace charconv
  234. } //namespace boost
  235. #endif //BOOST_FALLBACK_ROUTINES_HPP