1 
2 //  chrono_io
3 //
4 //  (C) Copyright Howard Hinnant
5 //  (C) Copyright 2010 Vicente J. Botet Escriba
6 //  Use, modification and distribution are subject to the Boost Software License,
7 //  Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 //  http://www.boost.org/LICENSE_1_0.txt).
9 //
10 // This code was adapted by Vicente from Howard Hinnant's experimental work
11 // on chrono i/o under lvm/libc++  to Boost
12 
13 #ifndef BOOST_CHRONO_IO_V1_CHRONO_IO_HPP
14 #define BOOST_CHRONO_IO_V1_CHRONO_IO_HPP
15 
16 #include <boost/chrono/chrono.hpp>
17 #include <boost/chrono/process_cpu_clocks.hpp>
18 #include <boost/chrono/thread_clock.hpp>
19 #include <boost/chrono/clock_string.hpp>
20 #include <boost/ratio/ratio_io.hpp>
21 #include <locale>
22 #include <boost/type_traits/is_scalar.hpp>
23 #include <boost/type_traits/is_signed.hpp>
24 #include <boost/mpl/if.hpp>
25 #include <boost/integer/common_factor_rt.hpp>
26 #include <boost/chrono/detail/scan_keyword.hpp>
27 #include <boost/utility/enable_if.hpp>
28 #include <boost/chrono/detail/no_warning/signed_unsigned_cmp.hpp>
29 
30 namespace boost
31 {
32 
33 namespace chrono
34 {
35 
36 template <class CharT>
37 class duration_punct
38     : public std::locale::facet
39 {
40 public:
41     typedef std::basic_string<CharT> string_type;
42     enum {use_long, use_short};
43 
44 private:
45     bool use_short_;
46     string_type long_seconds_;
47     string_type long_minutes_;
48     string_type long_hours_;
49     string_type short_seconds_;
50     string_type short_minutes_;
51     string_type short_hours_;
52 
53     template <class Period>
short_name(Period) const54         string_type short_name(Period) const
55             {return ::boost::ratio_string<Period, CharT>::short_name() + short_seconds_;}
56 
short_name(ratio<1>) const57     string_type short_name(ratio<1>) const    {return short_seconds_;}
short_name(ratio<60>) const58     string_type short_name(ratio<60>) const   {return short_minutes_;}
short_name(ratio<3600>) const59     string_type short_name(ratio<3600>) const {return short_hours_;}
60 
61     template <class Period>
long_name(Period) const62         string_type long_name(Period) const
63             {return ::boost::ratio_string<Period, CharT>::long_name() + long_seconds_;}
64 
long_name(ratio<1>) const65     string_type long_name(ratio<1>) const    {return long_seconds_;}
long_name(ratio<60>) const66     string_type long_name(ratio<60>) const   {return long_minutes_;}
long_name(ratio<3600>) const67     string_type long_name(ratio<3600>) const {return long_hours_;}
68 
69     void init_C();
70 public:
71     static std::locale::id id;
72 
duration_punct(int use=use_long)73     explicit duration_punct(int use = use_long)
74         : use_short_(use==use_short) {init_C();}
75 
76     duration_punct(int use,
77         const string_type& long_seconds, const string_type& long_minutes,
78         const string_type& long_hours, const string_type& short_seconds,
79         const string_type& short_minutes, const string_type& short_hours);
80 
81     duration_punct(int use, const duration_punct& d);
82 
83     template <class Period>
short_name() const84         string_type short_name() const
85             {return short_name(typename Period::type());}
86 
87     template <class Period>
long_name() const88         string_type long_name() const
89             {return long_name(typename Period::type());}
90 
91     template <class Period>
plural() const92         string_type plural() const
93             {return long_name(typename Period::type());}
94 
95     template <class Period>
singular() const96         string_type singular() const
97     {
98       return string_type(long_name(typename Period::type()), 0, long_name(typename Period::type()).size()-1);
99     }
100 
101     template <class Period>
name() const102         string_type name() const
103     {
104       if (use_short_) return short_name<Period>();
105       else {
106         return long_name<Period>();
107       }
108     }
109     template <class Period, class D>
name(D v) const110       string_type name(D v) const
111       {
112         if (use_short_) return short_name<Period>();
113         else
114         {
115           if (v==-1 || v==1)
116             return singular<Period>();
117           else
118             return plural<Period>();
119         }
120       }
121 
is_short_name() const122     bool is_short_name() const {return use_short_;}
is_long_name() const123     bool is_long_name() const {return !use_short_;}
124 };
125 
126 template <class CharT>
127 std::locale::id
128 duration_punct<CharT>::id;
129 
130 template <class CharT>
131 void
init_C()132 duration_punct<CharT>::init_C()
133 {
134     short_seconds_ = CharT('s');
135     short_minutes_ = CharT('m');
136     short_hours_ = CharT('h');
137     const CharT s[] = {'s', 'e', 'c', 'o', 'n', 'd', 's'};
138     const CharT m[] = {'m', 'i', 'n', 'u', 't', 'e', 's'};
139     const CharT h[] = {'h', 'o', 'u', 'r', 's'};
140     long_seconds_.assign(s, s + sizeof(s)/sizeof(s[0]));
141     long_minutes_.assign(m, m + sizeof(m)/sizeof(m[0]));
142     long_hours_.assign(h, h + sizeof(h)/sizeof(h[0]));
143 }
144 
145 template <class CharT>
duration_punct(int use,const string_type & long_seconds,const string_type & long_minutes,const string_type & long_hours,const string_type & short_seconds,const string_type & short_minutes,const string_type & short_hours)146 duration_punct<CharT>::duration_punct(int use,
147         const string_type& long_seconds, const string_type& long_minutes,
148         const string_type& long_hours, const string_type& short_seconds,
149         const string_type& short_minutes, const string_type& short_hours)
150     : use_short_(use==use_short),
151       long_seconds_(long_seconds),
152       long_minutes_(long_minutes),
153       long_hours_(long_hours),
154       short_seconds_(short_seconds),
155       short_minutes_(short_minutes),
156       short_hours_(short_hours)
157 {}
158 
159 template <class CharT>
duration_punct(int use,const duration_punct & d)160 duration_punct<CharT>::duration_punct(int use, const duration_punct& d)
161     : use_short_(use==use_short),
162       long_seconds_(d.long_seconds_),
163       long_minutes_(d.long_minutes_),
164       long_hours_(d.long_hours_),
165       short_seconds_(d.short_seconds_),
166       short_minutes_(d.short_minutes_),
167       short_hours_(d.short_hours_)
168 {}
169 
170 template <class CharT, class Traits>
171 std::basic_ostream<CharT, Traits>&
duration_short(std::basic_ostream<CharT,Traits> & os)172 duration_short(std::basic_ostream<CharT, Traits>& os)
173 {
174     typedef duration_punct<CharT> Facet;
175     std::locale loc = os.getloc();
176     if (std::has_facet<Facet>(loc))
177     {
178         const Facet& f = std::use_facet<Facet>(loc);
179         if (f.is_long_name())
180             os.imbue(std::locale(loc, new Facet(Facet::use_short, f)));
181     }
182     else
183         os.imbue(std::locale(loc, new Facet(Facet::use_short)));
184     return os;
185 }
186 
187 template <class CharT, class Traits>
188 std::basic_ostream<CharT, Traits>&
duration_long(std::basic_ostream<CharT,Traits> & os)189 duration_long(std::basic_ostream<CharT, Traits>& os)
190 {
191     typedef duration_punct<CharT> Facet;
192     std::locale loc = os.getloc();
193     if (std::has_facet<Facet>(loc))
194     {
195         const Facet& f = std::use_facet<Facet>(loc);
196         if (f.is_short_name())
197             os.imbue(std::locale(loc, new Facet(Facet::use_long, f)));
198     }
199     return os;
200 }
201 
202 template <class CharT, class Traits, class Rep, class Period>
203 std::basic_ostream<CharT, Traits>&
operator <<(std::basic_ostream<CharT,Traits> & os,const duration<Rep,Period> & d)204 operator<<(std::basic_ostream<CharT, Traits>& os, const duration<Rep, Period>& d)
205 {
206     typedef duration_punct<CharT> Facet;
207     std::locale loc = os.getloc();
208     if (!std::has_facet<Facet>(loc))
209         os.imbue(std::locale(loc, new Facet));
210     const Facet& f = std::use_facet<Facet>(os.getloc());
211     return os << d.count() << ' ' << f.template name<Period>(d.count());
212 }
213 
214 namespace chrono_detail {
215 template <class Rep, bool = is_scalar<Rep>::value>
216 struct duration_io_intermediate
217 {
218     typedef Rep type;
219 };
220 
221 template <class Rep>
222 struct duration_io_intermediate<Rep, true>
223 {
224     typedef typename mpl::if_c
225     <
226         is_floating_point<Rep>::value,
227             long double,
228             typename mpl::if_c
229             <
230                 is_signed<Rep>::value,
231                     long long,
232                     unsigned long long
233             >::type
234     >::type type;
235 };
236 
237 template <typename intermediate_type>
238 typename enable_if<is_integral<intermediate_type>, bool>::type
reduce(intermediate_type & r,unsigned long long & den,std::ios_base::iostate & err)239 reduce(intermediate_type& r, unsigned long long& den, std::ios_base::iostate& err)
240 {
241   typedef typename common_type<intermediate_type, unsigned long long>::type common_type_t;
242 
243     // Reduce r * num / den
244   common_type_t t = integer::gcd<common_type_t>(common_type_t(r), common_type_t(den));
245   r /= t;
246   den /= t;
247   if (den != 1)
248   {
249     // Conversion to Period is integral and not exact
250     err |= std::ios_base::failbit;
251     return false;
252   }
253   return true;
254 }
255 template <typename intermediate_type>
256 typename disable_if<is_integral<intermediate_type>, bool>::type
reduce(intermediate_type &,unsigned long long &,std::ios_base::iostate &)257 reduce(intermediate_type& , unsigned long long& , std::ios_base::iostate& )
258 {
259   return true;
260 }
261 
262 }
263 
264 template <class CharT, class Traits, class Rep, class Period>
265 std::basic_istream<CharT, Traits>&
operator >>(std::basic_istream<CharT,Traits> & is,duration<Rep,Period> & d)266 operator>>(std::basic_istream<CharT, Traits>& is, duration<Rep, Period>& d)
267 {
268   //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
269     typedef duration_punct<CharT> Facet;
270     std::locale loc = is.getloc();
271     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
272     if (!std::has_facet<Facet>(loc)) {
273       //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
274         is.imbue(std::locale(loc, new Facet));
275     }
276     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
277     loc = is.getloc();
278     const Facet& f = std::use_facet<Facet>(loc);
279     typedef typename chrono_detail::duration_io_intermediate<Rep>::type intermediate_type;
280     intermediate_type r;
281     std::ios_base::iostate err = std::ios_base::goodbit;
282     // read value into r
283     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
284     is >> r;
285     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
286     if (is.good())
287     {
288       //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
289         // now determine unit
290         typedef std::istreambuf_iterator<CharT, Traits> in_iterator;
291         in_iterator i(is);
292         in_iterator e;
293         //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
294         if (i != e && *i == ' ')  // mandatory ' ' after value
295         {
296           //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
297             ++i;
298             if (i != e)
299             {
300               //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
301                 // unit is num / den (yet to be determined)
302                 unsigned long long num = 0;
303                 unsigned long long den = 0;
304                 if (*i == '[')
305                 {
306                   //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
307                     // parse [N/D]s or [N/D]seconds format
308                     ++i;
309                     CharT x;
310                     is >> num >> x >> den;
311                     if (!is.good() || (x != '/'))
312                     {
313                       //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
314                         is.setstate(is.failbit);
315                         return is;
316                     }
317                     i = in_iterator(is);
318                     if (*i != ']')
319                     {
320                       //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
321                         is.setstate(is.failbit);
322                         return is;
323                     }
324                     ++i;
325                     const std::basic_string<CharT> units[] =
326                     {
327                         f.template singular<ratio<1> >(),
328                         f.template plural<ratio<1> >(),
329                         f.template short_name<ratio<1> >()
330                     };
331                     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
332                     const std::basic_string<CharT>* k = chrono_detail::scan_keyword(i, e,
333                                   units, units + sizeof(units)/sizeof(units[0]),
334                                   //~ std::use_facet<std::ctype<CharT> >(loc),
335                                   err);
336                     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
337                     is.setstate(err);
338                     switch ((k - units) / 3)
339                     {
340                     case 0:
341                       //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
342                         break;
343                     default:
344                         is.setstate(err);
345                         //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
346                         return is;
347                     }
348                 }
349                 else
350                 {
351                   //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
352                     // parse SI name, short or long
353                     const std::basic_string<CharT> units[] =
354                     {
355                         f.template singular<atto>(),
356                         f.template plural<atto>(),
357                         f.template short_name<atto>(),
358                         f.template singular<femto>(),
359                         f.template plural<femto>(),
360                         f.template short_name<femto>(),
361                         f.template singular<pico>(),
362                         f.template plural<pico>(),
363                         f.template short_name<pico>(),
364                         f.template singular<nano>(),
365                         f.template plural<nano>(),
366                         f.template short_name<nano>(),
367                         f.template singular<micro>(),
368                         f.template plural<micro>(),
369                         f.template short_name<micro>(),
370                         f.template singular<milli>(),
371                         f.template plural<milli>(),
372                         f.template short_name<milli>(),
373                         f.template singular<centi>(),
374                         f.template plural<centi>(),
375                         f.template short_name<centi>(),
376                         f.template singular<deci>(),
377                         f.template plural<deci>(),
378                         f.template short_name<deci>(),
379                         f.template singular<deca>(),
380                         f.template plural<deca>(),
381                         f.template short_name<deca>(),
382                         f.template singular<hecto>(),
383                         f.template plural<hecto>(),
384                         f.template short_name<hecto>(),
385                         f.template singular<kilo>(),
386                         f.template plural<kilo>(),
387                         f.template short_name<kilo>(),
388                         f.template singular<mega>(),
389                         f.template plural<mega>(),
390                         f.template short_name<mega>(),
391                         f.template singular<giga>(),
392                         f.template plural<giga>(),
393                         f.template short_name<giga>(),
394                         f.template singular<tera>(),
395                         f.template plural<tera>(),
396                         f.template short_name<tera>(),
397                         f.template singular<peta>(),
398                         f.template plural<peta>(),
399                         f.template short_name<peta>(),
400                         f.template singular<exa>(),
401                         f.template plural<exa>(),
402                         f.template short_name<exa>(),
403                         f.template singular<ratio<1> >(),
404                         f.template plural<ratio<1> >(),
405                         f.template short_name<ratio<1> >(),
406                         f.template singular<ratio<60> >(),
407                         f.template plural<ratio<60> >(),
408                         f.template short_name<ratio<60> >(),
409                         f.template singular<ratio<3600> >(),
410                         f.template plural<ratio<3600> >(),
411                         f.template short_name<ratio<3600> >()
412                     };
413                     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
414                     const std::basic_string<CharT>* k = chrono_detail::scan_keyword(i, e,
415                                   units, units + sizeof(units)/sizeof(units[0]),
416                                   //~ std::use_facet<std::ctype<CharT> >(loc),
417                                   err);
418                     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
419                     switch ((k - units) / 3)
420                     {
421                     case 0:
422                         num = 1ULL;
423                         den = 1000000000000000000ULL;
424                         break;
425                     case 1:
426                         num = 1ULL;
427                         den = 1000000000000000ULL;
428                         break;
429                     case 2:
430                         num = 1ULL;
431                         den = 1000000000000ULL;
432                         break;
433                     case 3:
434                         num = 1ULL;
435                         den = 1000000000ULL;
436                         break;
437                     case 4:
438                         num = 1ULL;
439                         den = 1000000ULL;
440                         break;
441                     case 5:
442                         num = 1ULL;
443                         den = 1000ULL;
444                         break;
445                     case 6:
446                         num = 1ULL;
447                         den = 100ULL;
448                         break;
449                     case 7:
450                         num = 1ULL;
451                         den = 10ULL;
452                         break;
453                     case 8:
454                         num = 10ULL;
455                         den = 1ULL;
456                         break;
457                     case 9:
458                         num = 100ULL;
459                         den = 1ULL;
460                         break;
461                     case 10:
462                         num = 1000ULL;
463                         den = 1ULL;
464                         break;
465                     case 11:
466                         num = 1000000ULL;
467                         den = 1ULL;
468                         break;
469                     case 12:
470                         num = 1000000000ULL;
471                         den = 1ULL;
472                         break;
473                     case 13:
474                         num = 1000000000000ULL;
475                         den = 1ULL;
476                         break;
477                     case 14:
478                         num = 1000000000000000ULL;
479                         den = 1ULL;
480                         break;
481                     case 15:
482                         num = 1000000000000000000ULL;
483                         den = 1ULL;
484                         break;
485                     case 16:
486                         num = 1;
487                         den = 1;
488                         break;
489                     case 17:
490                         num = 60;
491                         den = 1;
492                         break;
493                     case 18:
494                         num = 3600;
495                         den = 1;
496                         break;
497                     default:
498                       //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
499                         is.setstate(err|is.failbit);
500                         return is;
501                     }
502                 }
503                 //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
504                 // unit is num/den
505                 // r should be multiplied by (num/den) / Period
506                 // Reduce (num/den) / Period to lowest terms
507                 unsigned long long gcd_n1_n2 = integer::gcd<unsigned long long>(num, Period::num);
508                 unsigned long long gcd_d1_d2 = integer::gcd<unsigned long long>(den, Period::den);
509                 num /= gcd_n1_n2;
510                 den /= gcd_d1_d2;
511                 unsigned long long n2 = Period::num / gcd_n1_n2;
512                 unsigned long long d2 = Period::den / gcd_d1_d2;
513                 if (num > (std::numeric_limits<unsigned long long>::max)() / d2 ||
514                     den > (std::numeric_limits<unsigned long long>::max)() / n2)
515                 {
516                   //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
517                     // (num/den) / Period overflows
518                     is.setstate(err|is.failbit);
519                     return is;
520                 }
521                 num *= d2;
522                 den *= n2;
523 
524                 typedef typename common_type<intermediate_type, unsigned long long>::type common_type_t;
525 
526                 //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
527                 // num / den is now factor to multiply by r
528                 if (!chrono_detail::reduce(r, den, err))
529                 {
530                   //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
531                   is.setstate(err|is.failbit);
532                   return is;
533                 }
534 
535                 //if (r > ((duration_values<common_type_t>::max)() / num))
536                 //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
537                 if (chrono::detail::gt(r,((duration_values<common_type_t>::max)() / num)))
538                 {
539                     // Conversion to Period overflowed
540                   //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
541                     is.setstate(err|is.failbit);
542                     return is;
543                 }
544                 //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
545                 common_type_t t = r * num;
546                 t /= den;
547                 //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
548 
549                 if (t > duration_values<common_type_t>::zero())
550                 {
551                   //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
552                   if ( (duration_values<Rep>::max)() < Rep(t))
553                   {
554                     //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
555                     // Conversion to Period overflowed
556                     is.setstate(err|is.failbit);
557                     return is;
558                   }
559                 }
560                 //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
561                 // Success!  Store it.
562                 d = duration<Rep, Period>(Rep(t));
563                 is.setstate(err);
564                 //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
565                 return is;
566             }
567             else {
568               //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
569                 is.setstate(is.failbit | is.eofbit);
570                 return is;
571             }
572         }
573         else
574         {
575           //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
576             if (i == e)
577               is.setstate(is.failbit|is.eofbit);
578             else
579               is.setstate(is.failbit);
580             //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
581             return is;
582         }
583     }
584     else {
585       //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl;
586         //is.setstate(is.failbit);
587       return is;
588     }
589 }
590 
591 
592 template <class CharT, class Traits, class Clock, class Duration>
593 std::basic_ostream<CharT, Traits>&
operator <<(std::basic_ostream<CharT,Traits> & os,const time_point<Clock,Duration> & tp)594 operator<<(std::basic_ostream<CharT, Traits>& os,
595            const time_point<Clock, Duration>& tp)
596 {
597     return os << tp.time_since_epoch() << clock_string<Clock, CharT>::since();
598 }
599 
600 template <class CharT, class Traits, class Clock, class Duration>
601 std::basic_istream<CharT, Traits>&
operator >>(std::basic_istream<CharT,Traits> & is,time_point<Clock,Duration> & tp)602 operator>>(std::basic_istream<CharT, Traits>& is,
603            time_point<Clock, Duration>& tp)
604 {
605     Duration d;
606     is >> d;
607     if (is.good())
608     {
609         const std::basic_string<CharT> units=clock_string<Clock, CharT>::since();
610         std::ios_base::iostate err = std::ios_base::goodbit;
611         typedef std::istreambuf_iterator<CharT, Traits> in_iterator;
612         in_iterator i(is);
613         in_iterator e;
614         std::ptrdiff_t k = chrono_detail::scan_keyword(i, e,
615                       &units, &units + 1,
616                       //~ std::use_facet<std::ctype<CharT> >(is.getloc()),
617                       err) - &units;
618         is.setstate(err);
619         if (k == 1)
620         {
621           is.setstate(err | is.failbit);
622             // failed to read epoch string
623             return is;
624         }
625         tp = time_point<Clock, Duration>(d);
626     }
627     else
628         is.setstate(is.failbit);
629     return is;
630 }
631 }  // chrono
632 
633 }
634 
635 #endif  // BOOST_CHRONO_CHRONO_IO_HPP
636