mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
AK: Add SaturatingMath.h with branchless saturating arithmetic
Add standalone saturating_add(), saturating_sub(), and saturating_mul() free functions for integral types. The signed implementations are fully branchless, using __builtin_add/sub/mul_overflow combined with bitmask selection.
This commit is contained in:
committed by
Andreas Kling
parent
be56df8d4f
commit
31f816a6d8
Notes:
github-actions[bot]
2026-03-21 23:21:24 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/31f816a6d88 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8551
85
AK/SaturatingMath.h
Normal file
85
AK/SaturatingMath.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<Integral T>
|
||||
requires(Signed<T>)
|
||||
constexpr T saturating_add(T a, T b)
|
||||
{
|
||||
using U = MakeUnsigned<T>;
|
||||
T result;
|
||||
U overflowed = __builtin_add_overflow(a, b, &result);
|
||||
T saturated = static_cast<T>(static_cast<U>(NumericLimits<T>::max()) + (static_cast<U>(a) >> (sizeof(T) * 8 - 1)));
|
||||
U mask = -overflowed;
|
||||
return static_cast<T>((static_cast<U>(saturated) & mask) | (static_cast<U>(result) & ~mask));
|
||||
}
|
||||
|
||||
template<Integral T>
|
||||
requires(Unsigned<T>)
|
||||
constexpr T saturating_add(T a, T b)
|
||||
{
|
||||
T result = a + b;
|
||||
result |= -static_cast<T>(result < a);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<Integral T>
|
||||
requires(Signed<T>)
|
||||
constexpr T saturating_sub(T a, T b)
|
||||
{
|
||||
using U = MakeUnsigned<T>;
|
||||
T result;
|
||||
U overflowed = __builtin_sub_overflow(a, b, &result);
|
||||
T saturated = static_cast<T>(static_cast<U>(NumericLimits<T>::max()) + (static_cast<U>(a) >> (sizeof(T) * 8 - 1)));
|
||||
U mask = -overflowed;
|
||||
return static_cast<T>((static_cast<U>(saturated) & mask) | (static_cast<U>(result) & ~mask));
|
||||
}
|
||||
|
||||
template<Integral T>
|
||||
requires(Unsigned<T>)
|
||||
constexpr T saturating_sub(T a, T b)
|
||||
{
|
||||
T result = a - b;
|
||||
result &= -static_cast<T>(result <= a);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<Integral T>
|
||||
requires(Signed<T>)
|
||||
constexpr T saturating_mul(T a, T b)
|
||||
{
|
||||
using U = MakeUnsigned<T>;
|
||||
T result;
|
||||
U overflowed = __builtin_mul_overflow(a, b, &result);
|
||||
// Same signs → positive overflow → max. Different signs → negative overflow → min.
|
||||
T saturated = static_cast<T>(static_cast<U>(NumericLimits<T>::max()) + ((static_cast<U>(a) ^ static_cast<U>(b)) >> (sizeof(T) * 8 - 1)));
|
||||
U mask = -overflowed;
|
||||
return static_cast<T>((static_cast<U>(saturated) & mask) | (static_cast<U>(result) & ~mask));
|
||||
}
|
||||
|
||||
template<Integral T>
|
||||
requires(Unsigned<T>)
|
||||
constexpr T saturating_mul(T a, T b)
|
||||
{
|
||||
T result;
|
||||
if (__builtin_mul_overflow(a, b, &result))
|
||||
return NumericLimits<T>::max();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if USING_AK_GLOBALLY
|
||||
using AK::saturating_add;
|
||||
using AK::saturating_mul;
|
||||
using AK::saturating_sub;
|
||||
#endif
|
||||
Reference in New Issue
Block a user