diff --git a/AK/Checked.h b/AK/Checked.h index 2ff5c6afa9..353e6e9e49 100644 --- a/AK/Checked.h +++ b/AK/Checked.h @@ -234,6 +234,24 @@ public: 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::max(); + else + m_value = NumericLimits::min(); + } + m_overflow = false; + } + constexpr Checked& operator+=(Checked const& other) { m_overflow |= other.m_overflow; @@ -354,6 +372,14 @@ public: return checked.value(); } + template + static constexpr T saturating_mul(U a, V b) + { + Checked checked { a }; + checked.saturating_mul(b); + return checked.value(); + } + template [[nodiscard]] static constexpr bool multiplication_would_overflow(U u, V v) { diff --git a/Userland/Libraries/LibAudio/FlacLoader.cpp b/Userland/Libraries/LibAudio/FlacLoader.cpp index afa8b05675..b9d37d790e 100644 --- a/Userland/Libraries/LibAudio/FlacLoader.cpp +++ b/Userland/Libraries/LibAudio/FlacLoader.cpp @@ -797,16 +797,16 @@ ErrorOr FlacLoaderPlugin::decode_custom_lpc(Vector& deco // approximate the waveform with the predictor for (size_t i = subframe.order; i < m_current_frame->sample_count; ++i) { // (see below) - i64 sample = 0; + Checked sample = 0; for (size_t t = 0; t < subframe.order; ++t) { // It's really important that we compute in 64-bit land here. // Even though FLAC operates at a maximum bit depth of 32 bits, modern encoders use super-large coefficients for maximum compression. // These will easily overflow 32 bits and cause strange white noise that abruptly stops intermittently (at the end of a frame). - // The simple fix of course is to do intermediate computations in 64 bits. + // The simple fix of course is to do intermediate computations in 64 bits, but we additionally use saturating arithmetic. // These considerations are not in the original FLAC spec, but have been added to the IETF standard: https://datatracker.ietf.org/doc/html/draft-ietf-cellar-flac-03#appendix-A.3 - sample += static_cast(coefficients[t]) * static_cast(decoded[i - t - 1]); + sample.saturating_add(Checked::saturating_mul(static_cast(coefficients[t]), static_cast(decoded[i - t - 1]))); } - decoded[i] += sample >> lpc_shift; + decoded[i] += sample.value() >> lpc_shift; } return {};