mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-25 23:41:28 +08:00
libstdc++: Make chrono::hh_mm_ss more compact
This uses a single byte for the minutes and seconds members, and places the bool member next to those single bytes. This means we do not need 40 bytes to store a time that can fit in a single 8-byte integer. When there is no subsecond precision we can do away with the _M_ss member altogether. If the subsecond precision is coarse enough, we can use a smaller representation for _M_ss, e.g. hh_mm_ss<milliseconds> only needs uint_least32_t for _M_ss, and hh_mm_ss<duration<long, ratio<1,10>> and hh_mm_ss<duration<int8_t, nano>> only need a single byte. In the latter case the type can only ever represent up to 255ns anyway, so we don't need a larger representation type (in such cases, we could even remove the _M_h, _M_m and _M_s members, but it's a very unlikely scenario that isn't worth optimizing for). Except for specializations with a floating-point rep or using higher precision than nanoseconds, hh_mm_ss should now fit in 16 bytes, or even 12 bytes for x86-32 where alignof(long long) == 4. libstdc++-v3/ChangeLog: * include/std/chrono (chrono::hh_mm_ss): Do not use 64-bit representations for all four duration members. Reorder members. (hh_mm_ss::hh_mm_ss()): Define as defaulted. (hh_mm_ss::hh_mm_ss(Duration)): Delegate to a new private constructor, instead of calling chrono::abs repeatedly. * testsuite/std/time/hh_mm_ss/1.cc: Check floating-point representations. Check default constructor. Check sizes.
This commit is contained in:
parent
4ba94abf14
commit
5329e1a8e1
@ -41,6 +41,7 @@
|
||||
#include <bits/chrono.h>
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
# include <bit>
|
||||
# include <sstream>
|
||||
# include <string>
|
||||
# include <vector>
|
||||
@ -2262,6 +2263,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
// HH_MM_SS
|
||||
|
||||
/// @cond undocumented
|
||||
namespace __detail
|
||||
{
|
||||
consteval long long
|
||||
@ -2273,22 +2275,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return __r;
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** Utility for splitting a duration into hours, minutes, and seconds
|
||||
*
|
||||
* This is a convenience type that provides accessors for the constituent
|
||||
* parts (hours, minutes, seconds and subseconds) of a duration.
|
||||
*
|
||||
* @since C++20
|
||||
*/
|
||||
template<typename _Duration>
|
||||
class hh_mm_ss
|
||||
{
|
||||
static_assert( __is_duration<_Duration>::value );
|
||||
|
||||
private:
|
||||
static constexpr int
|
||||
static consteval int
|
||||
_S_fractional_width()
|
||||
{
|
||||
int __multiplicity_2 = 0;
|
||||
int __multiplicity_5 = 0;
|
||||
auto __den = _Duration::period::den;
|
||||
while ((__den % 2) == 0)
|
||||
{
|
||||
++__multiplicity_2;
|
||||
__den /= 2;
|
||||
}
|
||||
const int __multiplicity_2 = std::__countr_zero((uintmax_t)__den);
|
||||
__den >>= __multiplicity_2;
|
||||
int __multiplicity_5 = 0;
|
||||
while ((__den % 5) == 0)
|
||||
{
|
||||
++__multiplicity_5;
|
||||
@ -2304,6 +2312,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
return __width;
|
||||
}
|
||||
|
||||
constexpr
|
||||
hh_mm_ss(_Duration __d, bool __is_neg) noexcept
|
||||
: _M_h (duration_cast<chrono::hours>(__d)),
|
||||
_M_m (duration_cast<chrono::minutes>(__d - hours())),
|
||||
_M_s (duration_cast<chrono::seconds>(__d - hours() - minutes())),
|
||||
_M_is_neg(__is_neg)
|
||||
{
|
||||
auto __ss = __d - hours() - minutes() - seconds();
|
||||
if constexpr (treat_as_floating_point_v<typename precision::rep>)
|
||||
_M_ss._M_r = __ss.count();
|
||||
else if constexpr (precision::period::den != 1)
|
||||
_M_ss._M_r = duration_cast<precision>(__ss).count();
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr unsigned fractional_width = {_S_fractional_width()};
|
||||
|
||||
@ -2312,28 +2334,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
chrono::seconds::rep>,
|
||||
ratio<1, __detail::__pow10(fractional_width)>>;
|
||||
|
||||
constexpr
|
||||
hh_mm_ss() noexcept
|
||||
: hh_mm_ss{_Duration::zero()}
|
||||
{ }
|
||||
constexpr hh_mm_ss() noexcept = default;
|
||||
|
||||
constexpr explicit
|
||||
hh_mm_ss(_Duration __d) noexcept
|
||||
: _M_is_neg (__d < _Duration::zero()),
|
||||
_M_h (duration_cast<chrono::hours>(abs(__d))),
|
||||
_M_m (duration_cast<chrono::minutes>(abs(__d) - hours())),
|
||||
_M_s (duration_cast<chrono::seconds>(abs(__d) - hours() - minutes()))
|
||||
{
|
||||
if constexpr (treat_as_floating_point_v<typename precision::rep>)
|
||||
_M_ss = abs(__d) - hours() - minutes() - seconds();
|
||||
else
|
||||
_M_ss = duration_cast<precision>(abs(__d) - hours()
|
||||
- minutes() - seconds());
|
||||
}
|
||||
: hh_mm_ss(chrono::abs(__d), __d < _Duration::zero())
|
||||
{ }
|
||||
|
||||
constexpr bool
|
||||
is_negative() const noexcept
|
||||
{ return _M_is_neg; }
|
||||
{
|
||||
if constexpr (!__is_unsigned)
|
||||
return _M_is_neg;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr chrono::hours
|
||||
hours() const noexcept
|
||||
@ -2349,7 +2364,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
constexpr precision
|
||||
subseconds() const noexcept
|
||||
{ return _M_ss; }
|
||||
{ return static_cast<precision>(_M_ss); }
|
||||
|
||||
constexpr explicit
|
||||
operator precision() const noexcept
|
||||
@ -2358,20 +2373,82 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
constexpr precision
|
||||
to_duration() const noexcept
|
||||
{
|
||||
if (_M_is_neg)
|
||||
return -(_M_h + _M_m + _M_s + _M_ss);
|
||||
else
|
||||
return _M_h + _M_m + _M_s + _M_ss;
|
||||
if constexpr (!__is_unsigned)
|
||||
if (_M_is_neg)
|
||||
return -(_M_h + _M_m + _M_s + subseconds());
|
||||
return _M_h + _M_m + _M_s + subseconds();
|
||||
}
|
||||
|
||||
// TODO: Implement operator<<.
|
||||
|
||||
private:
|
||||
bool _M_is_neg;
|
||||
chrono::hours _M_h;
|
||||
chrono::minutes _M_m;
|
||||
chrono::seconds _M_s;
|
||||
precision _M_ss;
|
||||
static constexpr bool __is_unsigned
|
||||
= __and_v<is_integral<typename _Duration::rep>,
|
||||
is_unsigned<typename _Duration::rep>>;
|
||||
|
||||
template<typename _Ratio>
|
||||
using __byte_duration = duration<unsigned char, _Ratio>;
|
||||
|
||||
// The type of the _M_ss member that holds the subsecond precision.
|
||||
template<typename _Dur>
|
||||
struct __subseconds
|
||||
{
|
||||
typename _Dur::rep _M_r{};
|
||||
|
||||
constexpr explicit
|
||||
operator _Dur() const noexcept
|
||||
{ return _Dur(_M_r); }
|
||||
};
|
||||
|
||||
// An empty class if this precision doesn't need subseconds.
|
||||
template<typename _Rep>
|
||||
requires (!treat_as_floating_point_v<_Rep>)
|
||||
struct __subseconds<duration<_Rep, ratio<1>>>
|
||||
{
|
||||
constexpr explicit
|
||||
operator duration<_Rep, ratio<1>>() const noexcept
|
||||
{ return {}; }
|
||||
};
|
||||
|
||||
// True if the maximum constructor argument can be represented in _Tp.
|
||||
template<typename _Tp>
|
||||
static constexpr bool __fits
|
||||
= duration_values<typename _Duration::rep>::max()
|
||||
<= duration_values<_Tp>::max();
|
||||
|
||||
template<typename _Rep, typename _Period>
|
||||
requires (!treat_as_floating_point_v<_Rep>)
|
||||
&& ratio_less_v<_Period, ratio<1, 1>>
|
||||
&& (ratio_greater_equal_v<_Period, ratio<1, 250>>
|
||||
|| __fits<unsigned char>)
|
||||
struct __subseconds<duration<_Rep, _Period>>
|
||||
{
|
||||
unsigned char _M_r{};
|
||||
|
||||
constexpr explicit
|
||||
operator duration<_Rep, _Period>() const noexcept
|
||||
{ return duration<_Rep, _Period>(_M_r); }
|
||||
};
|
||||
|
||||
template<typename _Rep, typename _Period>
|
||||
requires (!treat_as_floating_point_v<_Rep>)
|
||||
&& ratio_less_v<_Period, ratio<1, 250>>
|
||||
&& (ratio_greater_equal_v<_Period, ratio<1, 4'000'000'000>>
|
||||
|| __fits<uint_least32_t>)
|
||||
struct __subseconds<duration<_Rep, _Period>>
|
||||
{
|
||||
uint_least32_t _M_r{};
|
||||
|
||||
constexpr explicit
|
||||
operator duration<_Rep, _Period>() const noexcept
|
||||
{ return duration<_Rep, _Period>(_M_r); }
|
||||
};
|
||||
|
||||
chrono::hours _M_h{};
|
||||
__byte_duration<ratio<60>> _M_m{};
|
||||
__byte_duration<ratio<1>> _M_s{};
|
||||
bool _M_is_neg{};
|
||||
__subseconds<precision> _M_ss{};
|
||||
};
|
||||
|
||||
// 12/24 HOURS FUNCTIONS
|
||||
|
@ -59,5 +59,59 @@ constexpr_hh_mm_ss()
|
||||
|
||||
static_assert(seconds{hh_mm_ss{100min}} == 100min);
|
||||
|
||||
// TODO: treat_as_floating_point_v
|
||||
// treat_as_floating_point_v
|
||||
using fseconds = duration<double, ratio<1>>;
|
||||
constexpr hh_mm_ss<fseconds> fsec{0x123.0004p5s};
|
||||
static_assert(std::is_same_v<hh_mm_ss<fseconds>::precision, fseconds>);
|
||||
static_assert(fsec.hours() == 2h);
|
||||
static_assert(fsec.minutes() == 35min);
|
||||
static_assert(fsec.seconds() == 12s);
|
||||
static_assert(fsec.subseconds() == 0x.0004p5s);
|
||||
static_assert(!fsec.is_negative());
|
||||
static_assert(fsec.to_duration() == 0x123.0004p5s);
|
||||
|
||||
using fminutes = duration<double, ratio<60>>;
|
||||
constexpr hh_mm_ss<fminutes> fmin{-0x1.23p4min};
|
||||
static_assert(std::is_same_v<hh_mm_ss<fminutes>::precision, fseconds>);
|
||||
static_assert(fmin.hours() == 0h);
|
||||
static_assert(fmin.minutes() == 18min);
|
||||
static_assert(fmin.seconds() == 11s);
|
||||
static_assert(fmin.subseconds() == 0.25s);
|
||||
static_assert(fmin.is_negative());
|
||||
static_assert(fmin.to_duration() == -0x1.23p4min);
|
||||
}
|
||||
|
||||
constexpr void
|
||||
default_construction()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
constexpr hh_mm_ss<seconds> s1;
|
||||
static_assert(s1.to_duration() == s1.to_duration().zero());
|
||||
constexpr hh_mm_ss<duration<char>> s2;
|
||||
static_assert(s2.to_duration() == s2.to_duration().zero());
|
||||
constexpr hh_mm_ss<duration<int, std::centi>> s3;
|
||||
static_assert(s3.to_duration() == s3.to_duration().zero());
|
||||
constexpr hh_mm_ss<duration<long long, std::femto>> s4;
|
||||
static_assert(s4.to_duration() == s4.to_duration().zero());
|
||||
constexpr hh_mm_ss<duration<double>> s5;
|
||||
static_assert(s5.to_duration() == s5.to_duration().zero());
|
||||
}
|
||||
|
||||
constexpr void
|
||||
size()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
struct S0 { long long h; char m; char s; bool neg; };
|
||||
static_assert(sizeof(hh_mm_ss<seconds>) == sizeof(S0));
|
||||
struct S1 { long long h; char m; char s; bool neg; char ss; };
|
||||
static_assert(sizeof(hh_mm_ss<duration<int, std::centi>>) == sizeof(S1));
|
||||
struct S2 { long long h; char m, s; bool neg; int ss; };
|
||||
static_assert(sizeof(hh_mm_ss<duration<int, std::milli>>) == sizeof(S2));
|
||||
static_assert(sizeof(hh_mm_ss<duration<int, std::pico>>) == sizeof(S2));
|
||||
struct S3 { long long h; char m, s; bool neg; long long ss; };
|
||||
static_assert(sizeof(hh_mm_ss<duration<long long, std::pico>>) == sizeof(S3));
|
||||
struct S4 { long long h; char m, s; bool neg; double ss; };
|
||||
static_assert(sizeof(hh_mm_ss<duration<double, std::micro>>) == sizeof(S4));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user