1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3//
4// Distributed under the Boost Software License, Version 1.0.
5// https://www.boost.org/LICENSE_1_0.txt
6
7#include <boost/locale/formatting.hpp>
8#include "boost/locale/icu/all_generator.hpp"
9#include "boost/locale/icu/cdata.hpp"
10#include "boost/locale/icu/formatter.hpp"
11#include "boost/locale/icu/formatters_cache.hpp"
12#include <algorithm>
13#include <ios>
14#include <limits>
15#include <locale>
16#include <string>
17#include <type_traits>
18
19namespace boost { namespace locale { namespace impl_icu {
20
21 namespace detail {
22 template<typename T, bool integer = std::numeric_limits<T>::is_integer>
23 struct icu_format_type;
24
25 template<typename T>
26 struct icu_format_type<T, true> {
27 // ICU supports 32 and 64 bit ints, use the former as long as it fits, else the latter
28 typedef typename std::conditional<std::numeric_limits<T>::digits <= 31, int32_t, int64_t>::type type;
29 };
30 template<typename T>
31 struct icu_format_type<T, false> {
32 // Only float type ICU supports is double
33 typedef double type;
34 };
35
36 // ICU does not support uint64_t values so fall back to the parent/std formatting
37 // if the number is to large to fit into an int64_t
38 template<typename T,
39 bool BigUInt = !std::numeric_limits<T>::is_signed && std::numeric_limits<T>::is_integer
40 && (sizeof(T) >= sizeof(uint64_t))>
41 struct use_parent_traits {
42 static bool use(T /*v*/) { return false; }
43 };
44 template<typename T>
45 struct use_parent_traits<T, true> {
46 static bool use(T v) { return v > static_cast<T>(std::numeric_limits<int64_t>::max()); }
47 };
48
49 template<typename ValueType>
50 static bool use_parent(std::ios_base& ios, ValueType v)
51 {
52 const uint64_t flg = ios_info::get(ios).display_flags();
53 if(flg == flags::posix)
54 return true;
55 if(use_parent_traits<ValueType>::use(v))
56 return true;
57
58 if(!std::numeric_limits<ValueType>::is_integer)
59 return false;
60
61 if(flg == flags::number && (ios.flags() & std::ios_base::basefield) != std::ios_base::dec)
62 return true;
63 return false;
64 }
65 } // namespace detail
66
67 template<typename CharType>
68 class num_format : public std::num_put<CharType> {
69 public:
70 typedef typename std::num_put<CharType>::iter_type iter_type;
71 typedef std::basic_string<CharType> string_type;
72 typedef formatter<CharType> formatter_type;
73
74 num_format(const cdata& d, size_t refs = 0) : std::num_put<CharType>(refs), loc_(d.locale()), enc_(d.encoding())
75 {}
76
77 protected:
78 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, long val) const override
79 {
80 return do_real_put(out, ios, fill, val);
81 }
82 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, unsigned long val) const override
83 {
84 return do_real_put(out, ios, fill, val);
85 }
86 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, double val) const override
87 {
88 return do_real_put(out, ios, fill, val);
89 }
90 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, long double val) const override
91 {
92 return do_real_put(out, ios, fill, val);
93 }
94
95 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, long long val) const override
96 {
97 return do_real_put(out, ios, fill, val);
98 }
99 iter_type do_put(iter_type out, std::ios_base& ios, CharType fill, unsigned long long val) const override
100 {
101 return do_real_put(out, ios, fill, val);
102 }
103
104 private:
105 template<typename ValueType>
106 iter_type do_real_put(iter_type out, std::ios_base& ios, CharType fill, ValueType val) const
107 {
108 if(detail::use_parent(ios, val))
109 return std::num_put<CharType>::do_put(out, ios, fill, val);
110
111 const auto formatter = formatter_type::create(ios, loc_, enc_);
112
113 if(!formatter)
114 return std::num_put<CharType>::do_put(out, ios, fill, val);
115
116 size_t code_points;
117 typedef typename detail::icu_format_type<ValueType>::type icu_type;
118 const string_type& str = formatter->format(static_cast<icu_type>(val), code_points);
119 std::streamsize on_left = 0, on_right = 0, points = code_points;
120 if(points < ios.width()) {
121 std::streamsize n = ios.width() - points;
122
123 std::ios_base::fmtflags flags = ios.flags() & std::ios_base::adjustfield;
124
125 // We do not really know internal point, so we assume that it does not
126 // exist. So according to the standard field should be right aligned
127 if(flags != std::ios_base::left)
128 on_left = n;
129 on_right = n - on_left;
130 }
131 while(on_left > 0) {
132 *out++ = fill;
133 on_left--;
134 }
135 std::copy(str.begin(), str.end(), out);
136 while(on_right > 0) {
137 *out++ = fill;
138 on_right--;
139 }
140 ios.width(wide: 0);
141 return out;
142 }
143
144 icu::Locale loc_;
145 std::string enc_;
146
147 }; // num_format
148
149 template<typename CharType>
150 class num_parse : public std::num_get<CharType> {
151 public:
152 num_parse(const cdata& d, size_t refs = 0) : std::num_get<CharType>(refs), loc_(d.locale()), enc_(d.encoding())
153 {}
154
155 protected:
156 typedef typename std::num_get<CharType>::iter_type iter_type;
157 typedef std::basic_string<CharType> string_type;
158 typedef formatter<CharType> formatter_type;
159 typedef std::basic_istream<CharType> stream_type;
160
161 iter_type
162 do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, long& val) const override
163 {
164 return do_real_get(in, end, ios, err, val);
165 }
166
167 iter_type do_get(iter_type in,
168 iter_type end,
169 std::ios_base& ios,
170 std::ios_base::iostate& err,
171 unsigned short& val) const override
172 {
173 return do_real_get(in, end, ios, err, val);
174 }
175
176 iter_type do_get(iter_type in,
177 iter_type end,
178 std::ios_base& ios,
179 std::ios_base::iostate& err,
180 unsigned int& val) const override
181 {
182 return do_real_get(in, end, ios, err, val);
183 }
184
185 iter_type do_get(iter_type in,
186 iter_type end,
187 std::ios_base& ios,
188 std::ios_base::iostate& err,
189 unsigned long& val) const override
190 {
191 return do_real_get(in, end, ios, err, val);
192 }
193
194 iter_type
195 do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, float& val) const override
196 {
197 return do_real_get(in, end, ios, err, val);
198 }
199
200 iter_type
201 do_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, double& val) const override
202 {
203 return do_real_get(in, end, ios, err, val);
204 }
205
206 iter_type do_get(iter_type in,
207 iter_type end,
208 std::ios_base& ios,
209 std::ios_base::iostate& err,
210 long double& val) const override
211 {
212 return do_real_get(in, end, ios, err, val);
213 }
214
215 iter_type do_get(iter_type in,
216 iter_type end,
217 std::ios_base& ios,
218 std::ios_base::iostate& err,
219 long long& val) const override
220 {
221 return do_real_get(in, end, ios, err, val);
222 }
223
224 iter_type do_get(iter_type in,
225 iter_type end,
226 std::ios_base& ios,
227 std::ios_base::iostate& err,
228 unsigned long long& val) const override
229 {
230 return do_real_get(in, end, ios, err, val);
231 }
232
233 private:
234 //
235 // This is not really an efficient solution, but it works
236 //
237 template<typename ValueType>
238 iter_type
239 do_real_get(iter_type in, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, ValueType& val) const
240 {
241 stream_type* stream_ptr = dynamic_cast<stream_type*>(&ios);
242 if(!stream_ptr || detail::use_parent(ios, ValueType(0)))
243 return std::num_get<CharType>::do_get(in, end, ios, err, val);
244
245 const auto formatter = formatter_type::create(ios, loc_, enc_);
246 if(!formatter)
247 return std::num_get<CharType>::do_get(in, end, ios, err, val);
248
249 string_type tmp;
250 tmp.reserve(64);
251
252 CharType c;
253 while(in != end && (((c = *in) <= 32 && (c > 0)) || c == 127)) // Assuming that ASCII is a subset
254 ++in;
255
256 while(tmp.size() < 4096 && in != end && *in != '\n')
257 tmp += *in++;
258
259 typedef typename detail::icu_format_type<ValueType>::type icu_type;
260 icu_type value;
261 size_t parsed_chars;
262
263 if((parsed_chars = formatter->parse(tmp, value)) == 0 || !is_losless_castable<ValueType>(value))
264 err |= std::ios_base::failbit;
265 else
266 val = static_cast<ValueType>(value);
267
268 for(size_t n = tmp.size(); n > parsed_chars; n--)
269 stream_ptr->putback(tmp[n - 1]);
270
271 in = iter_type(*stream_ptr);
272
273 if(in == end)
274 err |= std::ios_base::eofbit;
275 return in;
276 }
277
278 BOOST_LOCALE_START_CONST_CONDITION
279 template<typename TargetType, typename SrcType>
280 bool is_losless_castable(SrcType v) const
281 {
282 typedef std::numeric_limits<TargetType> target_limits;
283 typedef std::numeric_limits<SrcType> casted_limits;
284 if(v < 0 && !target_limits::is_signed)
285 return false;
286
287 constexpr TargetType max_val = target_limits::max();
288
289 if(sizeof(SrcType) > sizeof(TargetType) && v > static_cast<SrcType>(max_val))
290 return false;
291
292 if(target_limits::is_integer == casted_limits::is_integer)
293 return true;
294
295 if(target_limits::is_integer) { // and source is not
296 if(static_cast<SrcType>(static_cast<TargetType>(v)) != v)
297 return false;
298 }
299 return true;
300 }
301 BOOST_LOCALE_END_CONST_CONDITION
302
303 icu::Locale loc_;
304 std::string enc_;
305 };
306
307 template<typename CharType>
308 std::locale install_formatting_facets(const std::locale& in, const cdata& cd)
309 {
310 std::locale tmp = std::locale(in, new num_format<CharType>(cd));
311 if(!std::has_facet<formatters_cache>(loc: in))
312 tmp = std::locale(tmp, new formatters_cache(cd.locale()));
313 return tmp;
314 }
315
316 template<typename CharType>
317 std::locale install_parsing_facets(const std::locale& in, const cdata& cd)
318 {
319 std::locale tmp = std::locale(in, new num_parse<CharType>(cd));
320 if(!std::has_facet<formatters_cache>(loc: in))
321 tmp = std::locale(tmp, new formatters_cache(cd.locale()));
322 return tmp;
323 }
324
325 std::locale create_formatting(const std::locale& in, const cdata& cd, char_facet_t type)
326 {
327 switch(type) {
328 case char_facet_t::nochar: break;
329 case char_facet_t::char_f: return install_formatting_facets<char>(in, cd);
330 case char_facet_t::wchar_f: return install_formatting_facets<wchar_t>(in, cd);
331#ifdef __cpp_char8_t
332 case char_facet_t::char8_f: break; // std-facet not available (yet)
333#endif
334#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
335 case char_facet_t::char16_f: return install_formatting_facets<char16_t>(in, cd);
336#endif
337#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
338 case char_facet_t::char32_f: return install_formatting_facets<char32_t>(in, cd);
339#endif
340 }
341 return in;
342 }
343
344 std::locale create_parsing(const std::locale& in, const cdata& cd, char_facet_t type)
345 {
346 switch(type) {
347 case char_facet_t::nochar: break;
348 case char_facet_t::char_f: return install_parsing_facets<char>(in, cd);
349 case char_facet_t::wchar_f: return install_parsing_facets<wchar_t>(in, cd);
350#ifdef __cpp_char8_t
351 case char_facet_t::char8_f: break; // std-facet not available (yet)
352#endif
353#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
354 case char_facet_t::char16_f: return install_parsing_facets<char16_t>(in, cd);
355#endif
356#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
357 case char_facet_t::char32_f: return install_parsing_facets<char32_t>(in, cd);
358#endif
359 }
360 return in;
361 }
362
363}}} // namespace boost::locale::impl_icu
364
365// boostinspect:nominmax
366

source code of boost/libs/locale/src/boost/locale/icu/numeric.cpp