From 183b2e71ba8d85293db493cab27b8adb4af54981 Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 7 Mar 2021 23:34:13 -0700 Subject: [PATCH] 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. --- AK/Tests/TestTime.cpp | 45 +++++++++++--- AK/Time.cpp | 138 +++++++++++++++++++++++++++++++++++++++--- AK/Time.h | 42 ++++++++++++- 3 files changed, 207 insertions(+), 18 deletions(-) diff --git a/AK/Tests/TestTime.cpp b/AK/Tests/TestTime.cpp index 0e79338b6e..7999be706d 100644 --- a/AK/Tests/TestTime.cpp +++ b/AK/Tests/TestTime.cpp @@ -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); diff --git a/AK/Time.cpp b/AK/Time.cpp index 1f4eb9ec93..30b550c33f 100644 --- a/AK/Time.cpp +++ b/AK/Time.cpp @@ -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 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 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 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 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 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 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 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); diff --git a/AK/Time.h b/AK/Time.h index 20633f1070..e3ecb2724a 100644 --- a/AK/Time.h +++ b/AK/Time.h @@ -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) {