fallback_routines.hpp 7.2 KB

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