| 1 | // Copyright Kevlin Henney, 2000-2005. |
| 2 | // Copyright Alexander Nasonov, 2006-2010. |
| 3 | // Copyright Antony Polukhin, 2011-2020. |
| 4 | // |
| 5 | // Distributed under the Boost Software License, Version 1.0. (See |
| 6 | // accompanying file LICENSE_1_0.txt or copy at |
| 7 | // http://www.boost.org/LICENSE_1_0.txt) |
| 8 | // |
| 9 | // what: lexical_cast custom keyword cast |
| 10 | // who: contributed by Kevlin Henney, |
| 11 | // enhanced with contributions from Terje Slettebo, |
| 12 | // with additional fixes and suggestions from Gennaro Prota, |
| 13 | // Beman Dawes, Dave Abrahams, Daryle Walker, Peter Dimov, |
| 14 | // Alexander Nasonov, Antony Polukhin, Justin Viiret, Michael Hofmann, |
| 15 | // Cheng Yang, Matthew Bradbury, David W. Birdsall, Pavel Korzh and other Boosters |
| 16 | // when: November 2000, March 2003, June 2005, June 2006, March 2011 - 2014 |
| 17 | |
| 18 | #ifndef BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP |
| 19 | #define BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP |
| 20 | |
| 21 | #include <boost/config.hpp> |
| 22 | #ifdef BOOST_HAS_PRAGMA_ONCE |
| 23 | # pragma once |
| 24 | #endif |
| 25 | |
| 26 | #include <climits> |
| 27 | #include <cstddef> |
| 28 | #include <string> |
| 29 | #include <cstring> |
| 30 | #include <cstdio> |
| 31 | #include <boost/limits.hpp> |
| 32 | #include <boost/type_traits/conditional.hpp> |
| 33 | #include <boost/static_assert.hpp> |
| 34 | #include <boost/detail/workaround.hpp> |
| 35 | |
| 36 | |
| 37 | #ifndef BOOST_NO_STD_LOCALE |
| 38 | # include <locale> |
| 39 | #else |
| 40 | # ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE |
| 41 | // Getting error at this point means, that your STL library is old/lame/misconfigured. |
| 42 | // If nothing can be done with STL library, define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE, |
| 43 | // but beware: lexical_cast will understand only 'C' locale delimeters and thousands |
| 44 | // separators. |
| 45 | # error "Unable to use <locale> header. Define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE to force " |
| 46 | # error "boost::lexical_cast to use only 'C' locale during conversions." |
| 47 | # endif |
| 48 | #endif |
| 49 | |
| 50 | #include <boost/lexical_cast/detail/lcast_char_constants.hpp> |
| 51 | #include <boost/type_traits/make_unsigned.hpp> |
| 52 | #include <boost/type_traits/is_signed.hpp> |
| 53 | #include <boost/noncopyable.hpp> |
| 54 | |
| 55 | namespace boost |
| 56 | { |
| 57 | namespace detail // lcast_to_unsigned |
| 58 | { |
| 59 | template<class T> |
| 60 | inline |
| 61 | BOOST_DEDUCED_TYPENAME boost::make_unsigned<T>::type lcast_to_unsigned(const T value) BOOST_NOEXCEPT { |
| 62 | typedef BOOST_DEDUCED_TYPENAME boost::make_unsigned<T>::type result_type; |
| 63 | return value < 0 |
| 64 | ? static_cast<result_type>(0u - static_cast<result_type>(value)) |
| 65 | : static_cast<result_type>(value); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | namespace detail // lcast_put_unsigned |
| 70 | { |
| 71 | template <class Traits, class T, class CharT> |
| 72 | class lcast_put_unsigned: boost::noncopyable { |
| 73 | typedef BOOST_DEDUCED_TYPENAME Traits::int_type int_type; |
| 74 | BOOST_DEDUCED_TYPENAME boost::conditional< |
| 75 | (sizeof(unsigned) > sizeof(T)) |
| 76 | , unsigned |
| 77 | , T |
| 78 | >::type m_value; |
| 79 | CharT* m_finish; |
| 80 | CharT const m_czero; |
| 81 | int_type const m_zero; |
| 82 | |
| 83 | public: |
| 84 | lcast_put_unsigned(const T n_param, CharT* finish) BOOST_NOEXCEPT |
| 85 | : m_value(n_param), m_finish(finish) |
| 86 | , m_czero(lcast_char_constants<CharT>::zero), m_zero(Traits::to_int_type(m_czero)) |
| 87 | { |
| 88 | #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS |
| 89 | BOOST_STATIC_ASSERT(!std::numeric_limits<T>::is_signed); |
| 90 | #endif |
| 91 | } |
| 92 | |
| 93 | CharT* convert() { |
| 94 | #ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE |
| 95 | std::locale loc; |
| 96 | if (loc == std::locale::classic()) { |
| 97 | return main_convert_loop(); |
| 98 | } |
| 99 | |
| 100 | typedef std::numpunct<CharT> numpunct; |
| 101 | numpunct const& np = BOOST_USE_FACET(numpunct, loc); |
| 102 | std::string const grouping = np.grouping(); |
| 103 | std::string::size_type const grouping_size = grouping.size(); |
| 104 | |
| 105 | if (!grouping_size || grouping[0] <= 0) { |
| 106 | return main_convert_loop(); |
| 107 | } |
| 108 | |
| 109 | #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS |
| 110 | // Check that ulimited group is unreachable: |
| 111 | BOOST_STATIC_ASSERT(std::numeric_limits<T>::digits10 < CHAR_MAX); |
| 112 | #endif |
| 113 | CharT const thousands_sep = np.thousands_sep(); |
| 114 | std::string::size_type group = 0; // current group number |
| 115 | char last_grp_size = grouping[0]; |
| 116 | char left = last_grp_size; |
| 117 | |
| 118 | do { |
| 119 | if (left == 0) { |
| 120 | ++group; |
| 121 | if (group < grouping_size) { |
| 122 | char const grp_size = grouping[group]; |
| 123 | last_grp_size = (grp_size <= 0 ? static_cast<char>(CHAR_MAX) : grp_size); |
| 124 | } |
| 125 | |
| 126 | left = last_grp_size; |
| 127 | --m_finish; |
| 128 | Traits::assign(*m_finish, thousands_sep); |
| 129 | } |
| 130 | |
| 131 | --left; |
| 132 | } while (main_convert_iteration()); |
| 133 | |
| 134 | return m_finish; |
| 135 | #else |
| 136 | return main_convert_loop(); |
| 137 | #endif |
| 138 | } |
| 139 | |
| 140 | private: |
| 141 | inline bool main_convert_iteration() BOOST_NOEXCEPT { |
| 142 | --m_finish; |
| 143 | int_type const digit = static_cast<int_type>(m_value % 10U); |
| 144 | Traits::assign(*m_finish, Traits::to_char_type(m_zero + digit)); |
| 145 | m_value /= 10; |
| 146 | return !!m_value; // suppressing warnings |
| 147 | } |
| 148 | |
| 149 | inline CharT* main_convert_loop() BOOST_NOEXCEPT { |
| 150 | while (main_convert_iteration()); |
| 151 | return m_finish; |
| 152 | } |
| 153 | }; |
| 154 | } |
| 155 | |
| 156 | namespace detail // lcast_ret_unsigned |
| 157 | { |
| 158 | template <class Traits, class T, class CharT> |
| 159 | class lcast_ret_unsigned: boost::noncopyable { |
| 160 | bool m_multiplier_overflowed; |
| 161 | T m_multiplier; |
| 162 | T& m_value; |
| 163 | const CharT* const m_begin; |
| 164 | const CharT* m_end; |
| 165 | |
| 166 | public: |
| 167 | lcast_ret_unsigned(T& value, const CharT* const begin, const CharT* end) BOOST_NOEXCEPT |
| 168 | : m_multiplier_overflowed(false), m_multiplier(1), m_value(value), m_begin(begin), m_end(end) |
| 169 | { |
| 170 | #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS |
| 171 | BOOST_STATIC_ASSERT(!std::numeric_limits<T>::is_signed); |
| 172 | |
| 173 | // GCC when used with flag -std=c++0x may not have std::numeric_limits |
| 174 | // specializations for __int128 and unsigned __int128 types. |
| 175 | // Try compilation with -std=gnu++0x or -std=gnu++11. |
| 176 | // |
| 177 | // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40856 |
| 178 | BOOST_STATIC_ASSERT_MSG(std::numeric_limits<T>::is_specialized, |
| 179 | "std::numeric_limits are not specialized for integral type passed to boost::lexical_cast" |
| 180 | ); |
| 181 | #endif |
| 182 | } |
| 183 | |
| 184 | inline bool convert() { |
| 185 | CharT const czero = lcast_char_constants<CharT>::zero; |
| 186 | --m_end; |
| 187 | m_value = static_cast<T>(0); |
| 188 | |
| 189 | if (m_begin > m_end || *m_end < czero || *m_end >= czero + 10) |
| 190 | return false; |
| 191 | m_value = static_cast<T>(*m_end - czero); |
| 192 | --m_end; |
| 193 | |
| 194 | #ifdef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE |
| 195 | return main_convert_loop(); |
| 196 | #else |
| 197 | std::locale loc; |
| 198 | if (loc == std::locale::classic()) { |
| 199 | return main_convert_loop(); |
| 200 | } |
| 201 | |
| 202 | typedef std::numpunct<CharT> numpunct; |
| 203 | numpunct const& np = BOOST_USE_FACET(numpunct, loc); |
| 204 | std::string const& grouping = np.grouping(); |
| 205 | std::string::size_type const grouping_size = grouping.size(); |
| 206 | |
| 207 | /* According to Programming languages - C++ |
| 208 | * we MUST check for correct grouping |
| 209 | */ |
| 210 | if (!grouping_size || grouping[0] <= 0) { |
| 211 | return main_convert_loop(); |
| 212 | } |
| 213 | |
| 214 | unsigned char current_grouping = 0; |
| 215 | CharT const thousands_sep = np.thousands_sep(); |
| 216 | char remained = static_cast<char>(grouping[current_grouping] - 1); |
| 217 | |
| 218 | for (;m_end >= m_begin; --m_end) |
| 219 | { |
| 220 | if (remained) { |
| 221 | if (!main_convert_iteration()) { |
| 222 | return false; |
| 223 | } |
| 224 | --remained; |
| 225 | } else { |
| 226 | if ( !Traits::eq(*m_end, thousands_sep) ) //|| begin == end ) return false; |
| 227 | { |
| 228 | /* |
| 229 | * According to Programming languages - C++ |
| 230 | * Digit grouping is checked. That is, the positions of discarded |
| 231 | * separators is examined for consistency with |
| 232 | * use_facet<numpunct<charT> >(loc ).grouping() |
| 233 | * |
| 234 | * BUT what if there is no separators at all and grouping() |
| 235 | * is not empty? Well, we have no extraced separators, so we |
| 236 | * won`t check them for consistency. This will allow us to |
| 237 | * work with "C" locale from other locales |
| 238 | */ |
| 239 | return main_convert_loop(); |
| 240 | } else { |
| 241 | if (m_begin == m_end) return false; |
| 242 | if (current_grouping < grouping_size - 1) ++current_grouping; |
| 243 | remained = grouping[current_grouping]; |
| 244 | } |
| 245 | } |
| 246 | } /*for*/ |
| 247 | |
| 248 | return true; |
| 249 | #endif |
| 250 | } |
| 251 | |
| 252 | private: |
| 253 | // Iteration that does not care about grouping/separators and assumes that all |
| 254 | // input characters are digits |
| 255 | inline bool main_convert_iteration() BOOST_NOEXCEPT { |
| 256 | CharT const czero = lcast_char_constants<CharT>::zero; |
| 257 | T const maxv = (std::numeric_limits<T>::max)(); |
| 258 | |
| 259 | m_multiplier_overflowed = m_multiplier_overflowed || (maxv/10 < m_multiplier); |
| 260 | m_multiplier = static_cast<T>(m_multiplier * 10); |
| 261 | |
| 262 | T const dig_value = static_cast<T>(*m_end - czero); |
| 263 | T const new_sub_value = static_cast<T>(m_multiplier * dig_value); |
| 264 | |
| 265 | // We must correctly handle situations like `000000000000000000000000000001`. |
| 266 | // So we take care of overflow only if `dig_value` is not '0'. |
| 267 | if (*m_end < czero || *m_end >= czero + 10 // checking for correct digit |
| 268 | || (dig_value && ( // checking for overflow of ... |
| 269 | m_multiplier_overflowed // ... multiplier |
| 270 | || static_cast<T>(maxv / dig_value) < m_multiplier // ... subvalue |
| 271 | || static_cast<T>(maxv - new_sub_value) < m_value // ... whole expression |
| 272 | )) |
| 273 | ) return false; |
| 274 | |
| 275 | m_value = static_cast<T>(m_value + new_sub_value); |
| 276 | |
| 277 | return true; |
| 278 | } |
| 279 | |
| 280 | bool main_convert_loop() BOOST_NOEXCEPT { |
| 281 | for ( ; m_end >= m_begin; --m_end) { |
| 282 | if (!main_convert_iteration()) { |
| 283 | return false; |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | return true; |
| 288 | } |
| 289 | }; |
| 290 | } |
| 291 | } // namespace boost |
| 292 | |
| 293 | #endif // BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP |
| 294 | |
| 295 | |