mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 17:10:23 +00:00
c6fafd3e90
Previously, in LibGFX's `Point` class, calculated distances were passed to the integer `abs` function, even if the stored type was a float. This caused the value to unexpectedly be truncated. Luckily, this API was not used with floating point types, but that can change in the future, so why not fix it now :^) Since we are in C++, we can use function overloading to make things easy, and to automatically use the right version. This is even better than the LibC/LibM functions, as using a bit of hackery, they are able to be constant-evaluated. They use compiler intrinsics, so they do not depend on external code and the compiler can emit the most optimized code by default. Since we aren't using the C++ standard library's trick of importing everything into the `AK` namespace, this `abs` function cannot be exported to the global namespace, as the names would clash.
160 lines
3.4 KiB
C++
160 lines
3.4 KiB
C++
/*
|
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/StdLibExtraDetails.h>
|
|
|
|
#include <AK/Assertions.h>
|
|
|
|
template<typename T, typename U>
|
|
constexpr auto round_up_to_power_of_two(T value, U power_of_two) requires(IsIntegral<T>&& IsIntegral<U>)
|
|
{
|
|
return ((value - 1) & ~(power_of_two - 1)) + power_of_two;
|
|
}
|
|
|
|
namespace std {
|
|
|
|
// NOTE: This is in the "std" namespace since some compiler features rely on it.
|
|
|
|
template<typename T>
|
|
constexpr T&& move(T& arg)
|
|
{
|
|
return static_cast<T&&>(arg);
|
|
}
|
|
|
|
}
|
|
|
|
using std::move;
|
|
|
|
namespace AK::Detail {
|
|
template<typename T>
|
|
struct _RawPtr {
|
|
using Type = T*;
|
|
};
|
|
}
|
|
|
|
namespace AK {
|
|
|
|
template<class T>
|
|
constexpr T&& forward(RemoveReference<T>& param)
|
|
{
|
|
return static_cast<T&&>(param);
|
|
}
|
|
|
|
template<class T>
|
|
constexpr T&& forward(RemoveReference<T>&& param) noexcept
|
|
{
|
|
static_assert(!IsLvalueReference<T>, "Can't forward an rvalue as an lvalue.");
|
|
return static_cast<T&&>(param);
|
|
}
|
|
|
|
template<typename T, typename SizeType = decltype(sizeof(T)), SizeType N>
|
|
constexpr SizeType array_size(T (&)[N])
|
|
{
|
|
return N;
|
|
}
|
|
|
|
template<typename T>
|
|
constexpr T min(const T& a, const IdentityType<T>& b)
|
|
{
|
|
return b < a ? b : a;
|
|
}
|
|
|
|
template<typename T>
|
|
constexpr T max(const T& a, const IdentityType<T>& b)
|
|
{
|
|
return a < b ? b : a;
|
|
}
|
|
|
|
template<typename T>
|
|
constexpr T clamp(const T& value, const IdentityType<T>& min, const IdentityType<T>& max)
|
|
{
|
|
VERIFY(max >= min);
|
|
if (value > max)
|
|
return max;
|
|
if (value < min)
|
|
return min;
|
|
return value;
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
constexpr T ceil_div(T a, U b)
|
|
{
|
|
static_assert(sizeof(T) == sizeof(U));
|
|
T result = a / b;
|
|
if ((a % b) != 0)
|
|
++result;
|
|
return result;
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
inline void swap(T& a, U& b)
|
|
{
|
|
U tmp = move((U&)a);
|
|
a = (T &&) move(b);
|
|
b = move(tmp);
|
|
}
|
|
|
|
template<typename T, typename U = T>
|
|
constexpr T exchange(T& slot, U&& value)
|
|
{
|
|
T old_value = move(slot);
|
|
slot = forward<U>(value);
|
|
return old_value;
|
|
}
|
|
|
|
template<typename T>
|
|
using RawPtr = typename Detail::_RawPtr<T>::Type;
|
|
|
|
template<typename V>
|
|
constexpr decltype(auto) to_underlying(V value) requires(IsEnum<V>)
|
|
{
|
|
return static_cast<UnderlyingType<V>>(value);
|
|
}
|
|
|
|
constexpr bool is_constant_evaluated()
|
|
{
|
|
#if __has_builtin(__builtin_is_constant_evaluated)
|
|
return __builtin_is_constant_evaluated();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// These can't be exported into the global namespace as they would clash with the C standard library.
|
|
|
|
#define __DEFINE_GENERIC_ABS(type, zero, intrinsic) \
|
|
constexpr type abs(type num) \
|
|
{ \
|
|
if (is_constant_evaluated()) \
|
|
return num < zero ? -num : num; \
|
|
else \
|
|
return __builtin_##intrinsic(num); \
|
|
}
|
|
|
|
__DEFINE_GENERIC_ABS(int, 0, abs);
|
|
__DEFINE_GENERIC_ABS(long, 0l, labs);
|
|
__DEFINE_GENERIC_ABS(long long, 0ll, llabs);
|
|
#ifndef KERNEL
|
|
__DEFINE_GENERIC_ABS(float, 0.0f, fabsf);
|
|
__DEFINE_GENERIC_ABS(double, 0.0, fabs);
|
|
__DEFINE_GENERIC_ABS(long double, 0.0l, fabsl);
|
|
#endif
|
|
|
|
}
|
|
|
|
using AK::array_size;
|
|
using AK::ceil_div;
|
|
using AK::clamp;
|
|
using AK::exchange;
|
|
using AK::forward;
|
|
using AK::is_constant_evaluated;
|
|
using AK::max;
|
|
using AK::min;
|
|
using AK::RawPtr;
|
|
using AK::swap;
|
|
using AK::to_underlying;
|