duration_get.hpp 19 KB

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