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