AK: Take advantage of constexpr in Time and add time conversion methods

By making the Time constructor constexpr we can optimize creating a
Time instance from hardcoded values.

Also add more functions to convert between Time and various time units.
This commit is contained in:
Tom 2021-03-07 23:34:13 -07:00 committed by Andreas Kling
parent 42bc229500
commit 183b2e71ba
3 changed files with 207 additions and 18 deletions

View file

@ -141,9 +141,14 @@ TEST_CASE(timeval_parsing)
TEST_CASE(addition)
{
#define EXPECT_ADDITION(s1, ns1, s2, ns2, sr, nsr) \
EXPECT_TIME(TIME(s1, ns1) + TIME(s2, ns2), sr, nsr); \
EXPECT_TIME(TIME(s2, ns2) + TIME(s1, ns1), sr, nsr);
#define EXPECT_ADDITION(s1, ns1, s2, ns2, sr, nsr) \
do { \
EXPECT_TIME(TIME(s1, ns1) + TIME(s2, ns2), sr, nsr); \
EXPECT_TIME(TIME(s2, ns2) + TIME(s1, ns1), sr, nsr); \
auto t = TIME(s1, ns1); \
t += TIME(s2, ns2); \
EXPECT_TIME(t, sr, nsr); \
} while (0)
EXPECT_ADDITION(11, 123'456'789, 22, 900'000'000, 34, 23'456'789);
@ -171,8 +176,13 @@ TEST_CASE(addition)
TEST_CASE(subtraction)
{
#define EXPECT_SUBTRACTION(s1, ns1, s2, ns2, sr, nsr) \
EXPECT_TIME(TIME(s1, ns1) - TIME(s2, ns2), sr, nsr);
#define EXPECT_SUBTRACTION(s1, ns1, s2, ns2, sr, nsr) \
do { \
EXPECT_TIME(TIME(s1, ns1) - TIME(s2, ns2), sr, nsr); \
auto t = TIME(s1, ns1); \
t -= TIME(s2, ns2); \
EXPECT_TIME(t, sr, nsr); \
} while (0)
EXPECT_SUBTRACTION(5, 0, 3, 0, 2, 0);
EXPECT_SUBTRACTION(0, 0, 0, 0, 0, 0);
@ -202,10 +212,31 @@ TEST_CASE(subtraction)
EXPECT_SUBTRACTION(-0x7fff'ffff'ffff'ffff, 999'999'995, 1, 999'999'996, (i64)-0x8000'0000'0000'0000, 0);
}
TEST_CASE(rounding)
{
EXPECT_EQ(TIME(2, 800'800'800).to_seconds(), 3);
EXPECT_EQ(TIME(2, 800'800'800).to_milliseconds(), 2'801);
EXPECT_EQ(TIME(2, 800'800'800).to_microseconds(), 2'800'801);
EXPECT_EQ(TIME(2, 800'800'800).to_nanoseconds(), 2'800'800'800);
EXPECT_EQ(TIME(-2, 800'800'800).to_seconds(), -2);
EXPECT_EQ(TIME(-2, 800'800'800).to_milliseconds(), -1'200);
EXPECT_EQ(TIME(-2, 800'800'800).to_microseconds(), -1'199'200);
EXPECT_EQ(TIME(-2, 800'800'800).to_nanoseconds(), -1'199'199'200);
EXPECT_EQ(TIME(0, 0).to_seconds(), 0);
EXPECT_EQ(TIME(0, 0).to_milliseconds(), 0);
EXPECT_EQ(TIME(0, 0).to_microseconds(), 0);
EXPECT_EQ(TIME(0, 0).to_nanoseconds(), 0);
}
TEST_CASE(truncation)
{
EXPECT_EQ(TIME(2, 800'000'000).to_truncated_seconds(), 2);
EXPECT_EQ(TIME(-2, -800'000'000).to_truncated_seconds(), -2);
EXPECT_EQ(TIME(2, 800'800'800).to_truncated_seconds(), 2);
EXPECT_EQ(TIME(2, 800'800'800).to_truncated_milliseconds(), 2'800);
EXPECT_EQ(TIME(2, 800'800'800).to_truncated_microseconds(), 2'800'800);
EXPECT_EQ(TIME(-2, -800'800'800).to_truncated_seconds(), -2);
EXPECT_EQ(TIME(-2, -800'800'800).to_truncated_milliseconds(), -2'800);
EXPECT_EQ(TIME(-2, -800'800'800).to_truncated_microseconds(), -2'800'800);
EXPECT_EQ(TIME(0, 0).to_truncated_seconds(), 0);
EXPECT_EQ(TIME(1, 999'999'999).to_truncated_seconds(), 1);

View file

@ -72,10 +72,6 @@ unsigned day_of_week(int year, unsigned month, int day)
return (year + year / 4 - year / 100 + year / 400 + seek_table[month - 1] + day) % 7;
}
Time Time::from_nanoseconds(i32 nanoseconds)
{
return Time::from_timespec({ 0, nanoseconds });
};
ALWAYS_INLINE static i32 sane_mod(i32& numerator, i32 denominator)
{
VERIFY(2 <= denominator && denominator <= 1'000'000'000);
@ -109,10 +105,123 @@ Time Time::from_timeval(const struct timeval& tv)
i64 Time::to_truncated_seconds() const
{
VERIFY(m_nanoseconds < 1'000'000'000);
if (m_seconds < 0 && m_nanoseconds)
return m_seconds + 1;
else
return m_seconds;
if (m_seconds < 0 && m_nanoseconds) {
Checked<i64> seconds(m_seconds);
seconds++;
return seconds.has_overflow() ? 0x7fff'ffff'ffff'ffffLL : seconds.value();
}
return m_seconds;
}
i64 Time::to_truncated_milliseconds() const
{
VERIFY(m_nanoseconds < 1'000'000'000);
Checked<i64> milliseconds(m_seconds);
milliseconds *= 1'000;
if (!milliseconds.has_overflow()) {
u32 add_ms = (u32)(m_nanoseconds / 1'000'000);
if (add_ms) {
milliseconds += add_ms;
if (m_seconds < 0 && m_nanoseconds % 1'000'000 != 0)
milliseconds++;
if (!milliseconds.has_overflow())
return milliseconds.value();
} else {
return milliseconds.value();
}
}
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
}
i64 Time::to_truncated_microseconds() const
{
VERIFY(m_nanoseconds < 1'000'000'000);
Checked<i64> microseconds(m_seconds);
microseconds *= 1'000'000;
if (!microseconds.has_overflow()) {
u32 add_us = (u32)(m_nanoseconds / 1'000);
if (add_us) {
microseconds += add_us;
if (m_seconds < 0 && m_nanoseconds % 1'000 != 0)
microseconds++;
if (!microseconds.has_overflow())
return microseconds.value();
} else {
return microseconds.value();
}
}
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
}
i64 Time::to_seconds() const
{
VERIFY(m_nanoseconds < 1'000'000'000);
if (m_seconds >= 0 && m_nanoseconds) {
Checked<i64> seconds(m_seconds);
seconds++;
return seconds.has_overflow() ? 0x7fff'ffff'ffff'ffffLL : seconds.value();
}
return m_seconds;
}
i64 Time::to_milliseconds() const
{
VERIFY(m_nanoseconds < 1'000'000'000);
Checked<i64> milliseconds(m_seconds);
milliseconds *= 1'000;
if (!milliseconds.has_overflow()) {
u32 add_ms = (u32)(m_nanoseconds / 1'000'000);
if (add_ms) {
milliseconds += add_ms;
if (!milliseconds.has_overflow()) {
if (m_seconds >= 0 && m_nanoseconds % 1'000'000 != 0) {
milliseconds++;
if (!milliseconds.has_overflow())
return milliseconds.value();
} else {
return milliseconds.value();
}
}
} else {
return milliseconds.value();
}
}
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
}
i64 Time::to_microseconds() const
{
VERIFY(m_nanoseconds < 1'000'000'000);
Checked<i64> microseconds(m_seconds);
microseconds *= 1'000'000;
if (!microseconds.has_overflow()) {
u32 add_us = (u32)(m_nanoseconds / 1'000);
if (add_us) {
microseconds += add_us;
if (!microseconds.has_overflow()) {
if (m_seconds >= 0 && m_nanoseconds % 1'000 != 0) {
microseconds++;
if (!microseconds.has_overflow())
return microseconds.value();
} else {
return microseconds.value();
}
}
} else {
return microseconds.value();
}
}
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
}
i64 Time::to_nanoseconds() const
{
VERIFY(m_nanoseconds < 1'000'000'000);
Checked<i64> nanoseconds(m_seconds);
nanoseconds *= 1'000'000'000;
if (!nanoseconds.has_overflow()) {
if (m_nanoseconds) {
nanoseconds += m_nanoseconds;
if (!nanoseconds.has_overflow())
return nanoseconds.value();
}
return nanoseconds.value();
}
return m_seconds < 0 ? -0x8000'0000'0000'0000LL : 0x7fff'ffff'ffff'ffffLL;
}
timespec Time::to_timespec() const
{
@ -165,6 +274,13 @@ Time Time::operator+(const Time& other) const
return Time { new_secs.value(), new_nsecs };
}
Time& Time::operator+=(const Time& other)
{
*this = *this + other;
return *this;
}
Time Time::operator-(const Time& other) const
{
VERIFY(m_nanoseconds < 1'000'000'000);
@ -184,6 +300,12 @@ Time Time::operator-(const Time& other) const
return Time { (m_seconds + 0x4000'0000'0000'0000) + 0x4000'0000'0000'0000, m_nanoseconds };
}
Time& Time::operator-=(const Time& other)
{
*this = *this - other;
return *this;
}
bool Time::operator<(const Time& other) const
{
return m_seconds < other.m_seconds || (m_seconds == other.m_seconds && m_nanoseconds < other.m_nanoseconds);

View file

@ -84,9 +84,35 @@ class Time {
public:
Time() = default;
Time(const Time&) = default;
Time& operator=(const Time&) = default;
static Time from_seconds(i64 seconds) { return Time(seconds, 0); }
static Time from_nanoseconds(i32 nanoseconds);
Time(Time&& other)
: m_seconds(exchange(other.m_seconds, 0))
, m_nanoseconds(exchange(other.m_nanoseconds, 0))
{
}
Time& operator=(Time&& other)
{
if (this != &other) {
m_seconds = exchange(other.m_seconds, 0);
m_nanoseconds = exchange(other.m_nanoseconds, 0);
}
return *this;
}
constexpr static Time from_seconds(i64 seconds) { return Time(seconds, 0); }
constexpr static Time from_nanoseconds(i64 nanoseconds)
{
return Time(nanoseconds / 1'000'000'000, nanoseconds % 1'000'000'000);
}
constexpr static Time from_microseconds(i64 microseconds)
{
return Time(microseconds / 1'000'000, (microseconds % 1'000'000) * 1'000);
}
constexpr static Time from_milliseconds(i64 milliseconds)
{
return Time(milliseconds / 1'000, (milliseconds % 1'000) * 1'000'000);
}
static Time from_timespec(const struct timespec&);
static Time from_timeval(const struct timeval&);
static Time min() { return Time(-0x8000'0000'0000'0000LL, 0); };
@ -96,20 +122,30 @@ public:
// Truncates "2.8 seconds" to 2 seconds.
// Truncates "-2.8 seconds" to -2 seconds.
i64 to_truncated_seconds() const;
i64 to_truncated_milliseconds() const;
i64 to_truncated_microseconds() const;
i64 to_seconds() const;
i64 to_milliseconds() const;
i64 to_microseconds() const;
i64 to_nanoseconds() const;
timespec to_timespec() const;
timeval to_timeval() const;
bool is_zero() const { return !m_seconds && !m_nanoseconds; }
bool operator==(const Time& other) const { return this->m_seconds == other.m_seconds && this->m_nanoseconds == other.m_nanoseconds; }
bool operator!=(const Time& other) const { return !(*this == other); }
Time operator+(const Time& other) const;
Time& operator+=(const Time& other);
Time operator-(const Time& other) const;
Time& operator-=(const Time& other);
bool operator<(const Time& other) const;
bool operator<=(const Time& other) const;
bool operator>(const Time& other) const;
bool operator>=(const Time& other) const;
private:
explicit Time(i64 seconds, u32 nanoseconds)
constexpr explicit Time(i64 seconds, u32 nanoseconds)
: m_seconds(seconds)
, m_nanoseconds(nanoseconds)
{