Line data Source code
1 : // Copyright Kevlin Henney, 2000-2005.
2 : // Copyright Alexander Nasonov, 2006-2010.
3 : // Copyright Antony Polukhin, 2011-2025.
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 <type_traits>
32 : #include <boost/limits.hpp>
33 : #include <boost/config/workaround.hpp>
34 : #include <boost/lexical_cast/detail/type_traits.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/core/noncopyable.hpp>
52 :
53 : namespace boost
54 : {
55 : namespace detail // lcast_to_unsigned
56 : {
57 : template<class T>
58 : #if defined(__clang__) && (__clang_major__ > 3 || __clang_minor__ > 6)
59 : __attribute__((no_sanitize("unsigned-integer-overflow")))
60 : #endif
61 : inline
62 : typename boost::detail::lcast::make_unsigned<T>::type lcast_to_unsigned(const T value) noexcept {
63 : typedef typename boost::detail::lcast::make_unsigned<T>::type result_type;
64 : return value < 0
65 : ? static_cast<result_type>(0u - static_cast<result_type>(value))
66 : : static_cast<result_type>(value);
67 : }
68 : }
69 :
70 : namespace detail // lcast_put_unsigned
71 : {
72 : template <class Traits, class T, class CharT>
73 : class lcast_put_unsigned: boost::noncopyable {
74 : typedef typename Traits::int_type int_type;
75 : typename std::conditional<
76 : (sizeof(unsigned) > sizeof(T))
77 : , unsigned
78 : , T
79 : >::type m_value;
80 : CharT* m_finish;
81 : CharT const m_czero;
82 : int_type const m_zero;
83 :
84 : public:
85 : lcast_put_unsigned(const T n_param, CharT* finish) noexcept
86 : : m_value(n_param), m_finish(finish)
87 : , m_czero(lcast_char_constants<CharT>::zero), m_zero(Traits::to_int_type(m_czero))
88 : {
89 : #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
90 : static_assert(!std::numeric_limits<T>::is_signed, "");
91 : #endif
92 : }
93 :
94 : CharT* convert() {
95 : #ifndef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
96 : std::locale loc;
97 : if (loc == std::locale::classic()) {
98 : return main_convert_loop();
99 : }
100 :
101 : typedef std::numpunct<CharT> numpunct;
102 : numpunct const& np = BOOST_USE_FACET(numpunct, loc);
103 : std::string const grouping = np.grouping();
104 : std::string::size_type const grouping_size = grouping.size();
105 :
106 : if (!grouping_size || grouping[0] <= 0) {
107 : return main_convert_loop();
108 : }
109 :
110 : #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
111 : // Check that ulimited group is unreachable:
112 : static_assert(std::numeric_limits<T>::digits10 < CHAR_MAX, "");
113 : #endif
114 : CharT const thousands_sep = np.thousands_sep();
115 : std::string::size_type group = 0; // current group number
116 : char last_grp_size = grouping[0];
117 : char left = last_grp_size;
118 :
119 : do {
120 : if (left == 0) {
121 : ++group;
122 : if (group < grouping_size) {
123 : char const grp_size = grouping[group];
124 : last_grp_size = (grp_size <= 0 ? static_cast<char>(CHAR_MAX) : grp_size);
125 : }
126 :
127 : left = last_grp_size;
128 : --m_finish;
129 : Traits::assign(*m_finish, thousands_sep);
130 : }
131 :
132 : --left;
133 : } while (main_convert_iteration());
134 :
135 : return m_finish;
136 : #else
137 : return main_convert_loop();
138 : #endif
139 : }
140 :
141 : private:
142 : inline bool main_convert_iteration() noexcept {
143 : --m_finish;
144 : int_type const digit = static_cast<int_type>(m_value % 10U);
145 : Traits::assign(*m_finish, Traits::to_char_type(m_zero + digit));
146 : m_value /= 10;
147 : return !!m_value; // suppressing warnings
148 : }
149 :
150 : inline CharT* main_convert_loop() noexcept {
151 : while (main_convert_iteration());
152 : return m_finish;
153 : }
154 : };
155 : }
156 :
157 : namespace detail // lcast_ret_unsigned
158 : {
159 : template <class Traits, class T, class CharT>
160 : class lcast_ret_unsigned: boost::noncopyable {
161 : bool m_multiplier_overflowed;
162 : T m_multiplier;
163 : T& m_value;
164 : const CharT* const m_begin;
165 : const CharT* m_end;
166 :
167 : public:
168 72 : lcast_ret_unsigned(T& value, const CharT* const begin, const CharT* end) noexcept
169 36 : : m_multiplier_overflowed(false), m_multiplier(1), m_value(value), m_begin(begin), m_end(end)
170 36 : {
171 : #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
172 : static_assert(!std::numeric_limits<T>::is_signed, "");
173 :
174 : // GCC when used with flag -std=c++0x may not have std::numeric_limits
175 : // specializations for __int128 and unsigned __int128 types.
176 : // Try compilation with -std=gnu++0x or -std=gnu++11.
177 : //
178 : // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40856
179 : static_assert(std::numeric_limits<T>::is_specialized,
180 : "std::numeric_limits are not specialized for integral type passed to boost::lexical_cast"
181 : );
182 : #endif
183 72 : }
184 :
185 36 : inline bool convert() {
186 36 : CharT const czero = lcast_char_constants<CharT>::zero;
187 36 : --m_end;
188 36 : m_value = static_cast<T>(0);
189 :
190 36 : if (m_begin > m_end || *m_end < czero || *m_end >= czero + 10)
191 0 : return false;
192 36 : m_value = static_cast<T>(*m_end - czero);
193 36 : --m_end;
194 :
195 : #ifdef BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
196 : return main_convert_loop();
197 : #else
198 36 : std::locale loc;
199 36 : if (loc == std::locale::classic()) {
200 36 : return main_convert_loop();
201 : }
202 :
203 : typedef std::numpunct<CharT> numpunct;
204 0 : numpunct const& np = BOOST_USE_FACET(numpunct, loc);
205 0 : std::string const& grouping = np.grouping();
206 0 : std::string::size_type const grouping_size = grouping.size();
207 :
208 : /* According to Programming languages - C++
209 : * we MUST check for correct grouping
210 : */
211 0 : if (!grouping_size || grouping[0] <= 0) {
212 0 : return main_convert_loop();
213 : }
214 :
215 0 : unsigned char current_grouping = 0;
216 0 : CharT const thousands_sep = np.thousands_sep();
217 0 : char remained = static_cast<char>(grouping[current_grouping] - 1);
218 :
219 0 : for (;m_end >= m_begin; --m_end)
220 : {
221 0 : if (remained) {
222 0 : if (!main_convert_iteration()) {
223 0 : return false;
224 : }
225 0 : --remained;
226 0 : } else {
227 0 : if ( !Traits::eq(*m_end, thousands_sep) ) //|| begin == end ) return false;
228 : {
229 : /*
230 : * According to Programming languages - C++
231 : * Digit grouping is checked. That is, the positions of discarded
232 : * separators is examined for consistency with
233 : * use_facet<numpunct<charT> >(loc ).grouping()
234 : *
235 : * BUT what if there is no separators at all and grouping()
236 : * is not empty? Well, we have no extraced separators, so we
237 : * won`t check them for consistency. This will allow us to
238 : * work with "C" locale from other locales
239 : */
240 0 : return main_convert_loop();
241 : } else {
242 0 : if (m_begin == m_end) return false;
243 0 : if (current_grouping < grouping_size - 1) ++current_grouping;
244 0 : remained = grouping[current_grouping];
245 : }
246 : }
247 0 : } /*for*/
248 :
249 0 : return true;
250 : #endif
251 36 : }
252 :
253 : private:
254 : // Iteration that does not care about grouping/separators and assumes that all
255 : // input characters are digits
256 : #if defined(__clang__) && (__clang_major__ > 3 || __clang_minor__ > 6)
257 : __attribute__((no_sanitize("unsigned-integer-overflow")))
258 : #endif
259 48 : inline bool main_convert_iteration() noexcept {
260 48 : CharT const czero = lcast_char_constants<CharT>::zero;
261 48 : T const maxv = (std::numeric_limits<T>::max)();
262 :
263 48 : m_multiplier_overflowed = m_multiplier_overflowed || (maxv/10 < m_multiplier);
264 48 : m_multiplier = static_cast<T>(m_multiplier * 10);
265 :
266 48 : T const dig_value = static_cast<T>(*m_end - czero);
267 48 : T const new_sub_value = static_cast<T>(m_multiplier * dig_value);
268 :
269 : // We must correctly handle situations like `000000000000000000000000000001`.
270 : // So we take care of overflow only if `dig_value` is not '0'.
271 74 : if (*m_end < czero || *m_end >= czero + 10 // checking for correct digit
272 48 : || (dig_value && ( // checking for overflow of ...
273 26 : m_multiplier_overflowed // ... multiplier
274 26 : || static_cast<T>(maxv / dig_value) < m_multiplier // ... subvalue
275 26 : || static_cast<T>(maxv - new_sub_value) < m_value // ... whole expression
276 : ))
277 0 : ) return false;
278 :
279 48 : m_value = static_cast<T>(m_value + new_sub_value);
280 :
281 48 : return true;
282 48 : }
283 :
284 36 : bool main_convert_loop() noexcept {
285 84 : for ( ; m_end >= m_begin; --m_end) {
286 48 : if (!main_convert_iteration()) {
287 0 : return false;
288 : }
289 48 : }
290 :
291 36 : return true;
292 36 : }
293 : };
294 : }
295 : } // namespace boost
296 :
297 : #endif // BOOST_LEXICAL_CAST_DETAIL_LCAST_UNSIGNED_CONVERTERS_HPP
298 :
|