mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
Everywhere: Use AK::SaturatingMath and remove Checked saturating APIs
Port all callers of Checked<T>::saturating_add/sub/mul to the new standalone functions in AK/SaturatingMath.h, and remove the old APIs from Checked.
This commit is contained in:
committed by
Andreas Kling
parent
31f816a6d8
commit
eb789e790e
Notes:
github-actions[bot]
2026-03-21 23:21:17 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/eb789e790e8 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8551
64
AK/Checked.h
64
AK/Checked.h
@@ -212,46 +212,6 @@ public:
|
||||
m_value = initial - m_value;
|
||||
}
|
||||
|
||||
constexpr void saturating_sub(T other)
|
||||
{
|
||||
sub(other);
|
||||
// Depending on whether other was positive or negative, we have to saturate to min or max.
|
||||
if (m_overflow && other <= 0)
|
||||
m_value = NumericLimits<T>::max();
|
||||
else if (m_overflow)
|
||||
m_value = NumericLimits<T>::min();
|
||||
m_overflow = false;
|
||||
}
|
||||
|
||||
constexpr void saturating_add(T other)
|
||||
{
|
||||
add(other);
|
||||
// Depending on whether other was positive or negative, we have to saturate to max or min.
|
||||
if (m_overflow && other >= 0)
|
||||
m_value = NumericLimits<T>::max();
|
||||
else if (m_overflow)
|
||||
m_value = NumericLimits<T>::min();
|
||||
m_overflow = false;
|
||||
}
|
||||
|
||||
constexpr void saturating_mul(T other)
|
||||
{
|
||||
// Figure out if the result is positive, negative or zero beforehand.
|
||||
auto either_is_zero = this->m_value == 0 || other == 0;
|
||||
auto result_is_positive = (this->m_value > 0) == (other > 0);
|
||||
|
||||
mul(other);
|
||||
if (m_overflow) {
|
||||
if (either_is_zero)
|
||||
m_value = 0;
|
||||
else if (result_is_positive)
|
||||
m_value = NumericLimits<T>::max();
|
||||
else
|
||||
m_value = NumericLimits<T>::min();
|
||||
}
|
||||
m_overflow = false;
|
||||
}
|
||||
|
||||
constexpr Checked& operator+=(Checked const& other)
|
||||
{
|
||||
m_overflow |= other.m_overflow;
|
||||
@@ -374,30 +334,6 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename U, typename V>
|
||||
static constexpr T saturating_add(U a, V b)
|
||||
{
|
||||
Checked checked { a };
|
||||
checked.saturating_add(b);
|
||||
return checked.value();
|
||||
}
|
||||
|
||||
template<typename U, typename V>
|
||||
static constexpr T saturating_sub(U a, V b)
|
||||
{
|
||||
Checked checked { a };
|
||||
checked.saturating_sub(b);
|
||||
return checked.value();
|
||||
}
|
||||
|
||||
template<typename U, typename V>
|
||||
static constexpr T saturating_mul(U a, V b)
|
||||
{
|
||||
Checked checked { a };
|
||||
checked.saturating_mul(b);
|
||||
return checked.value();
|
||||
}
|
||||
|
||||
template<typename U, typename V>
|
||||
[[nodiscard]] static constexpr bool multiplication_would_overflow(U u, V v)
|
||||
{
|
||||
|
||||
11
AK/Time.cpp
11
AK/Time.cpp
@@ -8,6 +8,7 @@
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/DateConstants.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/SaturatingMath.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Time.h>
|
||||
@@ -227,13 +228,13 @@ 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 seconds_product = saturating_mul(m_seconds, static_cast<i64>(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);
|
||||
time_units = saturating_add(time_units, ((static_cast<i64>(m_nanoseconds) * denominator + remainder_in_nanoseconds + rounding_half) / numerator) / 1'000'000'000);
|
||||
|
||||
return time_units;
|
||||
}
|
||||
@@ -342,9 +343,9 @@ ErrorOr<void> Formatter<Duration>::format(FormatBuilder& builder, Duration value
|
||||
|
||||
size_t integer_align_width = 0;
|
||||
if (align == FormatBuilder::Align::Right)
|
||||
integer_align_width = Checked<size_t>::saturating_sub(align_width, non_integer_width);
|
||||
integer_align_width = saturating_sub(align_width, non_integer_width);
|
||||
else if (align == FormatBuilder::Align::Center)
|
||||
integer_align_width = integer_width + Checked<size_t>::saturating_sub(align_width, total_width) / 2;
|
||||
integer_align_width = integer_width + saturating_sub(align_width, total_width) / 2;
|
||||
TRY(builder.put_u64(seconds, base, false, upper_case, m_zero_pad, m_use_separator, FormatBuilder::Align::Right, integer_align_width, m_fill, m_sign_mode, is_negative));
|
||||
|
||||
if (nanoseconds_to_precision != 0) {
|
||||
@@ -360,7 +361,7 @@ ErrorOr<void> Formatter<Duration>::format(FormatBuilder& builder, Duration value
|
||||
TRY(builder.builder().try_append('s'));
|
||||
|
||||
if (align_width > 0 && align != FormatBuilder::Align::Right) {
|
||||
auto padding_width = Checked<size_t>::saturating_sub(align_width, max(integer_width, integer_align_width) + non_integer_width);
|
||||
auto padding_width = saturating_sub(align_width, max(integer_width, integer_align_width) + non_integer_width);
|
||||
TRY(builder.builder().try_append_repeated(m_fill, padding_width));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <AK/FixedArray.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/SaturatingMath.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibMedia/Audio/SampleSpecification.h>
|
||||
|
||||
@@ -20,7 +21,7 @@ public:
|
||||
Audio::SampleSpecification const& sample_specification() const { return m_sample_specification; }
|
||||
AK::Duration timestamp() const { return m_timestamp; }
|
||||
i64 timestamp_in_samples() const { return m_timestamp_in_samples; }
|
||||
i64 end_timestamp_in_samples() const { return Checked<i64>::saturating_add(m_timestamp_in_samples, AK::clamp_to<i64>(sample_count())); }
|
||||
i64 end_timestamp_in_samples() const { return saturating_add(m_timestamp_in_samples, AK::clamp_to<i64>(sample_count())); }
|
||||
AK::Duration end_timestamp() const { return AK::Duration::from_time_units(end_timestamp_in_samples(), 1, sample_rate()); }
|
||||
Data& data() { return m_data; }
|
||||
Data const& data() const { return m_data; }
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AK/IntegralMath.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/SaturatingMath.h>
|
||||
#include <AK/Time.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibMedia/CodecID.h>
|
||||
@@ -757,12 +758,12 @@ static AK::Duration block_timestamp_to_duration(AK::Duration cluster_timestamp,
|
||||
// of that track. To get the timestamp in nanoseconds of the first frame in a Block or
|
||||
// SimpleBlock, the formula becomes:
|
||||
// `( ( Cluster\Timestamp + ( block timestamp * TrackTimestampScale ) ) * TimestampScale ) - CodecDelay`
|
||||
Checked<i64> timestamp_offset_in_cluster_offset = AK::clamp_to<i64>(static_cast<double>(timestamp_offset * AK::clamp_to<i64>(segment_timestamp_scale)) * track.timestamp_scale());
|
||||
timestamp_offset_in_cluster_offset.saturating_sub(AK::clamp_to<i64>(track.codec_delay()));
|
||||
auto timestamp_offset_in_cluster_offset = AK::clamp_to<i64>(static_cast<double>(timestamp_offset * AK::clamp_to<i64>(segment_timestamp_scale)) * track.timestamp_scale());
|
||||
timestamp_offset_in_cluster_offset = saturating_sub(timestamp_offset_in_cluster_offset, AK::clamp_to<i64>(track.codec_delay()));
|
||||
// This is only mentioned in the elements specification under TrackOffset.
|
||||
// https://www.matroska.org/technical/elements.html
|
||||
timestamp_offset_in_cluster_offset.saturating_add(AK::clamp_to<i64>(track.timestamp_offset()));
|
||||
return cluster_timestamp + AK::Duration::from_nanoseconds(timestamp_offset_in_cluster_offset.value());
|
||||
timestamp_offset_in_cluster_offset = saturating_add(timestamp_offset_in_cluster_offset, AK::clamp_to<i64>(track.timestamp_offset()));
|
||||
return cluster_timestamp + AK::Duration::from_nanoseconds(timestamp_offset_in_cluster_offset);
|
||||
}
|
||||
|
||||
DecoderErrorOr<Vector<ByteBuffer>> SampleIterator::get_frames(Block block)
|
||||
@@ -904,7 +905,7 @@ static DecoderErrorOr<Block> parse_block_group(Streamer& streamer, AK::Duration
|
||||
}
|
||||
case BLOCK_DURATION_ID: {
|
||||
auto duration = TRY(streamer.read_u64());
|
||||
auto duration_nanoseconds = Checked<i64>::saturating_mul(duration, segment_timestamp_scale);
|
||||
auto duration_nanoseconds = saturating_mul(AK::clamp_to<i64>(duration), AK::clamp_to<i64>(segment_timestamp_scale));
|
||||
if (track.timestamp_scale() != 1)
|
||||
duration_nanoseconds = AK::clamp_to<i64>(static_cast<double>(duration_nanoseconds) * track.timestamp_scale());
|
||||
block.set_duration(AK::Duration::from_nanoseconds(duration_nanoseconds));
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <AK/Enumerate.h>
|
||||
#include <AK/SaturatingMath.h>
|
||||
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
||||
#include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
|
||||
#include <LibWasm/AbstractMachine/Configuration.h>
|
||||
@@ -364,10 +365,9 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
||||
if (!table_instance || !elem_instance)
|
||||
return InstantiationError { "Invalid element referenced by active element segment" };
|
||||
|
||||
Checked<size_t> total_size = elem_instance->references().size();
|
||||
total_size.saturating_add(d);
|
||||
auto total_size = saturating_add(elem_instance->references().size(), static_cast<size_t>(d));
|
||||
|
||||
if (total_size.value() > table_instance->elements().size())
|
||||
if (total_size > table_instance->elements().size())
|
||||
return InstantiationError { "Table instantiation out of bounds" };
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/RedBlackTree.h>
|
||||
#include <AK/SIMDExtras.h>
|
||||
#include <AK/SaturatingMath.h>
|
||||
#include <AK/ScopedValueRollback.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibCore/File.h>
|
||||
@@ -2447,10 +2448,8 @@ HANDLE_INSTRUCTION(memory_copy)
|
||||
auto source_offset = configuration.take_source<source_address_mix>(1, addresses.sources).template to<i32>();
|
||||
auto destination_offset = configuration.take_source<source_address_mix>(2, addresses.sources).template to<i32>();
|
||||
|
||||
Checked<size_t> source_position = source_offset;
|
||||
source_position.saturating_add(count);
|
||||
Checked<size_t> destination_position = destination_offset;
|
||||
destination_position.saturating_add(count);
|
||||
auto source_position = saturating_add(static_cast<size_t>(source_offset), static_cast<size_t>(count));
|
||||
auto destination_position = saturating_add(static_cast<size_t>(destination_offset), static_cast<size_t>(count));
|
||||
TRAP_IN_LOOP_IF_NOT(source_position <= source_instance->data().size());
|
||||
TRAP_IN_LOOP_IF_NOT(destination_position <= destination_instance->data().size());
|
||||
|
||||
@@ -2488,10 +2487,8 @@ HANDLE_INSTRUCTION(memory_init)
|
||||
auto source_offset = configuration.take_source<source_address_mix>(1, addresses.sources).template to<u32>();
|
||||
auto destination_offset = configuration.take_source<source_address_mix>(2, addresses.sources).template to<u32>();
|
||||
|
||||
Checked<size_t> source_position = source_offset;
|
||||
source_position.saturating_add(count);
|
||||
Checked<size_t> destination_position = destination_offset;
|
||||
destination_position.saturating_add(count);
|
||||
auto source_position = saturating_add(static_cast<size_t>(source_offset), static_cast<size_t>(count));
|
||||
auto destination_position = saturating_add(static_cast<size_t>(destination_offset), static_cast<size_t>(count));
|
||||
TRAP_IN_LOOP_IF_NOT(source_position <= data.data().size());
|
||||
TRAP_IN_LOOP_IF_NOT(destination_position <= memory->data().size());
|
||||
|
||||
@@ -2566,10 +2563,8 @@ HANDLE_INSTRUCTION(table_copy)
|
||||
auto source_offset = configuration.take_source<source_address_mix>(1, addresses.sources).template to<u32>();
|
||||
auto destination_offset = configuration.take_source<source_address_mix>(2, addresses.sources).template to<u32>();
|
||||
|
||||
Checked<size_t> source_position = source_offset;
|
||||
source_position.saturating_add(count);
|
||||
Checked<size_t> destination_position = destination_offset;
|
||||
destination_position.saturating_add(count);
|
||||
auto source_position = saturating_add(static_cast<size_t>(source_offset), static_cast<size_t>(count));
|
||||
auto destination_position = saturating_add(static_cast<size_t>(destination_offset), static_cast<size_t>(count));
|
||||
TRAP_IN_LOOP_IF_NOT(source_position <= source_instance->elements().size());
|
||||
TRAP_IN_LOOP_IF_NOT(destination_position <= destination_instance->elements().size());
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/SaturatingMath.h>
|
||||
#include <LibWeb/CSS/ComputedProperties.h>
|
||||
#include <LibWeb/CSS/CountersSet.h>
|
||||
#include <LibWeb/DOM/AbstractElement.h>
|
||||
@@ -73,7 +74,7 @@ void CountersSet::increment_a_counter(FlyString name, DOM::AbstractElement const
|
||||
if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) {
|
||||
// FIXME: How should we handle existing counters with no value? Can that happen?
|
||||
VERIFY(existing_counter->value.has_value());
|
||||
existing_counter->value->saturating_add(amount.value());
|
||||
*existing_counter->value = saturating_add(*existing_counter->value, amount);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,7 +82,7 @@ void CountersSet::increment_a_counter(FlyString name, DOM::AbstractElement const
|
||||
// a new counter of the given name with a starting value of 0 before setting or incrementing its value.
|
||||
// https://drafts.csswg.org/css-lists-3/#valdef-counter-set-counter-name-integer
|
||||
auto& counter = instantiate_a_counter(name, element, false, 0);
|
||||
counter.value->saturating_add(amount.value());
|
||||
counter.value = saturating_add(*counter.value, amount);
|
||||
}
|
||||
|
||||
Optional<Counter&> CountersSet::last_counter_with_name(FlyString const& name)
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibWeb/DOM/AbstractElement.h>
|
||||
@@ -17,8 +16,7 @@ namespace Web::CSS {
|
||||
// "UAs may have implementation-specific limits on the maximum or minimum value of a counter.
|
||||
// If a counter reset, set, or increment would push the value outside of that range, the value
|
||||
// must be clamped to that range." - https://drafts.csswg.org/css-lists-3/#auto-numbering
|
||||
// So, we use a Checked<i32> and saturating addition/subtraction.
|
||||
using CounterValue = Checked<i32>;
|
||||
using CounterValue = i32;
|
||||
|
||||
// https://drafts.csswg.org/css-lists-3/#counter
|
||||
struct Counter {
|
||||
|
||||
@@ -49,7 +49,7 @@ String CounterStyleValue::resolve(DOM::AbstractElement& element_reference) const
|
||||
if (m_properties.function == CounterFunction::Counter) {
|
||||
// NOTE: This should always be present because of the handling of a missing counter above.
|
||||
auto& counter = counters_set.last_counter_with_name(m_properties.counter_name).value();
|
||||
return generate_a_counter_representation(m_properties.counter_style->as_counter_style().resolve_counter_style(registered_counter_styles), registered_counter_styles, counter.value.value_or(0).value());
|
||||
return generate_a_counter_representation(m_properties.counter_style->as_counter_style().resolve_counter_style(registered_counter_styles), registered_counter_styles, counter.value.value_or(0));
|
||||
}
|
||||
|
||||
// counters( <counter-name>, <string>, <counter-style>? )
|
||||
@@ -62,7 +62,7 @@ String CounterStyleValue::resolve(DOM::AbstractElement& element_reference) const
|
||||
if (counter.name != m_properties.counter_name)
|
||||
continue;
|
||||
|
||||
auto counter_string = generate_a_counter_representation(m_properties.counter_style->as_counter_style().resolve_counter_style(registered_counter_styles), registered_counter_styles, counter.value.value_or(0).value());
|
||||
auto counter_string = generate_a_counter_representation(m_properties.counter_style->as_counter_style().resolve_counter_style(registered_counter_styles), registered_counter_styles, counter.value.value_or(0));
|
||||
if (!stb.is_empty())
|
||||
stb.append(m_properties.join_string);
|
||||
stb.append(counter_string);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/DistinctNumeric.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/SaturatingMath.h>
|
||||
#include <AK/Traits.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
@@ -159,12 +160,12 @@ public:
|
||||
|
||||
constexpr CSSPixels& operator++()
|
||||
{
|
||||
m_value = Checked<int>::saturating_add(m_value, fixed_point_denominator);
|
||||
m_value = saturating_add(m_value, fixed_point_denominator);
|
||||
return *this;
|
||||
}
|
||||
constexpr CSSPixels& operator--()
|
||||
{
|
||||
m_value = Checked<int>::saturating_sub(m_value, fixed_point_denominator);
|
||||
m_value = saturating_sub(m_value, fixed_point_denominator);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -182,12 +183,12 @@ public:
|
||||
|
||||
constexpr CSSPixels operator+(CSSPixels const& other) const
|
||||
{
|
||||
return from_raw(Checked<int>::saturating_add(raw_value(), other.raw_value()));
|
||||
return from_raw(saturating_add(raw_value(), other.raw_value()));
|
||||
}
|
||||
|
||||
constexpr CSSPixels operator-(CSSPixels const& other) const
|
||||
{
|
||||
return from_raw(Checked<int>::saturating_sub(raw_value(), other.raw_value()));
|
||||
return from_raw(saturating_sub(raw_value(), other.raw_value()));
|
||||
}
|
||||
|
||||
constexpr CSSPixels operator*(CSSPixels const& other) const
|
||||
@@ -203,11 +204,11 @@ public:
|
||||
// If any bit after was 1 as well
|
||||
if (value & (radix_mask >> 1u)) {
|
||||
// We need to round away from 0
|
||||
int_value = Checked<int>::saturating_add(int_value, 1);
|
||||
int_value = saturating_add(int_value, 1);
|
||||
} else {
|
||||
// Otherwise we round to the next even value
|
||||
// Which means we add the least significant bit of the raw integer value
|
||||
int_value = Checked<int>::saturating_add(int_value, int_value & 1);
|
||||
int_value = saturating_add(int_value, int_value & 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user