mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
AK+Tests: Add time units conversion functions to Duration
These take a numerator and denominator defining the unit in a fractions of a second. Conversion is done in integers, meaning that these must clamp when approaching numeric limits.
This commit is contained in:
parent
ec14948175
commit
a1358fa970
Notes:
github-actions[bot]
2025-11-17 15:53:19 +00:00
Author: https://github.com/Zaggy1024
Commit: a1358fa970
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6786
Reviewed-by: https://github.com/gmta ✅
3 changed files with 81 additions and 0 deletions
38
AK/Time.cpp
38
AK/Time.cpp
|
|
@ -67,6 +67,28 @@ Duration Duration::from_timeval(const struct timeval& tv)
|
|||
return Duration::from_half_sanitized(tv.tv_sec, extra_secs, usecs * 1'000);
|
||||
}
|
||||
|
||||
Duration Duration::from_time_units(i64 time_units, u32 numerator, u32 denominator)
|
||||
{
|
||||
VERIFY(numerator != 0);
|
||||
VERIFY(denominator != 0);
|
||||
|
||||
auto seconds_checked = Checked<i64>(time_units);
|
||||
seconds_checked.mul(numerator);
|
||||
seconds_checked.div(denominator);
|
||||
if (time_units < 0)
|
||||
seconds_checked.sub(1);
|
||||
|
||||
if (seconds_checked.has_overflow())
|
||||
return Duration(time_units >= 0 ? NumericLimits<i64>::max() : NumericLimits<i64>::min(), 0);
|
||||
auto seconds = seconds_checked.value_unchecked();
|
||||
auto seconds_in_time_units = seconds * denominator / numerator;
|
||||
auto remainder_in_time_units = time_units - seconds_in_time_units;
|
||||
auto nanoseconds = ((remainder_in_time_units * 1'000'000'000 * numerator) + (denominator / 2)) / denominator;
|
||||
VERIFY(nanoseconds >= 0);
|
||||
VERIFY(nanoseconds < 1'000'000'000);
|
||||
return Duration(seconds, static_cast<u32>(nanoseconds));
|
||||
}
|
||||
|
||||
i64 Duration::to_truncated_seconds() const
|
||||
{
|
||||
VERIFY(m_nanoseconds < 1'000'000'000);
|
||||
|
|
@ -196,6 +218,22 @@ timeval Duration::to_timeval() const
|
|||
return { static_cast<sec_type>(m_seconds), static_cast<usec_type>(m_nanoseconds) / 1000 };
|
||||
}
|
||||
|
||||
i64 Duration::to_time_units(u32 numerator, u32 denominator) const
|
||||
{
|
||||
VERIFY(numerator != 0);
|
||||
VERIFY(denominator != 0);
|
||||
|
||||
auto seconds_product = Checked<i64>::saturating_mul(m_seconds, denominator);
|
||||
auto time_units = seconds_product / numerator;
|
||||
auto remainder = seconds_product % numerator;
|
||||
|
||||
auto remainder_in_nanoseconds = remainder * 1'000'000'000;
|
||||
auto rounding_half = static_cast<i64>(numerator) * 500'000'000;
|
||||
time_units = Checked<i64>::saturating_add(time_units, ((static_cast<i64>(m_nanoseconds) * denominator + remainder_in_nanoseconds + rounding_half) / numerator) / 1'000'000'000);
|
||||
|
||||
return time_units;
|
||||
}
|
||||
|
||||
Duration Duration::from_half_sanitized(i64 seconds, i32 extra_seconds, u32 nanoseconds)
|
||||
{
|
||||
VERIFY(nanoseconds < 1'000'000'000);
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ public:
|
|||
[[nodiscard]] static Duration from_ticks(clock_t, time_t);
|
||||
[[nodiscard]] static Duration from_timespec(const struct timespec&);
|
||||
[[nodiscard]] static Duration from_timeval(const struct timeval&);
|
||||
[[nodiscard]] static Duration from_time_units(i64 units, u32 numerator, u32 denominator);
|
||||
// We don't pull in <stdint.h> for the pretty min/max definitions because this file is also included in the Kernel
|
||||
[[nodiscard]] constexpr static Duration min() { return Duration(-__INT64_MAX__ - 1LL, 0); }
|
||||
[[nodiscard]] constexpr static Duration zero() { return Duration(0, 0); }
|
||||
|
|
@ -263,6 +264,7 @@ public:
|
|||
[[nodiscard]] timespec to_timespec() const;
|
||||
// Rounds towards -inf (it was the easiest to implement).
|
||||
[[nodiscard]] timeval to_timeval() const;
|
||||
[[nodiscard]] i64 to_time_units(u32 numerator, u32 denominator) const;
|
||||
|
||||
[[nodiscard]] bool is_zero() const { return (m_seconds == 0) && (m_nanoseconds == 0); }
|
||||
[[nodiscard]] bool is_negative() const { return m_seconds < 0; }
|
||||
|
|
|
|||
|
|
@ -939,3 +939,44 @@ TEST_CASE(from_f64_seconds)
|
|||
|
||||
EXPECT_DEATH("Converting float NaN seconds", (void)Duration::from_seconds_f64(NAN));
|
||||
}
|
||||
|
||||
TEST_CASE(time_units)
|
||||
{
|
||||
EXPECT_EQ(Duration::from_time_units(1, 1, 1), Duration::from_seconds(1));
|
||||
EXPECT_EQ(Duration::from_time_units(-312, 1, 48'000), Duration::from_microseconds(-6'500));
|
||||
EXPECT_EQ(Duration::from_time_units(960, 1, 48'000), Duration::from_microseconds(20'000));
|
||||
EXPECT_EQ(Duration::from_time_units(960, 1, 48'000), Duration::from_microseconds(20'000));
|
||||
EXPECT_EQ(Duration::from_time_units(8, 4, 1), Duration::from_seconds(32));
|
||||
EXPECT_EQ(Duration::from_time_units(3, 3, 2'000'000'000), Duration::from_nanoseconds(5));
|
||||
EXPECT_EQ(Duration::from_time_units(4, 3, 2'000'000'000), Duration::from_nanoseconds(6));
|
||||
EXPECT_EQ(Duration::from_time_units(999'999'998, 1, 2'000'000'000), Duration::from_nanoseconds(499'999'999));
|
||||
EXPECT_EQ(Duration::from_time_units(999'999'999, 1, 2'000'000'000), Duration::from_nanoseconds(500'000'000));
|
||||
EXPECT_EQ(Duration::from_time_units(1'000'000'000, 1, 2'000'000'000), Duration::from_nanoseconds(500'000'000));
|
||||
|
||||
EXPECT_EQ(Duration::from_time_units(NumericLimits<i64>::max(), 1, 2), Duration::from_seconds(NumericLimits<i64>::max() / 2) + Duration::from_milliseconds(500));
|
||||
EXPECT_EQ(Duration::from_time_units((NumericLimits<i64>::max() / 2), 2, 1), Duration::from_seconds(NumericLimits<i64>::max() - 1));
|
||||
EXPECT_EQ(Duration::from_time_units((NumericLimits<i64>::max() / 2) + 1, 2, 1), Duration::from_seconds(NumericLimits<i64>::max()));
|
||||
EXPECT_EQ(Duration::from_time_units((NumericLimits<i64>::min() / 2), 2, 1), Duration::from_seconds(NumericLimits<i64>::min()));
|
||||
EXPECT_EQ(Duration::from_time_units((NumericLimits<i64>::min() / 2) - 1, 2, 1), Duration::from_seconds(NumericLimits<i64>::min()));
|
||||
|
||||
EXPECT_EQ(Duration::from_milliseconds(999).to_time_units(1, 48'000), 47'952);
|
||||
EXPECT_EQ(Duration::from_milliseconds(-12'500).to_time_units(1, 1'000), -12'500);
|
||||
EXPECT_EQ(Duration::from_milliseconds(-12'500).to_time_units(1, 1'000), -12'500);
|
||||
|
||||
EXPECT_EQ(Duration::from_nanoseconds(154'489'696).to_time_units(1, 48'000), 7'416);
|
||||
EXPECT_EQ(Duration::from_nanoseconds(154'489'375).to_time_units(1, 48'000), 7'415);
|
||||
EXPECT_EQ(Duration::from_nanoseconds(-154'489'696).to_time_units(1, 48'000), -7'416);
|
||||
EXPECT_EQ(Duration::from_nanoseconds(-154'489'375).to_time_units(1, 48'000), -7'415);
|
||||
EXPECT_EQ(Duration::from_nanoseconds(1'900'000'000).to_time_units(3, 2), 1);
|
||||
EXPECT_EQ(Duration::from_nanoseconds(1'800'000'000).to_time_units(3, 1), 1);
|
||||
EXPECT_EQ(Duration::from_seconds(3).to_time_units(4, 1), 1);
|
||||
EXPECT_EQ(Duration::from_seconds(4).to_time_units(4, 1), 1);
|
||||
EXPECT_EQ(Duration::from_seconds(5).to_time_units(4, 1), 1);
|
||||
EXPECT_EQ(Duration::from_seconds(6).to_time_units(4, 1), 2);
|
||||
|
||||
EXPECT_EQ(Duration::from_seconds(2'147'483'649).to_time_units(1, NumericLimits<u32>::max()), NumericLimits<i64>::max());
|
||||
EXPECT_EQ(Duration::from_seconds(2'147'483'648).to_time_units(1, NumericLimits<u32>::max()), NumericLimits<i64>::max() - (NumericLimits<u32>::max() / 2));
|
||||
|
||||
EXPECT_DEATH("From time units with zero numerator", (void)Duration::from_time_units(1, 0, 1));
|
||||
EXPECT_DEATH("From time units with zero denominator", (void)Duration::from_time_units(1, 1, 0));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue