// chrono_io // // (C) Copyright Howard Hinnant // (C) Copyright 2010 Vicente J. Botet Escriba // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt). // // This code was adapted by Vicente from Howard Hinnant's experimental work // on chrono i/o under lvm/libc++ to Boost #ifndef BOOST_CHRONO_IO_V1_CHRONO_IO_HPP #define BOOST_CHRONO_IO_V1_CHRONO_IO_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace chrono { template class duration_punct : public std::locale::facet { public: typedef std::basic_string string_type; enum {use_long, use_short}; private: bool use_short_; string_type long_seconds_; string_type long_minutes_; string_type long_hours_; string_type short_seconds_; string_type short_minutes_; string_type short_hours_; template string_type short_name(Period) const {return ::boost::ratio_string::short_name() + short_seconds_;} string_type short_name(ratio<1>) const {return short_seconds_;} string_type short_name(ratio<60>) const {return short_minutes_;} string_type short_name(ratio<3600>) const {return short_hours_;} template string_type long_name(Period) const {return ::boost::ratio_string::long_name() + long_seconds_;} string_type long_name(ratio<1>) const {return long_seconds_;} string_type long_name(ratio<60>) const {return long_minutes_;} string_type long_name(ratio<3600>) const {return long_hours_;} void init_C(); public: static std::locale::id id; explicit duration_punct(int use = use_long) : use_short_(use==use_short) {init_C();} 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); duration_punct(int use, const duration_punct& d); template string_type short_name() const {return short_name(typename Period::type());} template string_type long_name() const {return long_name(typename Period::type());} template string_type plural() const {return long_name(typename Period::type());} template string_type singular() const { return string_type(long_name(typename Period::type()), 0, long_name(typename Period::type()).size()-1); } template string_type name() const { if (use_short_) return short_name(); else { return long_name(); } } template string_type name(D v) const { if (use_short_) return short_name(); else { if (v==-1 || v==1) return singular(); else return plural(); } } bool is_short_name() const {return use_short_;} bool is_long_name() const {return !use_short_;} }; template std::locale::id duration_punct::id; template void duration_punct::init_C() { short_seconds_ = CharT('s'); short_minutes_ = CharT('m'); short_hours_ = CharT('h'); const CharT s[] = {'s', 'e', 'c', 'o', 'n', 'd', 's'}; const CharT m[] = {'m', 'i', 'n', 'u', 't', 'e', 's'}; const CharT h[] = {'h', 'o', 'u', 'r', 's'}; long_seconds_.assign(s, s + sizeof(s)/sizeof(s[0])); long_minutes_.assign(m, m + sizeof(m)/sizeof(m[0])); long_hours_.assign(h, h + sizeof(h)/sizeof(h[0])); } template duration_punct::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) : use_short_(use==use_short), long_seconds_(long_seconds), long_minutes_(long_minutes), long_hours_(long_hours), short_seconds_(short_seconds), short_minutes_(short_minutes), short_hours_(short_hours) {} template duration_punct::duration_punct(int use, const duration_punct& d) : use_short_(use==use_short), long_seconds_(d.long_seconds_), long_minutes_(d.long_minutes_), long_hours_(d.long_hours_), short_seconds_(d.short_seconds_), short_minutes_(d.short_minutes_), short_hours_(d.short_hours_) {} template std::basic_ostream& duration_short(std::basic_ostream& os) { typedef duration_punct Facet; std::locale loc = os.getloc(); if (std::has_facet(loc)) { const Facet& f = std::use_facet(loc); if (f.is_long_name()) os.imbue(std::locale(loc, new Facet(Facet::use_short, f))); } else os.imbue(std::locale(loc, new Facet(Facet::use_short))); return os; } template std::basic_ostream& duration_long(std::basic_ostream& os) { typedef duration_punct Facet; std::locale loc = os.getloc(); if (std::has_facet(loc)) { const Facet& f = std::use_facet(loc); if (f.is_short_name()) os.imbue(std::locale(loc, new Facet(Facet::use_long, f))); } return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, const duration& d) { typedef duration_punct Facet; std::locale loc = os.getloc(); if (!std::has_facet(loc)) os.imbue(std::locale(loc, new Facet)); const Facet& f = std::use_facet(os.getloc()); return os << d.count() << ' ' << f.template name(d.count()); } namespace chrono_detail { template ::value> struct duration_io_intermediate { typedef Rep type; }; template struct duration_io_intermediate { typedef typename mpl::if_c < is_floating_point::value, long double, typename mpl::if_c < is_signed::value, long long, unsigned long long >::type >::type type; }; template typename enable_if, bool>::type reduce(intermediate_type& r, unsigned long long& den, std::ios_base::iostate& err) { typedef typename common_type::type common_type_t; // Reduce r * num / den common_type_t t = math::gcd(common_type_t(r), common_type_t(den)); r /= t; den /= t; if (den != 1) { // Conversion to Period is integral and not exact err |= std::ios_base::failbit; return false; } return true; } template typename disable_if, bool>::type reduce(intermediate_type& , unsigned long long& , std::ios_base::iostate& ) { return true; } } template std::basic_istream& operator>>(std::basic_istream& is, duration& d) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; typedef duration_punct Facet; std::locale loc = is.getloc(); //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; if (!std::has_facet(loc)) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.imbue(std::locale(loc, new Facet)); } //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; loc = is.getloc(); const Facet& f = std::use_facet(loc); typedef typename chrono_detail::duration_io_intermediate::type intermediate_type; intermediate_type r; std::ios_base::iostate err = std::ios_base::goodbit; // read value into r //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is >> r; //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; if (is.good()) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // now determine unit typedef std::istreambuf_iterator in_iterator; in_iterator i(is); in_iterator e; //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; if (i != e && *i == ' ') // mandatory ' ' after value { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; ++i; if (i != e) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // unit is num / den (yet to be determined) unsigned long long num = 0; unsigned long long den = 0; if (*i == '[') { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // parse [N/D]s or [N/D]seconds format ++i; CharT x; is >> num >> x >> den; if (!is.good() || (x != '/')) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.setstate(is.failbit); return is; } i = in_iterator(is); if (*i != ']') { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.setstate(is.failbit); return is; } ++i; const std::basic_string units[] = { f.template singular >(), f.template plural >(), f.template short_name >() }; //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; const std::basic_string* k = chrono_detail::scan_keyword(i, e, units, units + sizeof(units)/sizeof(units[0]), //~ std::use_facet >(loc), err); //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.setstate(err); switch ((k - units) / 3) { case 0: //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; break; default: is.setstate(err); //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; return is; } } else { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // parse SI name, short or long const std::basic_string units[] = { f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular(), f.template plural(), f.template short_name(), f.template singular >(), f.template plural >(), f.template short_name >(), f.template singular >(), f.template plural >(), f.template short_name >(), f.template singular >(), f.template plural >(), f.template short_name >() }; //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; const std::basic_string* k = chrono_detail::scan_keyword(i, e, units, units + sizeof(units)/sizeof(units[0]), //~ std::use_facet >(loc), err); //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; switch ((k - units) / 3) { case 0: num = 1ULL; den = 1000000000000000000ULL; break; case 1: num = 1ULL; den = 1000000000000000ULL; break; case 2: num = 1ULL; den = 1000000000000ULL; break; case 3: num = 1ULL; den = 1000000000ULL; break; case 4: num = 1ULL; den = 1000000ULL; break; case 5: num = 1ULL; den = 1000ULL; break; case 6: num = 1ULL; den = 100ULL; break; case 7: num = 1ULL; den = 10ULL; break; case 8: num = 10ULL; den = 1ULL; break; case 9: num = 100ULL; den = 1ULL; break; case 10: num = 1000ULL; den = 1ULL; break; case 11: num = 1000000ULL; den = 1ULL; break; case 12: num = 1000000000ULL; den = 1ULL; break; case 13: num = 1000000000000ULL; den = 1ULL; break; case 14: num = 1000000000000000ULL; den = 1ULL; break; case 15: num = 1000000000000000000ULL; den = 1ULL; break; case 16: num = 1; den = 1; break; case 17: num = 60; den = 1; break; case 18: num = 3600; den = 1; break; default: //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.setstate(err|is.failbit); return is; } } //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // unit is num/den // r should be multiplied by (num/den) / Period // Reduce (num/den) / Period to lowest terms unsigned long long gcd_n1_n2 = math::gcd(num, Period::num); unsigned long long gcd_d1_d2 = math::gcd(den, Period::den); num /= gcd_n1_n2; den /= gcd_d1_d2; unsigned long long n2 = Period::num / gcd_n1_n2; unsigned long long d2 = Period::den / gcd_d1_d2; if (num > (std::numeric_limits::max)() / d2 || den > (std::numeric_limits::max)() / n2) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // (num/den) / Period overflows is.setstate(err|is.failbit); return is; } num *= d2; den *= n2; typedef typename common_type::type common_type_t; //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // num / den is now factor to multiply by r if (!chrono_detail::reduce(r, den, err)) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.setstate(err|is.failbit); return is; } //if (r > ((duration_values::max)() / num)) //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; if (chrono::detail::gt(r,((duration_values::max)() / num))) { // Conversion to Period overflowed //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.setstate(err|is.failbit); return is; } //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; common_type_t t = r * num; t /= den; //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; if (t > duration_values::zero()) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; Rep pt = t; if ( (duration_values::max)() < pt) { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // Conversion to Period overflowed is.setstate(err|is.failbit); return is; } } //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; // Success! Store it. r = Rep(t); d = duration(r); is.setstate(err); //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; return is; } else { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; is.setstate(is.failbit | is.eofbit); return is; } } else { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; if (i == e) is.setstate(is.failbit|is.eofbit); else is.setstate(is.failbit); //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; return is; } } else { //std::cerr << __FILE__ << "[" << __LINE__ << "]"<< std::endl; //is.setstate(is.failbit); return is; } } template std::basic_ostream& operator<<(std::basic_ostream& os, const time_point& tp) { return os << tp.time_since_epoch() << clock_string::since(); } template std::basic_istream& operator>>(std::basic_istream& is, time_point& tp) { Duration d; is >> d; if (is.good()) { const std::basic_string units=clock_string::since(); std::ios_base::iostate err = std::ios_base::goodbit; typedef std::istreambuf_iterator in_iterator; in_iterator i(is); in_iterator e; std::ptrdiff_t k = chrono_detail::scan_keyword(i, e, &units, &units + 1, //~ std::use_facet >(is.getloc()), err) - &units; is.setstate(err); if (k == 1) { is.setstate(err | is.failbit); // failed to read epoch string return is; } tp = time_point(d); } else is.setstate(is.failbit); return is; } } // chrono } #endif // BOOST_CHRONO_CHRONO_IO_HPP