1 // (C) Copyright Howard Hinnant 2 // (C) Copyright 2011 Vicente J. Botet Escriba 3 // Use, modification and distribution are subject to the Boost Software License, 4 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt). 6 // 7 8 #ifndef BOOST_CHRONO_IO_DURATION_GET_HPP 9 #define BOOST_CHRONO_IO_DURATION_GET_HPP 10 11 #include <boost/chrono/config.hpp> 12 #include <string> 13 #include <boost/type_traits/is_scalar.hpp> 14 #include <boost/utility/enable_if.hpp> 15 #include <boost/type_traits/is_signed.hpp> 16 #include <boost/mpl/if.hpp> 17 #include <boost/integer/common_factor_rt.hpp> 18 #include <boost/chrono/detail/scan_keyword.hpp> 19 #include <boost/chrono/detail/no_warning/signed_unsigned_cmp.hpp> 20 #include <boost/chrono/process_cpu_clocks.hpp> 21 22 #include <boost/assert.hpp> 23 #include <locale> 24 25 /** 26 * Duration formatting facet for input. 27 */ 28 namespace boost 29 { 30 namespace chrono 31 { 32 33 namespace detail 34 { 35 template <class Rep, bool = is_scalar<Rep>::value> 36 struct duration_io_intermediate 37 { 38 typedef Rep type; 39 }; 40 41 template <class Rep> 42 struct duration_io_intermediate<Rep, true> 43 { 44 typedef typename mpl::if_c<is_floating_point<Rep>::value, long double, typename mpl::if_c< 45 is_signed<Rep>::value, long long, unsigned long long>::type>::type type; 46 }; 47 48 template <class Rep> 49 struct duration_io_intermediate<process_times<Rep>, false> 50 { 51 typedef process_times<typename duration_io_intermediate<Rep>::type> type; 52 }; 53 54 template <typename intermediate_type> reduce(intermediate_type & r,unsigned long long & den,std::ios_base::iostate & err)55 typename enable_if<is_integral<intermediate_type> , bool>::type reduce(intermediate_type& r, 56 unsigned long long& den, std::ios_base::iostate& err) 57 { 58 typedef typename common_type<intermediate_type, unsigned long long>::type common_type_t; 59 60 // Reduce r * num / den 61 common_type_t t = integer::gcd<common_type_t>(common_type_t(r), common_type_t(den)); 62 r /= t; 63 den /= t; 64 if (den != 1) 65 { 66 // Conversion to Period is integral and not exact 67 err |= std::ios_base::failbit; 68 return false; 69 } 70 return true; 71 } 72 template <typename intermediate_type> reduce(intermediate_type &,unsigned long long &,std::ios_base::iostate &)73 typename disable_if<is_integral<intermediate_type> , bool>::type reduce(intermediate_type&, unsigned long long&, 74 std::ios_base::iostate&) 75 { 76 return true; 77 } 78 79 } 80 81 /** 82 * @c duration_get is used to parse a character sequence, extracting 83 * components of a duration into a class duration. 84 * Each get member parses a format as produced by a corresponding format specifier to time_put<>::put. 85 * If the sequence being parsed matches the correct format, the 86 * corresponding member of the class duration argument are set to the 87 * value used to produce the sequence; 88 * otherwise either an error is reported or unspecified values are assigned. 89 * In other words, user confirmation is required for reliable parsing of 90 * user-entered durations, but machine-generated formats can be parsed 91 * reliably. This allows parsers to be aggressive about interpreting user 92 * variations on standard formats. 93 * 94 * If the end iterator is reached during parsing of the get() member 95 * function, the member sets std::ios_base::eofbit in err. 96 */ 97 template <class CharT, class InputIterator = std::istreambuf_iterator<CharT> > 98 class duration_get: public std::locale::facet 99 { 100 public: 101 /** 102 * Type of character the facet is instantiated on. 103 */ 104 typedef CharT char_type; 105 /** 106 * Type of character string passed to member functions. 107 */ 108 typedef std::basic_string<CharT> string_type; 109 /** 110 * Type of iterator used to scan the character buffer. 111 */ 112 typedef InputIterator iter_type; 113 114 /** 115 * Construct a @c duration_get facet. 116 * @param refs 117 * @Effects Construct a @c duration_get facet. 118 * If the @c refs argument is @c 0 then destruction of the object is 119 * delegated to the @c locale, or locales, containing it. This allows 120 * the user to ignore lifetime management issues. On the other had, 121 * if @c refs is @c 1 then the object must be explicitly deleted; 122 * the @c locale will not do so. In this case, the object can be 123 * maintained across the lifetime of multiple locales. 124 */ 125 duration_get(size_t refs=0)126 explicit duration_get(size_t refs = 0) : 127 std::locale::facet(refs) 128 { 129 } 130 131 /** 132 * @param s start input stream iterator 133 * @param end end input stream iterator 134 * @param ios a reference to a ios_base 135 * @param err the ios_base state 136 * @param d the duration 137 * @param pattern begin of the formatting pattern 138 * @param pat_end end of the formatting pattern 139 * 140 * Requires: [pattern,pat_end) shall be a valid range. 141 * 142 * Effects: The function starts by evaluating err = std::ios_base::goodbit. 143 * It then enters a loop, reading zero or more characters from s at 144 * each iteration. Unless otherwise specified below, the loop 145 * terminates when the first of the following conditions holds: 146 * - The expression pattern == pat_end evaluates to true. 147 * - The expression err == std::ios_base::goodbit evaluates to false. 148 * - The expression s == end evaluates to true, in which case the 149 * function evaluates err = std::ios_base::eofbit | std::ios_base::failbit. 150 * - The next element of pattern is equal to '%', followed by a conversion 151 * specifier character, format. 152 * If the number of elements in the range [pattern,pat_end) is not 153 * sufficient to unambiguously determine whether the conversion 154 * specification is complete and valid, the function evaluates 155 * err = std::ios_base::failbit. Otherwise, the function evaluates 156 * s = get_value(s, end, ios, err, r) when the conversion specification is 'v' and 157 * s = get_value(s, end, ios, err, rt) when the conversion specification is 'u'. 158 * If err == std::ios_base::goodbit holds after 159 * the evaluation of the expression, the function increments pattern to 160 * point just past the end of the conversion specification and continues 161 * looping. 162 * - The expression isspace(*pattern, ios.getloc()) evaluates to true, in 163 * which case the function first increments pattern until 164 * pattern == pat_end || !isspace(*pattern, ios.getloc()) evaluates to true, 165 * then advances s until s == end || !isspace(*s, ios.getloc()) is true, 166 * and finally resumes looping. 167 * - The next character read from s matches the element pointed to by 168 * pattern in a case-insensitive comparison, in which case the function 169 * evaluates ++pattern, ++s and continues looping. Otherwise, the function 170 * evaluates err = std::ios_base::failbit. 171 * 172 * Once r and rt are retrieved, 173 * Returns: s 174 */ 175 template <typename Rep, typename Period> get(iter_type s,iter_type end,std::ios_base & ios,std::ios_base::iostate & err,duration<Rep,Period> & d,const char_type * pattern,const char_type * pat_end) const176 iter_type get(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, 177 duration<Rep, Period> &d, const char_type *pattern, const char_type *pat_end) const 178 { 179 if (std::has_facet<duration_units<CharT> >(ios.getloc())) 180 { 181 duration_units<CharT> const&facet = std::use_facet<duration_units<CharT> >(ios.getloc()); 182 return get(facet, s, end, ios, err, d, pattern, pat_end); 183 } 184 else 185 { 186 duration_units_default<CharT> facet; 187 return get(facet, s, end, ios, err, d, pattern, pat_end); 188 } 189 } 190 191 template <typename Rep, typename Period> get(duration_units<CharT> const & facet,iter_type s,iter_type end,std::ios_base & ios,std::ios_base::iostate & err,duration<Rep,Period> & d,const char_type * pattern,const char_type * pat_end) const192 iter_type get(duration_units<CharT> const&facet, iter_type s, iter_type end, std::ios_base& ios, 193 std::ios_base::iostate& err, duration<Rep, Period> &d, const char_type *pattern, const char_type *pat_end) const 194 { 195 196 typedef typename detail::duration_io_intermediate<Rep>::type intermediate_type; 197 intermediate_type r; 198 rt_ratio rt; 199 bool value_found = false, unit_found = false; 200 201 const std::ctype<char_type>& ct = std::use_facet<std::ctype<char_type> >(ios.getloc()); 202 while (pattern != pat_end && err == std::ios_base::goodbit) 203 { 204 if (s == end) 205 { 206 err |= std::ios_base::eofbit; 207 break; 208 } 209 if (ct.narrow(*pattern, 0) == '%') 210 { 211 if (++pattern == pat_end) 212 { 213 err |= std::ios_base::failbit; 214 return s; 215 } 216 char cmd = ct.narrow(*pattern, 0); 217 switch (cmd) 218 { 219 case 'v': 220 { 221 if (value_found) 222 { 223 err |= std::ios_base::failbit; 224 return s; 225 } 226 value_found = true; 227 s = get_value(s, end, ios, err, r); 228 if (err & (std::ios_base::badbit | std::ios_base::failbit)) 229 { 230 return s; 231 } 232 break; 233 } 234 case 'u': 235 { 236 if (unit_found) 237 { 238 err |= std::ios_base::failbit; 239 return s; 240 } 241 unit_found = true; 242 s = get_unit(facet, s, end, ios, err, rt); 243 if (err & (std::ios_base::badbit | std::ios_base::failbit)) 244 { 245 return s; 246 } 247 break; 248 } 249 default: 250 BOOST_ASSERT(false && "Boost::Chrono internal error."); 251 break; 252 } 253 254 ++pattern; 255 } 256 else if (ct.is(std::ctype_base::space, *pattern)) 257 { 258 for (++pattern; pattern != pat_end && ct.is(std::ctype_base::space, *pattern); ++pattern) 259 ; 260 for (; s != end && ct.is(std::ctype_base::space, *s); ++s) 261 ; 262 } 263 else if (ct.toupper(*s) == ct.toupper(*pattern)) 264 { 265 ++s; 266 ++pattern; 267 } 268 else 269 { 270 err |= std::ios_base::failbit; 271 return s; 272 } 273 274 } 275 276 unsigned long long num = rt.num; 277 unsigned long long den = rt.den; 278 279 // r should be multiplied by (num/den) / Period 280 // Reduce (num/den) / Period to lowest terms 281 unsigned long long gcd_n1_n2 = integer::gcd<unsigned long long>(num, Period::num); 282 unsigned long long gcd_d1_d2 = integer::gcd<unsigned long long>(den, Period::den); 283 num /= gcd_n1_n2; 284 den /= gcd_d1_d2; 285 unsigned long long n2 = Period::num / gcd_n1_n2; 286 unsigned long long d2 = Period::den / gcd_d1_d2; 287 if (num > (std::numeric_limits<unsigned long long>::max)() / d2 || den 288 > (std::numeric_limits<unsigned long long>::max)() / n2) 289 { 290 // (num/den) / Period overflows 291 err |= std::ios_base::failbit; 292 return s; 293 } 294 num *= d2; 295 den *= n2; 296 297 typedef typename common_type<intermediate_type, unsigned long long>::type common_type_t; 298 299 // num / den is now factor to multiply by r 300 if (!detail::reduce(r, den, err)) return s; 301 302 if (chrono::detail::gt(r, ( (duration_values<common_type_t>::max)() / num))) 303 { 304 // Conversion to Period overflowed 305 err |= std::ios_base::failbit; 306 return s; 307 } 308 common_type_t t = r * num; 309 t /= den; 310 if (t > duration_values<common_type_t>::zero()) 311 { 312 if ( (duration_values<Rep>::max)() < Rep(t)) 313 { 314 // Conversion to Period overflowed 315 err |= std::ios_base::failbit; 316 return s; 317 } 318 } 319 // Success! Store it. 320 d = duration<Rep, Period> (Rep(t)); 321 322 return s; 323 } 324 325 /** 326 * 327 * @param s start input stream iterator 328 * @param end end input stream iterator 329 * @param ios a reference to a ios_base 330 * @param err the ios_base state 331 * @param d the duration 332 * Stores the duration pattern from the @c duration_unit facet in let say @c str. Last as if 333 * @code 334 * return get(s, end, ios, err, ios, d, str.data(), str.data() + str.size()); 335 * @codeend 336 * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name 337 */ 338 template <typename Rep, typename Period> get(iter_type s,iter_type end,std::ios_base & ios,std::ios_base::iostate & err,duration<Rep,Period> & d) const339 iter_type get(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, 340 duration<Rep, Period> & d) const 341 { 342 if (std::has_facet<duration_units<CharT> >(ios.getloc())) 343 { 344 duration_units<CharT> const&facet = std::use_facet<duration_units<CharT> >(ios.getloc()); 345 std::basic_string<CharT> str = facet.get_pattern(); 346 return get(facet, s, end, ios, err, d, str.data(), str.data() + str.size()); 347 } 348 else 349 { 350 duration_units_default<CharT> facet; 351 std::basic_string<CharT> str = facet.get_pattern(); 352 return get(facet, s, end, ios, err, d, str.data(), str.data() + str.size()); 353 } 354 } 355 356 /** 357 * 358 * @param s start input stream iterator 359 * @param end end input stream iterator 360 * @param ios a reference to a ios_base 361 * @param err the ios_base state 362 * @param r a reference to the duration representation. 363 * @Effects As if 364 * @code 365 * return std::use_facet<std::num_get<cahr_type, iter_type> >(ios.getloc()).get(s, end, ios, err, r); 366 * @endcode 367 * 368 * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name 369 */ 370 template <typename Rep> get_value(iter_type s,iter_type end,std::ios_base & ios,std::ios_base::iostate & err,Rep & r) const371 iter_type get_value(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, Rep& r) const 372 { 373 return std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r); 374 } 375 template <typename Rep> get_value(iter_type s,iter_type end,std::ios_base & ios,std::ios_base::iostate & err,process_times<Rep> & r) const376 iter_type get_value(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, process_times<Rep>& r) const 377 { 378 if (s == end) { 379 err |= std::ios_base::eofbit; 380 return s; 381 } else if (*s != '{') { // mandatory '{' 382 err |= std::ios_base::failbit; 383 return s; 384 } 385 ++s; 386 s = std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r.real); 387 if (s == end) { 388 err |= std::ios_base::eofbit; 389 return s; 390 } else if (*s != ';') { // mandatory ';' 391 err |= std::ios_base::failbit; 392 return s; 393 } 394 ++s; 395 s = std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r.user); 396 if (s == end) { 397 err |= std::ios_base::eofbit; 398 return s; 399 } else if (*s != ';') { // mandatory ';' 400 err |= std::ios_base::failbit; 401 return s; 402 } 403 ++s; 404 s = std::use_facet<std::num_get<CharT, iter_type> >(ios.getloc()).get(s, end, ios, err, r.system); 405 if (s == end) { 406 err |= std::ios_base::eofbit; 407 return s; 408 } else if (*s != '}') { // mandatory '}' 409 err |= std::ios_base::failbit; 410 return s; 411 } 412 return s; 413 } 414 415 /** 416 * 417 * @param s start input stream iterator 418 * @param e end input stream iterator 419 * @param ios a reference to a ios_base 420 * @param err the ios_base state 421 * @param rt a reference to the duration run-time ratio. 422 * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name 423 */ get_unit(iter_type i,iter_type e,std::ios_base & is,std::ios_base::iostate & err,rt_ratio & rt) const424 iter_type get_unit(iter_type i, iter_type e, std::ios_base& is, std::ios_base::iostate& err, rt_ratio &rt) const 425 { 426 if (std::has_facet<duration_units<CharT> >(is.getloc())) 427 { 428 return get_unit(std::use_facet<duration_units<CharT> >(is.getloc()), i, e, is, err, rt); 429 } 430 else 431 { 432 duration_units_default<CharT> facet; 433 return get_unit(facet, i, e, is, err, rt); 434 } 435 } 436 437 get_unit(duration_units<CharT> const & facet,iter_type i,iter_type e,std::ios_base & is,std::ios_base::iostate & err,rt_ratio & rt) const438 iter_type get_unit(duration_units<CharT> const &facet, iter_type i, iter_type e, std::ios_base& is, 439 std::ios_base::iostate& err, rt_ratio &rt) const 440 { 441 442 if (*i == '[') 443 { 444 // parse [N/D]s or [N/D]second or [N/D]seconds format 445 ++i; 446 i = std::use_facet<std::num_get<CharT, iter_type> >(is.getloc()).get(i, e, is, err, rt.num); 447 if ( (err & std::ios_base::failbit) != 0) 448 { 449 return i; 450 } 451 452 if (i == e) 453 { 454 err |= std::ios_base::failbit; 455 return i; 456 } 457 CharT x = *i++; 458 if (x != '/') 459 { 460 err |= std::ios_base::failbit; 461 return i; 462 } 463 i = std::use_facet<std::num_get<CharT, iter_type> >(is.getloc()).get(i, e, is, err, rt.den); 464 if ( (err & std::ios_base::failbit) != 0) 465 { 466 return i; 467 } 468 if (i == e) 469 { 470 err |= std::ios_base::failbit; 471 return i; 472 } 473 if (*i != ']') 474 { 475 err |= std::ios_base::failbit; 476 return i; 477 } 478 ++i; 479 if (i == e) 480 { 481 err |= std::ios_base::failbit; 482 return i; 483 } 484 // parse s or second or seconds 485 return do_get_n_d_valid_unit(facet, i, e, is, err); 486 } 487 else 488 { 489 return do_get_valid_unit(facet, i, e, is, err, rt); 490 } 491 } 492 493 /** 494 * Unique identifier for this type of facet. 495 */ 496 static std::locale::id id; 497 498 /** 499 * @Effects Destroy the facet 500 */ ~duration_get()501 ~duration_get() 502 { 503 } 504 505 protected: 506 507 /** 508 * Extracts the run-time ratio associated to the duration when it is given in prefix form. 509 * 510 * This is an extension point of this facet so that we can take in account other periods that can have a useful 511 * translation in other contexts, as e.g. days and weeks. 512 * 513 * @param facet the duration_units facet 514 * @param i start input stream iterator. 515 * @param e end input stream iterator. 516 * @param ios a reference to a ios_base. 517 * @param err the ios_base state. 518 * @return @c s 519 */ do_get_n_d_valid_unit(duration_units<CharT> const & facet,iter_type i,iter_type e,std::ios_base &,std::ios_base::iostate & err) const520 iter_type do_get_n_d_valid_unit(duration_units<CharT> const &facet, iter_type i, iter_type e, 521 std::ios_base&, std::ios_base::iostate& err) const 522 { 523 // parse SI name, short or long 524 525 const string_type* units = facet.get_n_d_valid_units_start(); 526 const string_type* units_end = facet.get_n_d_valid_units_end(); 527 528 const string_type* k = chrono_detail::scan_keyword(i, e, units, units_end, 529 //~ std::use_facet<std::ctype<CharT> >(loc), 530 err); 531 if (err & (std::ios_base::badbit | std::ios_base::failbit)) 532 { 533 return i; 534 } 535 if (!facet.match_n_d_valid_unit(k)) 536 { 537 err |= std::ios_base::failbit; 538 } 539 return i; 540 } 541 542 /** 543 * Extracts the run-time ratio associated to the duration when it is given in prefix form. 544 * 545 * This is an extension point of this facet so that we can take in account other periods that can have a useful 546 * translation in other contexts, as e.g. days and weeks. 547 * 548 * @param facet the duration_units facet 549 * @param i start input stream iterator. 550 * @param e end input stream iterator. 551 * @param ios a reference to a ios_base. 552 * @param err the ios_base state. 553 * @param rt a reference to the duration run-time ratio. 554 * @Effects 555 * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name. 556 */ do_get_valid_unit(duration_units<CharT> const & facet,iter_type i,iter_type e,std::ios_base &,std::ios_base::iostate & err,rt_ratio & rt) const557 iter_type do_get_valid_unit(duration_units<CharT> const &facet, iter_type i, iter_type e, 558 std::ios_base&, std::ios_base::iostate& err, rt_ratio &rt) const 559 { 560 // parse SI name, short or long 561 562 const string_type* units = facet.get_valid_units_start(); 563 const string_type* units_end = facet.get_valid_units_end(); 564 565 err = std::ios_base::goodbit; 566 const string_type* k = chrono_detail::scan_keyword(i, e, units, units_end, 567 //~ std::use_facet<std::ctype<CharT> >(loc), 568 err); 569 if (err & (std::ios_base::badbit | std::ios_base::failbit)) 570 { 571 return i; 572 } 573 if (!facet.match_valid_unit(k, rt)) 574 { 575 err |= std::ios_base::failbit; 576 } 577 return i; 578 } 579 }; 580 581 /** 582 * Unique identifier for this type of facet. 583 */ 584 template <class CharT, class InputIterator> 585 std::locale::id duration_get<CharT, InputIterator>::id; 586 587 } // chrono 588 } 589 // boost 590 591 #endif // header 592