Chris@16: // (C) Copyright Howard Hinnant Chris@16: // (C) Copyright 2011 Vicente J. Botet Escriba Chris@16: // Use, modification and distribution are subject to the Boost Software License, Chris@16: // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt). Chris@16: // Chris@16: Chris@16: #ifndef BOOST_CHRONO_IO_DURATION_GET_HPP Chris@16: #define BOOST_CHRONO_IO_DURATION_GET_HPP Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: #include Chris@16: #include Chris@16: #include Chris@101: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: /** Chris@16: * Duration formatting facet for input. Chris@16: */ Chris@16: namespace boost Chris@16: { Chris@16: namespace chrono Chris@16: { Chris@16: Chris@16: namespace detail Chris@16: { Chris@16: template ::value> Chris@16: struct duration_io_intermediate Chris@16: { Chris@16: typedef Rep type; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct duration_io_intermediate Chris@16: { Chris@16: typedef typename mpl::if_c::value, long double, typename mpl::if_c< Chris@16: is_signed::value, long long, unsigned long long>::type>::type type; Chris@16: }; Chris@16: Chris@101: template Chris@101: struct duration_io_intermediate, false> Chris@101: { Chris@101: typedef process_times::type> type; Chris@101: }; Chris@101: Chris@16: template Chris@16: typename enable_if , bool>::type reduce(intermediate_type& r, Chris@16: unsigned long long& den, std::ios_base::iostate& err) Chris@16: { Chris@16: typedef typename common_type::type common_type_t; Chris@16: Chris@16: // Reduce r * num / den Chris@101: common_type_t t = integer::gcd(common_type_t(r), common_type_t(den)); Chris@16: r /= t; Chris@16: den /= t; Chris@16: if (den != 1) Chris@16: { Chris@16: // Conversion to Period is integral and not exact Chris@16: err |= std::ios_base::failbit; Chris@16: return false; Chris@16: } Chris@16: return true; Chris@16: } Chris@16: template Chris@16: typename disable_if , bool>::type reduce(intermediate_type&, unsigned long long&, Chris@16: std::ios_base::iostate&) Chris@16: { Chris@16: return true; Chris@16: } Chris@16: Chris@16: } Chris@16: Chris@16: /** Chris@16: * @c duration_get is used to parse a character sequence, extracting Chris@16: * components of a duration into a class duration. Chris@16: * Each get member parses a format as produced by a corresponding format specifier to time_put<>::put. Chris@16: * If the sequence being parsed matches the correct format, the Chris@16: * corresponding member of the class duration argument are set to the Chris@16: * value used to produce the sequence; Chris@16: * otherwise either an error is reported or unspecified values are assigned. Chris@16: * In other words, user confirmation is required for reliable parsing of Chris@16: * user-entered durations, but machine-generated formats can be parsed Chris@16: * reliably. This allows parsers to be aggressive about interpreting user Chris@16: * variations on standard formats. Chris@16: * Chris@16: * If the end iterator is reached during parsing of the get() member Chris@16: * function, the member sets std::ios_base::eofbit in err. Chris@16: */ Chris@16: template > Chris@16: class duration_get: public std::locale::facet Chris@16: { Chris@16: public: Chris@16: /** Chris@16: * Type of character the facet is instantiated on. Chris@16: */ Chris@16: typedef CharT char_type; Chris@16: /** Chris@16: * Type of character string passed to member functions. Chris@16: */ Chris@16: typedef std::basic_string string_type; Chris@16: /** Chris@16: * Type of iterator used to scan the character buffer. Chris@16: */ Chris@16: typedef InputIterator iter_type; Chris@16: Chris@16: /** Chris@16: * Construct a @c duration_get facet. Chris@16: * @param refs Chris@16: * @Effects Construct a @c duration_get facet. Chris@16: * If the @c refs argument is @c 0 then destruction of the object is Chris@16: * delegated to the @c locale, or locales, containing it. This allows Chris@16: * the user to ignore lifetime management issues. On the other had, Chris@16: * if @c refs is @c 1 then the object must be explicitly deleted; Chris@16: * the @c locale will not do so. In this case, the object can be Chris@16: * maintained across the lifetime of multiple locales. Chris@16: */ Chris@16: Chris@16: explicit duration_get(size_t refs = 0) : Chris@16: std::locale::facet(refs) Chris@16: { Chris@16: } Chris@16: Chris@16: /** Chris@16: * @param s start input stream iterator Chris@16: * @param end end input stream iterator Chris@16: * @param ios a reference to a ios_base Chris@16: * @param err the ios_base state Chris@16: * @param d the duration Chris@16: * @param pattern begin of the formatting pattern Chris@16: * @param pat_end end of the formatting pattern Chris@16: * Chris@16: * Requires: [pattern,pat_end) shall be a valid range. Chris@16: * Chris@16: * Effects: The function starts by evaluating err = std::ios_base::goodbit. Chris@16: * It then enters a loop, reading zero or more characters from s at Chris@16: * each iteration. Unless otherwise specified below, the loop Chris@16: * terminates when the first of the following conditions holds: Chris@16: * - The expression pattern == pat_end evaluates to true. Chris@16: * - The expression err == std::ios_base::goodbit evaluates to false. Chris@16: * - The expression s == end evaluates to true, in which case the Chris@16: * function evaluates err = std::ios_base::eofbit | std::ios_base::failbit. Chris@16: * - The next element of pattern is equal to '%', followed by a conversion Chris@16: * specifier character, format. Chris@16: * If the number of elements in the range [pattern,pat_end) is not Chris@16: * sufficient to unambiguously determine whether the conversion Chris@16: * specification is complete and valid, the function evaluates Chris@16: * err = std::ios_base::failbit. Otherwise, the function evaluates Chris@16: * s = get_value(s, end, ios, err, r) when the conversion specification is 'v' and Chris@16: * s = get_value(s, end, ios, err, rt) when the conversion specification is 'u'. Chris@16: * If err == std::ios_base::goodbit holds after Chris@16: * the evaluation of the expression, the function increments pattern to Chris@16: * point just past the end of the conversion specification and continues Chris@16: * looping. Chris@16: * - The expression isspace(*pattern, ios.getloc()) evaluates to true, in Chris@16: * which case the function first increments pattern until Chris@16: * pattern == pat_end || !isspace(*pattern, ios.getloc()) evaluates to true, Chris@16: * then advances s until s == end || !isspace(*s, ios.getloc()) is true, Chris@16: * and finally resumes looping. Chris@16: * - The next character read from s matches the element pointed to by Chris@16: * pattern in a case-insensitive comparison, in which case the function Chris@16: * evaluates ++pattern, ++s and continues looping. Otherwise, the function Chris@16: * evaluates err = std::ios_base::failbit. Chris@16: * Chris@16: * Once r and rt are retrieved, Chris@16: * Returns: s Chris@16: */ Chris@16: template Chris@16: iter_type get(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, Chris@16: duration &d, const char_type *pattern, const char_type *pat_end) const Chris@16: { Chris@16: if (std::has_facet >(ios.getloc())) Chris@16: { Chris@16: duration_units const&facet = std::use_facet >(ios.getloc()); Chris@16: return get(facet, s, end, ios, err, d, pattern, pat_end); Chris@16: } Chris@16: else Chris@16: { Chris@16: duration_units_default facet; Chris@16: return get(facet, s, end, ios, err, d, pattern, pat_end); Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: iter_type get(duration_units const&facet, iter_type s, iter_type end, std::ios_base& ios, Chris@16: std::ios_base::iostate& err, duration &d, const char_type *pattern, const char_type *pat_end) const Chris@16: { Chris@16: Chris@16: typedef typename detail::duration_io_intermediate::type intermediate_type; Chris@16: intermediate_type r; Chris@16: rt_ratio rt; Chris@16: bool value_found = false, unit_found = false; Chris@16: Chris@16: const std::ctype& ct = std::use_facet >(ios.getloc()); Chris@16: while (pattern != pat_end && err == std::ios_base::goodbit) Chris@16: { Chris@16: if (s == end) Chris@16: { Chris@16: err |= std::ios_base::eofbit; Chris@16: break; Chris@16: } Chris@16: if (ct.narrow(*pattern, 0) == '%') Chris@16: { Chris@16: if (++pattern == pat_end) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return s; Chris@16: } Chris@16: char cmd = ct.narrow(*pattern, 0); Chris@16: switch (cmd) Chris@16: { Chris@16: case 'v': Chris@16: { Chris@16: if (value_found) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return s; Chris@16: } Chris@16: value_found = true; Chris@16: s = get_value(s, end, ios, err, r); Chris@16: if (err & (std::ios_base::badbit | std::ios_base::failbit)) Chris@16: { Chris@16: return s; Chris@16: } Chris@16: break; Chris@16: } Chris@16: case 'u': Chris@16: { Chris@16: if (unit_found) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return s; Chris@16: } Chris@16: unit_found = true; Chris@16: s = get_unit(facet, s, end, ios, err, rt); Chris@16: if (err & (std::ios_base::badbit | std::ios_base::failbit)) Chris@16: { Chris@16: return s; Chris@16: } Chris@16: break; Chris@16: } Chris@16: default: Chris@16: BOOST_ASSERT(false && "Boost::Chrono internal error."); Chris@16: break; Chris@16: } Chris@16: Chris@16: ++pattern; Chris@16: } Chris@16: else if (ct.is(std::ctype_base::space, *pattern)) Chris@16: { Chris@16: for (++pattern; pattern != pat_end && ct.is(std::ctype_base::space, *pattern); ++pattern) Chris@16: ; Chris@16: for (; s != end && ct.is(std::ctype_base::space, *s); ++s) Chris@16: ; Chris@16: } Chris@16: else if (ct.toupper(*s) == ct.toupper(*pattern)) Chris@16: { Chris@16: ++s; Chris@16: ++pattern; Chris@16: } Chris@16: else Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return s; Chris@16: } Chris@16: Chris@16: } Chris@16: Chris@16: unsigned long long num = rt.num; Chris@16: unsigned long long den = rt.den; Chris@16: Chris@16: // r should be multiplied by (num/den) / Period Chris@16: // Reduce (num/den) / Period to lowest terms Chris@101: unsigned long long gcd_n1_n2 = integer::gcd(num, Period::num); Chris@101: unsigned long long gcd_d1_d2 = integer::gcd(den, Period::den); Chris@16: num /= gcd_n1_n2; Chris@16: den /= gcd_d1_d2; Chris@16: unsigned long long n2 = Period::num / gcd_n1_n2; Chris@16: unsigned long long d2 = Period::den / gcd_d1_d2; Chris@16: if (num > (std::numeric_limits::max)() / d2 || den Chris@16: > (std::numeric_limits::max)() / n2) Chris@16: { Chris@16: // (num/den) / Period overflows Chris@16: err |= std::ios_base::failbit; Chris@16: return s; Chris@16: } Chris@16: num *= d2; Chris@16: den *= n2; Chris@16: Chris@16: typedef typename common_type::type common_type_t; Chris@16: Chris@16: // num / den is now factor to multiply by r Chris@16: if (!detail::reduce(r, den, err)) return s; Chris@16: Chris@16: if (chrono::detail::gt(r, ( (duration_values::max)() / num))) Chris@16: { Chris@16: // Conversion to Period overflowed Chris@16: err |= std::ios_base::failbit; Chris@16: return s; Chris@16: } Chris@16: common_type_t t = r * num; Chris@16: t /= den; Chris@101: if (t > duration_values::zero()) Chris@16: { Chris@16: Rep pt = t; Chris@16: if ( (duration_values::max)() < pt) Chris@16: { Chris@16: // Conversion to Period overflowed Chris@16: err |= std::ios_base::failbit; Chris@16: return s; Chris@16: } Chris@16: } Chris@16: // Success! Store it. Chris@16: r = Rep(t); Chris@16: d = duration (r); Chris@16: Chris@16: return s; Chris@16: } Chris@16: Chris@16: /** Chris@16: * Chris@16: * @param s start input stream iterator Chris@16: * @param end end input stream iterator Chris@16: * @param ios a reference to a ios_base Chris@16: * @param err the ios_base state Chris@16: * @param d the duration Chris@16: * Stores the duration pattern from the @c duration_unit facet in let say @c str. Last as if Chris@16: * @code Chris@16: * return get(s, end, ios, err, ios, d, str.data(), str.data() + str.size()); Chris@16: * @codeend Chris@16: * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name Chris@16: */ Chris@16: template Chris@16: iter_type get(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, Chris@16: duration & d) const Chris@16: { Chris@16: if (std::has_facet >(ios.getloc())) Chris@16: { Chris@16: duration_units const&facet = std::use_facet >(ios.getloc()); Chris@16: std::basic_string str = facet.get_pattern(); Chris@16: return get(facet, s, end, ios, err, d, str.data(), str.data() + str.size()); Chris@16: } Chris@16: else Chris@16: { Chris@16: duration_units_default facet; Chris@16: std::basic_string str = facet.get_pattern(); Chris@16: return get(facet, s, end, ios, err, d, str.data(), str.data() + str.size()); Chris@16: } Chris@16: } Chris@16: Chris@16: /** Chris@16: * Chris@16: * @param s start input stream iterator Chris@16: * @param end end input stream iterator Chris@16: * @param ios a reference to a ios_base Chris@16: * @param err the ios_base state Chris@16: * @param r a reference to the duration representation. Chris@16: * @Effects As if Chris@16: * @code Chris@16: * return std::use_facet >(ios.getloc()).get(s, end, ios, err, r); Chris@16: * @endcode Chris@16: * Chris@16: * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name Chris@16: */ Chris@16: template Chris@16: iter_type get_value(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, Rep& r) const Chris@16: { Chris@16: return std::use_facet >(ios.getloc()).get(s, end, ios, err, r); Chris@16: } Chris@101: template Chris@101: iter_type get_value(iter_type s, iter_type end, std::ios_base& ios, std::ios_base::iostate& err, process_times& r) const Chris@101: { Chris@101: if (s == end) { Chris@101: err |= std::ios_base::eofbit; Chris@101: return s; Chris@101: } else if (*s != '{') { // mandatory '{' Chris@101: err |= std::ios_base::failbit; Chris@101: return s; Chris@101: } Chris@101: ++s; Chris@101: s = std::use_facet >(ios.getloc()).get(s, end, ios, err, r.real); Chris@101: if (s == end) { Chris@101: err |= std::ios_base::eofbit; Chris@101: return s; Chris@101: } else if (*s != ';') { // mandatory ';' Chris@101: err |= std::ios_base::failbit; Chris@101: return s; Chris@101: } Chris@101: ++s; Chris@101: s = std::use_facet >(ios.getloc()).get(s, end, ios, err, r.user); Chris@101: if (s == end) { Chris@101: err |= std::ios_base::eofbit; Chris@101: return s; Chris@101: } else if (*s != ';') { // mandatory ';' Chris@101: err |= std::ios_base::failbit; Chris@101: return s; Chris@101: } Chris@101: ++s; Chris@101: s = std::use_facet >(ios.getloc()).get(s, end, ios, err, r.system); Chris@101: if (s == end) { Chris@101: err |= std::ios_base::eofbit; Chris@101: return s; Chris@101: } else if (*s != '}') { // mandatory '}' Chris@101: err |= std::ios_base::failbit; Chris@101: return s; Chris@101: } Chris@101: return s; Chris@101: } Chris@16: Chris@16: /** Chris@16: * Chris@16: * @param s start input stream iterator Chris@16: * @param e end input stream iterator Chris@16: * @param ios a reference to a ios_base Chris@16: * @param err the ios_base state Chris@16: * @param rt a reference to the duration run-time ratio. Chris@16: * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name Chris@16: */ Chris@16: iter_type get_unit(iter_type i, iter_type e, std::ios_base& is, std::ios_base::iostate& err, rt_ratio &rt) const Chris@16: { Chris@16: if (std::has_facet >(is.getloc())) Chris@16: { Chris@16: return get_unit(std::use_facet >(is.getloc()), i, e, is, err, rt); Chris@16: } Chris@16: else Chris@16: { Chris@16: duration_units_default facet; Chris@16: return get_unit(facet, i, e, is, err, rt); Chris@16: } Chris@16: } Chris@16: Chris@16: Chris@16: iter_type get_unit(duration_units const &facet, iter_type i, iter_type e, std::ios_base& is, Chris@16: std::ios_base::iostate& err, rt_ratio &rt) const Chris@16: { Chris@16: Chris@16: if (*i == '[') Chris@16: { Chris@16: // parse [N/D]s or [N/D]second or [N/D]seconds format Chris@16: ++i; Chris@16: i = std::use_facet >(is.getloc()).get(i, e, is, err, rt.num); Chris@16: if ( (err & std::ios_base::failbit) != 0) Chris@16: { Chris@16: return i; Chris@16: } Chris@16: Chris@16: if (i == e) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return i; Chris@16: } Chris@16: CharT x = *i++; Chris@16: if (x != '/') Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return i; Chris@16: } Chris@16: i = std::use_facet >(is.getloc()).get(i, e, is, err, rt.den); Chris@16: if ( (err & std::ios_base::failbit) != 0) Chris@16: { Chris@16: return i; Chris@16: } Chris@16: if (i == e) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return i; Chris@16: } Chris@16: if (*i != ']') Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return i; Chris@16: } Chris@16: ++i; Chris@16: if (i == e) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: return i; Chris@16: } Chris@16: // parse s or second or seconds Chris@16: return do_get_n_d_valid_unit(facet, i, e, is, err); Chris@16: } Chris@16: else Chris@16: { Chris@16: return do_get_valid_unit(facet, i, e, is, err, rt); Chris@16: } Chris@16: } Chris@16: Chris@16: /** Chris@16: * Unique identifier for this type of facet. Chris@16: */ Chris@16: static std::locale::id id; Chris@16: Chris@16: /** Chris@16: * @Effects Destroy the facet Chris@16: */ Chris@16: ~duration_get() Chris@16: { Chris@16: } Chris@16: Chris@16: protected: Chris@16: Chris@16: /** Chris@16: * Extracts the run-time ratio associated to the duration when it is given in prefix form. Chris@16: * Chris@16: * This is an extension point of this facet so that we can take in account other periods that can have a useful Chris@16: * translation in other contexts, as e.g. days and weeks. Chris@16: * Chris@16: * @param facet the duration_units facet Chris@16: * @param i start input stream iterator. Chris@16: * @param e end input stream iterator. Chris@16: * @param ios a reference to a ios_base. Chris@16: * @param err the ios_base state. Chris@16: * @return @c s Chris@16: */ Chris@16: iter_type do_get_n_d_valid_unit(duration_units const &facet, iter_type i, iter_type e, Chris@16: std::ios_base&, std::ios_base::iostate& err) const Chris@16: { Chris@16: // parse SI name, short or long Chris@16: Chris@16: const string_type* units = facet.get_n_d_valid_units_start(); Chris@16: const string_type* units_end = facet.get_n_d_valid_units_end(); Chris@16: Chris@16: const string_type* k = chrono_detail::scan_keyword(i, e, units, units_end, Chris@16: //~ std::use_facet >(loc), Chris@16: err); Chris@16: if (err & (std::ios_base::badbit | std::ios_base::failbit)) Chris@16: { Chris@16: return i; Chris@16: } Chris@16: if (!facet.match_n_d_valid_unit(k)) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: } Chris@16: return i; Chris@16: } Chris@16: Chris@16: /** Chris@16: * Extracts the run-time ratio associated to the duration when it is given in prefix form. Chris@16: * Chris@16: * This is an extension point of this facet so that we can take in account other periods that can have a useful Chris@16: * translation in other contexts, as e.g. days and weeks. Chris@16: * Chris@16: * @param facet the duration_units facet Chris@16: * @param i start input stream iterator. Chris@16: * @param e end input stream iterator. Chris@16: * @param ios a reference to a ios_base. Chris@16: * @param err the ios_base state. Chris@16: * @param rt a reference to the duration run-time ratio. Chris@16: * @Effects Chris@16: * @Returns An iterator pointing just beyond the last character that can be determined to be part of a valid name. Chris@16: */ Chris@16: iter_type do_get_valid_unit(duration_units const &facet, iter_type i, iter_type e, Chris@16: std::ios_base&, std::ios_base::iostate& err, rt_ratio &rt) const Chris@16: { Chris@16: // parse SI name, short or long Chris@16: Chris@16: const string_type* units = facet.get_valid_units_start(); Chris@16: const string_type* units_end = facet.get_valid_units_end(); Chris@16: Chris@16: err = std::ios_base::goodbit; Chris@16: const string_type* k = chrono_detail::scan_keyword(i, e, units, units_end, Chris@16: //~ std::use_facet >(loc), Chris@16: err); Chris@16: if (err & (std::ios_base::badbit | std::ios_base::failbit)) Chris@16: { Chris@16: return i; Chris@16: } Chris@16: if (!facet.match_valid_unit(k, rt)) Chris@16: { Chris@16: err |= std::ios_base::failbit; Chris@16: } Chris@16: return i; Chris@16: } Chris@16: }; Chris@16: Chris@16: /** Chris@16: * Unique identifier for this type of facet. Chris@16: */ Chris@16: template Chris@16: std::locale::id duration_get::id; Chris@16: Chris@16: } // chrono Chris@16: } Chris@16: // boost Chris@16: Chris@16: #endif // header