mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
AK+LibX86: Generalize u128/256 to AK::UFixedBigInt
Doing these as custom classes might be faster, especially when writing them in SSE, but this would cause a lot of Code duplication and due to the nature of constexprs and the intelligence of the compiler they might be using SSE/MMX either way
This commit is contained in:
parent
9270fb1e69
commit
5ffe23e4f3
Notes:
sideshowbarker
2024-07-18 12:25:47 +09:00
Author: https://github.com/Hendiadyoin1 Commit: https://github.com/SerenityOS/serenity/commit/5ffe23e4f3f Pull-request: https://github.com/SerenityOS/serenity/pull/7294 Reviewed-by: https://github.com/Dexesttp Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/kleinesfilmroellchen
11 changed files with 905 additions and 665 deletions
771
AK/UFixedBigInt.h
Normal file
771
AK/UFixedBigInt.h
Normal file
|
@ -0,0 +1,771 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AK/StdLibExtraDetails.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
requires(sizeof(T) >= sizeof(u64) && IsUnsigned<T>) class UFixedBigInt;
|
||||
|
||||
// FIXME: This breaks formatting
|
||||
// template<typename T>
|
||||
// constexpr inline bool Detail::IsIntegral<UFixedBigInt<T>> = true;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool Detail::IsUnsigned<UFixedBigInt<T>> = true;
|
||||
template<typename T>
|
||||
constexpr inline bool Detail::IsSigned<UFixedBigInt<T>> = false;
|
||||
|
||||
template<typename T>
|
||||
struct NumericLimits<UFixedBigInt<T>> {
|
||||
static constexpr UFixedBigInt<T> min() { return 0; }
|
||||
static constexpr UFixedBigInt<T> max() { return { NumericLimits<T>::max(), NumericLimits<T>::max() }; }
|
||||
static constexpr bool is_signed() { return false; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
requires(sizeof(T) >= sizeof(u64) && IsUnsigned<T>) class UFixedBigInt {
|
||||
public:
|
||||
using R = UFixedBigInt<T>;
|
||||
|
||||
constexpr UFixedBigInt() = default;
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr UFixedBigInt(U low)
|
||||
: m_low(low)
|
||||
, m_high(0u)
|
||||
{
|
||||
}
|
||||
template<Unsigned U, Unsigned U2>
|
||||
requires(sizeof(T) >= sizeof(U) && sizeof(T) >= sizeof(U2)) constexpr UFixedBigInt(U low, U2 high)
|
||||
: m_low(low)
|
||||
, m_high(high)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr T& low()
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
constexpr const T& low() const
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
constexpr T& high()
|
||||
{
|
||||
return m_high;
|
||||
}
|
||||
constexpr const T& high() const
|
||||
{
|
||||
return m_high;
|
||||
}
|
||||
|
||||
Span<u8> bytes()
|
||||
{
|
||||
return Span<u8>(reinterpret_cast<u8*>(this), sizeof(R));
|
||||
}
|
||||
Span<const u8> bytes() const
|
||||
{
|
||||
return Span<const u8>(reinterpret_cast<const u8*>(this), sizeof(R));
|
||||
}
|
||||
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) explicit operator U() const
|
||||
{
|
||||
return static_cast<U>(m_low);
|
||||
}
|
||||
|
||||
// Utils
|
||||
constexpr size_t clz() const requires(IsSame<T, u64>)
|
||||
{
|
||||
if (m_high)
|
||||
return __builtin_clzll(m_high);
|
||||
else
|
||||
return sizeof(T) * 8 + __builtin_clzll(m_low);
|
||||
}
|
||||
constexpr size_t clz() const requires(!IsSame<T, u64>)
|
||||
{
|
||||
if (m_high)
|
||||
return m_high.clz();
|
||||
else
|
||||
return sizeof(T) * 8 + m_low.clz();
|
||||
}
|
||||
constexpr size_t ctz() const requires(IsSame<T, u64>)
|
||||
{
|
||||
if (m_low)
|
||||
return __builtin_ctzll(m_low);
|
||||
else
|
||||
return sizeof(T) * 8 + __builtin_ctzll(m_high);
|
||||
}
|
||||
constexpr size_t ctz() const requires(!IsSame<T, u64>)
|
||||
{
|
||||
if (m_low)
|
||||
return m_low.ctz();
|
||||
else
|
||||
return sizeof(T) * 8 + m_high.ctz();
|
||||
}
|
||||
constexpr size_t popcnt() const requires(IsSame<T, u64>)
|
||||
{
|
||||
return __builtin_popcntll(m_low) + __builtin_popcntll(m_high);
|
||||
}
|
||||
constexpr size_t popcnt() const requires(!IsSame<T, u64>)
|
||||
{
|
||||
return m_low.popcnt() + m_high.popcnt();
|
||||
}
|
||||
|
||||
// Comparison Operations
|
||||
constexpr bool operator!() const
|
||||
{
|
||||
return !m_low && !m_high;
|
||||
}
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return m_low || m_high;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator==(const T& other) const
|
||||
{
|
||||
return !m_high && m_low == other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator!=(const T& other) const
|
||||
{
|
||||
return m_high || m_low != other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator>(const T& other) const
|
||||
{
|
||||
return m_high || m_low > other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator<(const T& other) const
|
||||
{
|
||||
return !m_high && m_low < other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator>=(const T& other) const
|
||||
{
|
||||
return *this == other || *this > other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator<=(const T& other) const
|
||||
{
|
||||
return *this == other || *this < other;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const R& other) const
|
||||
{
|
||||
return m_low == other.low() && m_high == other.high();
|
||||
}
|
||||
constexpr bool operator!=(const R& other) const
|
||||
{
|
||||
return m_low != other.low() || m_high != other.high();
|
||||
}
|
||||
constexpr bool operator>(const R& other) const
|
||||
{
|
||||
return m_high > other.high()
|
||||
|| (m_high == other.high() && m_low > other.low());
|
||||
}
|
||||
constexpr bool operator<(const R& other) const
|
||||
{
|
||||
return m_high < other.high()
|
||||
|| (m_high == other.high() && m_low < other.low());
|
||||
}
|
||||
constexpr bool operator>=(const R& other) const
|
||||
{
|
||||
return *this == other || *this > other;
|
||||
}
|
||||
constexpr bool operator<=(const R& other) const
|
||||
{
|
||||
return *this == other || *this < other;
|
||||
}
|
||||
|
||||
// Bitwise operations
|
||||
constexpr R operator~() const
|
||||
{
|
||||
return { ~m_low, ~m_high };
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr U operator&(const U& other) const
|
||||
{
|
||||
return static_cast<const U>(m_low) & other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr R operator|(const U& other) const
|
||||
{
|
||||
return { m_low | other, m_high };
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr R operator^(const U& other) const
|
||||
{
|
||||
return { m_low ^ other, m_high };
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R operator<<(const U& shift) const
|
||||
{
|
||||
if (shift >= sizeof(R) * 8u)
|
||||
return 0u;
|
||||
if (shift >= sizeof(T) * 8u)
|
||||
return R { 0u, m_low << (shift - sizeof(T) * 8u) };
|
||||
if (!shift)
|
||||
return *this;
|
||||
|
||||
T overflow = m_low >> (sizeof(T) * 8u - shift);
|
||||
return R { m_low << shift, (m_high << shift) | overflow };
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R operator>>(const U& shift) const
|
||||
{
|
||||
if (shift >= sizeof(R) * 8u)
|
||||
return 0u;
|
||||
if (shift >= sizeof(T) * 8u)
|
||||
return m_high >> (shift - sizeof(T) * 8u);
|
||||
if (!shift)
|
||||
return *this;
|
||||
|
||||
T underflow = m_high << (sizeof(T) * 8u - shift);
|
||||
return R { (m_low >> shift) | underflow, m_high >> shift };
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R rol(const U& shift) const
|
||||
{
|
||||
return (*this >> sizeof(T) * 8u - shift) | (*this << shift);
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R ror(const U& shift) const
|
||||
{
|
||||
return (*this << sizeof(T) * 8u - shift) | (*this >> shift);
|
||||
}
|
||||
|
||||
constexpr R operator&(const R& other) const
|
||||
{
|
||||
return { m_low & other.low(), m_high & other.high() };
|
||||
}
|
||||
constexpr R operator|(const R& other) const
|
||||
{
|
||||
return { m_low | other.low(), m_high | other.high() };
|
||||
}
|
||||
constexpr R operator^(const R& other) const
|
||||
{
|
||||
return { m_low ^ other.low(), m_high ^ other.high() };
|
||||
}
|
||||
|
||||
// Bitwise assignment
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr R& operator&=(const U& other)
|
||||
{
|
||||
m_high = 0u;
|
||||
m_low &= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr R& operator|=(const U& other)
|
||||
{
|
||||
m_low |= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr R& operator^=(const U& other)
|
||||
{
|
||||
m_low ^= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R& operator>>=(const U& other)
|
||||
{
|
||||
*this = *this >> other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R& operator<<=(const U& other)
|
||||
{
|
||||
*this = *this << other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr R& operator&=(const R& other)
|
||||
{
|
||||
m_high &= other.high();
|
||||
m_low &= other.low();
|
||||
return *this;
|
||||
}
|
||||
constexpr R& operator|=(const R& other)
|
||||
{
|
||||
m_high |= other.high();
|
||||
m_low |= other.low();
|
||||
return *this;
|
||||
}
|
||||
constexpr R& operator^=(const R& other)
|
||||
{
|
||||
m_high ^= other.high();
|
||||
m_low ^= other.low();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Arithmetics
|
||||
|
||||
// implies size of less than u64, so passing references isn't useful
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U) && IsSame<T, u64>) constexpr R addc(const U other, bool& carry) const
|
||||
{
|
||||
bool low_carry = Checked<T>::addition_would_overflow(m_low, other);
|
||||
low_carry |= Checked<T>::addition_would_overflow(m_low, carry);
|
||||
bool high_carry = Checked<T>::addition_would_overflow(m_high, low_carry);
|
||||
|
||||
T lower = m_low + other + carry;
|
||||
T higher = m_high + low_carry;
|
||||
|
||||
carry = high_carry;
|
||||
|
||||
return {
|
||||
lower,
|
||||
higher
|
||||
};
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(R) > sizeof(U) && sizeof(T) > sizeof(u64)) constexpr R addc(const U& other, bool& carry) const
|
||||
{
|
||||
T lower = m_low.addc(other, carry);
|
||||
T higher = m_high.addc(0u, carry);
|
||||
|
||||
return {
|
||||
lower,
|
||||
higher
|
||||
};
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(IsSame<R, U>&& IsSame<T, u64>) constexpr R addc(const U& other, bool& carry) const
|
||||
{
|
||||
bool low_carry = Checked<T>::addition_would_overflow(m_low, other.low());
|
||||
bool high_carry = Checked<T>::addition_would_overflow(m_high, other.high());
|
||||
|
||||
T lower = m_low + other.low();
|
||||
T higher = m_high + other.high();
|
||||
low_carry |= Checked<T>::addition_would_overflow(lower, carry);
|
||||
high_carry |= Checked<T>::addition_would_overflow(higher, low_carry);
|
||||
|
||||
lower += carry;
|
||||
higher += low_carry;
|
||||
carry = high_carry;
|
||||
|
||||
return {
|
||||
lower,
|
||||
higher
|
||||
};
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(IsSame<R, U> && sizeof(T) > sizeof(u64)) constexpr R addc(const U& other, bool& carry) const
|
||||
{
|
||||
T lower = m_low.addc(other.low(), carry);
|
||||
T higher = m_high.addc(other.high(), carry);
|
||||
|
||||
return {
|
||||
lower,
|
||||
higher
|
||||
};
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(R) < sizeof(U)) constexpr U addc(const U& other, bool& carry) const
|
||||
{
|
||||
return other.addc(*this, carry);
|
||||
}
|
||||
|
||||
// FIXME: subc for sizeof(T) < sizeof(U)
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr R subc(const U& other, bool& carry) const
|
||||
{
|
||||
bool low_carry = (!m_low && carry) || (m_low - carry) < other;
|
||||
bool high_carry = !m_high && low_carry;
|
||||
|
||||
T lower = m_low - other - carry;
|
||||
T higher = m_high - low_carry;
|
||||
carry = high_carry;
|
||||
|
||||
return { lower, higher };
|
||||
}
|
||||
constexpr R subc(const R& other, bool& carry) const
|
||||
{
|
||||
bool low_carry = (!m_low && carry) || (m_low - carry) < other.low();
|
||||
bool high_carry = (!m_high && low_carry) || (m_high - low_carry) < other.high();
|
||||
|
||||
T lower = m_low - other.low() - carry;
|
||||
T higher = m_high - other.high() - low_carry;
|
||||
carry = high_carry;
|
||||
|
||||
return { lower, higher };
|
||||
}
|
||||
|
||||
constexpr R operator+(const bool& other) const
|
||||
{
|
||||
bool carry = false; // unused
|
||||
return addc((u8)other, carry);
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R operator+(const U& other) const
|
||||
{
|
||||
bool carry = false; // unused
|
||||
return addc(other, carry);
|
||||
}
|
||||
|
||||
constexpr R operator-(const bool& other) const
|
||||
{
|
||||
bool carry = false; // unused
|
||||
return subc((u8)other, carry);
|
||||
}
|
||||
|
||||
template<Unsigned U>
|
||||
constexpr R operator-(const U& other) const
|
||||
{
|
||||
bool carry = false; // unused
|
||||
return subc(other, carry);
|
||||
}
|
||||
|
||||
template<Unsigned U>
|
||||
constexpr R& operator+=(const U& other)
|
||||
{
|
||||
*this = *this + other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R& operator-=(const U& other)
|
||||
{
|
||||
*this = *this - other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr R operator++()
|
||||
{
|
||||
// x++
|
||||
auto old = *this;
|
||||
*this += 1;
|
||||
return old;
|
||||
}
|
||||
constexpr R& operator++(int)
|
||||
{
|
||||
// ++x
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
constexpr R operator--()
|
||||
{
|
||||
// x--
|
||||
auto old = *this;
|
||||
*this -= 1;
|
||||
return old;
|
||||
}
|
||||
constexpr R& operator--(int)
|
||||
{
|
||||
// --x
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// FIXME: no restraints on this
|
||||
template<Unsigned U>
|
||||
requires(sizeof(R) >= sizeof(U)) constexpr R div_mod(const U& divisor, U& remainder) const
|
||||
{
|
||||
// FIXME: Is there a better way to raise a division by 0?
|
||||
// Maybe as a compiletime warning?
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdiv-by-zero"
|
||||
if (!divisor) {
|
||||
volatile int x = 1;
|
||||
volatile int y = 0;
|
||||
[[maybe_unused]] volatile int z = x / y;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// fastpaths
|
||||
if (*this < divisor) {
|
||||
remainder = static_cast<U>(*this);
|
||||
return 0u;
|
||||
}
|
||||
if (*this == divisor) {
|
||||
remainder = 0u;
|
||||
return 1u;
|
||||
}
|
||||
if (divisor == 1u) {
|
||||
remainder = 0u;
|
||||
return *this;
|
||||
}
|
||||
|
||||
remainder = 0u;
|
||||
R quotient = 0u;
|
||||
|
||||
for (ssize_t i = sizeof(R) * 8 - clz() - 1; i >= 0; --i) {
|
||||
remainder <<= 1u;
|
||||
remainder |= (*this >> (size_t)i) & 1u;
|
||||
if (remainder >= divisor) {
|
||||
remainder -= divisor;
|
||||
quotient |= R { 1u } << (size_t)i;
|
||||
}
|
||||
}
|
||||
|
||||
return quotient;
|
||||
}
|
||||
|
||||
template<Unsigned U>
|
||||
constexpr R operator*(U other) const
|
||||
{
|
||||
R res = 0u;
|
||||
R that = *this;
|
||||
for (; other != 0u; other >>= 1u) {
|
||||
if (other & 1u)
|
||||
res += that;
|
||||
that <<= 1u;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<Unsigned U>
|
||||
constexpr R operator/(const U& other) const
|
||||
{
|
||||
U mod { 0u }; // unused
|
||||
return div_mod(other, mod);
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr U operator%(const U& other) const
|
||||
{
|
||||
R res { 0u };
|
||||
div_mod(other, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<Unsigned U>
|
||||
constexpr R& operator*=(const U& other)
|
||||
{
|
||||
*this = *this * other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R& operator/=(const U& other)
|
||||
{
|
||||
*this = *this / other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned U>
|
||||
constexpr R& operator%=(const U& other)
|
||||
{
|
||||
*this = *this % other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr R sqrt() const
|
||||
{
|
||||
// Bitwise method: https://en.wikipedia.org/wiki/Integer_square_root#Using_bitwise_operations
|
||||
// the bitwise method seems to be way faster then Newtons:
|
||||
// https://quick-bench.com/q/eXZwW1DVhZxLE0llumeCXkfOK3Q
|
||||
if (*this == 1u)
|
||||
return 1u;
|
||||
|
||||
ssize_t shift = (sizeof(R) * 8 - clz()) & ~1ULL;
|
||||
// should be equivalent to:
|
||||
// long shift = 2;
|
||||
// while ((val >> shift) != 0)
|
||||
// shift += 2;
|
||||
|
||||
R res = 0u;
|
||||
while (shift >= 0) {
|
||||
res = res << 1u;
|
||||
R large_cand = (res | 1u);
|
||||
if (*this >> (size_t)shift >= large_cand * large_cand)
|
||||
res = large_cand;
|
||||
shift -= 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
constexpr R pow(u64 exp)
|
||||
{
|
||||
// Montgomery's Ladder Technique
|
||||
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Montgomery's_ladder_technique
|
||||
R x1 = *this;
|
||||
R x2 = *this * *this;
|
||||
u64 exp_copy = exp;
|
||||
for (ssize_t i = sizeof(u64) * 8 - __builtin_clzll(exp) - 2; i >= 0; --i) {
|
||||
if (exp_copy & 1u) {
|
||||
x2 *= x1;
|
||||
x1 *= x1;
|
||||
} else {
|
||||
x1 *= x2;
|
||||
x2 *= x2;
|
||||
}
|
||||
exp_copy >>= 1u;
|
||||
}
|
||||
return x1;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(U) > sizeof(u64)) constexpr R pow(U exp)
|
||||
{
|
||||
// Montgomery's Ladder Technique
|
||||
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Montgomery's_ladder_technique
|
||||
R x1 = *this;
|
||||
R x2 = *this * *this;
|
||||
U exp_copy = exp;
|
||||
for (ssize_t i = sizeof(U) * 8 - exp().clz() - 2; i >= 0; --i) {
|
||||
if (exp_copy & 1u) {
|
||||
x2 *= x1;
|
||||
x1 *= x1;
|
||||
} else {
|
||||
x1 *= x2;
|
||||
x2 *= x2;
|
||||
}
|
||||
exp_copy >>= 1u;
|
||||
}
|
||||
return x1;
|
||||
}
|
||||
|
||||
template<Unsigned U>
|
||||
constexpr U pow_mod(u64 exp, U mod)
|
||||
{
|
||||
// Left to right binary method:
|
||||
// https://en.wikipedia.org/wiki/Modular_exponentiation#Left-to-right_binary_method
|
||||
// FIXME: this is not sidechanel proof
|
||||
if (!mod)
|
||||
return 0u;
|
||||
|
||||
U res = 1;
|
||||
u64 exp_copy = exp;
|
||||
for (size_t i = sizeof(u64) - __builtin_clzll(exp) - 1u; i < exp; ++i) {
|
||||
res *= res;
|
||||
res %= mod;
|
||||
if (exp_copy & 1u) {
|
||||
res = (*this * res) % mod;
|
||||
}
|
||||
exp_copy >>= 1u;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
template<Unsigned ExpT, Unsigned U>
|
||||
requires(sizeof(ExpT) > sizeof(u64)) constexpr U pow_mod(ExpT exp, U mod)
|
||||
{
|
||||
// Left to right binary method:
|
||||
// https://en.wikipedia.org/wiki/Modular_exponentiation#Left-to-right_binary_method
|
||||
// FIXME: this is not side channel proof
|
||||
if (!mod)
|
||||
return 0u;
|
||||
|
||||
U res = 1;
|
||||
ExpT exp_copy = exp;
|
||||
for (size_t i = sizeof(ExpT) - exp.clz() - 1u; i < exp; ++i) {
|
||||
res *= res;
|
||||
res %= mod;
|
||||
if (exp_copy & 1u) {
|
||||
res = (*this * res) % mod;
|
||||
}
|
||||
exp_copy >>= 1u;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
constexpr size_t log2()
|
||||
{
|
||||
// FIXME: propper rounding
|
||||
return sizeof(R) - clz();
|
||||
}
|
||||
constexpr size_t logn(u64 base)
|
||||
{
|
||||
// FIXME: propper rounding
|
||||
return log2() / (sizeof(u64) - __builtin_clzll(base));
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(U) > sizeof(u64)) constexpr size_t logn(U base)
|
||||
{
|
||||
// FIXME: propper rounding
|
||||
return log2() / base.log2();
|
||||
}
|
||||
|
||||
private:
|
||||
T m_low;
|
||||
T m_high;
|
||||
};
|
||||
|
||||
// reverse operators
|
||||
template<Unsigned U, Unsigned T>
|
||||
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator<(const U a, const UFixedBigInt<T>& b) { return b >= a; }
|
||||
template<Unsigned U, Unsigned T>
|
||||
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator>(const U a, const UFixedBigInt<T>& b) { return b <= a; }
|
||||
template<Unsigned U, Unsigned T>
|
||||
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator<=(const U a, const UFixedBigInt<T>& b) { return b > a; }
|
||||
template<Unsigned U, Unsigned T>
|
||||
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator>=(const U a, const UFixedBigInt<T>& b) { return b < a; }
|
||||
|
||||
template<Unsigned T>
|
||||
struct Formatter<UFixedBigInt<T>> : StandardFormatter {
|
||||
Formatter() = default;
|
||||
explicit Formatter(StandardFormatter formatter)
|
||||
: StandardFormatter(formatter)
|
||||
{
|
||||
}
|
||||
|
||||
void format(FormatBuilder& builder, UFixedBigInt<T> value)
|
||||
{
|
||||
if (m_precision.has_value())
|
||||
VERIFY_NOT_REACHED();
|
||||
|
||||
if (m_mode == Mode::Pointer) {
|
||||
// these are way to big for a pointer
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
if (m_mode == Mode::Default)
|
||||
m_mode = Mode::Hexadecimal;
|
||||
|
||||
if (!value.high()) {
|
||||
Formatter<T> formatter { *this };
|
||||
return formatter.format(builder, value.low());
|
||||
}
|
||||
|
||||
u8 base = 0;
|
||||
if (m_mode == Mode::Binary) {
|
||||
base = 2;
|
||||
} else if (m_mode == Mode::BinaryUppercase) {
|
||||
base = 2;
|
||||
} else if (m_mode == Mode::Octal) {
|
||||
TODO();
|
||||
} else if (m_mode == Mode::Decimal) {
|
||||
TODO();
|
||||
} else if (m_mode == Mode::Hexadecimal) {
|
||||
base = 16;
|
||||
} else if (m_mode == Mode::HexadecimalUppercase) {
|
||||
base = 16;
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
ssize_t width = m_width.value_or(0);
|
||||
ssize_t lower_length = ceil_div(sizeof(T) * 8, (ssize_t)base);
|
||||
Formatter<T> formatter { *this };
|
||||
formatter.m_width = max(width - lower_length, (ssize_t)0);
|
||||
formatter.format(builder, value.high());
|
||||
builder.put_literal("'"sv);
|
||||
formatter.m_zero_pad = true;
|
||||
formatter.m_alternative_form = false;
|
||||
formatter.m_width = lower_length;
|
||||
formatter.format(builder, value.low());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Nit: Doing these as custom classes might be faster, especially when writing
|
||||
// then in SSE, but this would cause a lot of Code duplication and due to
|
||||
// the nature of constexprs and the intelligence of the compiler they might
|
||||
// be using SSE/MMX either way
|
||||
|
||||
// these sizes should suffice for most usecases
|
||||
using u128 = AK::UFixedBigInt<u64>;
|
||||
using u256 = AK::UFixedBigInt<u128>;
|
||||
using u512 = AK::UFixedBigInt<u256>;
|
||||
using u1024 = AK::UFixedBigInt<u512>;
|
||||
using u2048 = AK::UFixedBigInt<u1024>;
|
||||
using u4096 = AK::UFixedBigInt<u2048>;
|
|
@ -58,6 +58,7 @@ set(AK_TEST_SOURCES
|
|||
TestTuple.cpp
|
||||
TestTypeTraits.cpp
|
||||
TestTypedTransfer.cpp
|
||||
TestUFixedBigInt.cpp
|
||||
TestURL.cpp
|
||||
TestUtf8.cpp
|
||||
TestVariant.cpp
|
||||
|
|
127
Tests/AK/TestUFixedBigInt.cpp
Normal file
127
Tests/AK/TestUFixedBigInt.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
constexpr int test_iterations = 32;
|
||||
|
||||
TEST_CASE(one_plus_one)
|
||||
{
|
||||
u256 a = 1u;
|
||||
u256 b = 1u;
|
||||
EXPECT_EQ(a + b, u256(2u));
|
||||
}
|
||||
|
||||
TEST_CASE(identities)
|
||||
{
|
||||
srand(0);
|
||||
for (int i = 0; i < test_iterations; ++i) {
|
||||
auto x = get_random<u256>();
|
||||
if ((x >> 255u) & 1u) {
|
||||
// ignore numbers that could overflow
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
EXPECT_EQ((x << 0u), x);
|
||||
EXPECT_EQ((x >> 0u), x);
|
||||
EXPECT_EQ((x / 1u), x);
|
||||
EXPECT_EQ((x % (x + 1u)), x);
|
||||
EXPECT_EQ((x << 1u) >> 1u, x);
|
||||
EXPECT_EQ((x * 2u) / 2u, x);
|
||||
EXPECT_EQ((x + 2u) - 2u, x);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(sqrt)
|
||||
{
|
||||
srand(0);
|
||||
for (int i = 0; i < test_iterations; ++i) {
|
||||
u256 x = get_random<u128>();
|
||||
EXPECT_EQ((x * x).sqrt(), x);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(add_overflow_propagation)
|
||||
{
|
||||
u256 a = NumericLimits<u128>::max();
|
||||
u256 b = a + a;
|
||||
u256 c = a * 2u;
|
||||
|
||||
EXPECT_EQ(b.low(), NumericLimits<u128>::max() - 1u);
|
||||
EXPECT_EQ(b.high(), 1u);
|
||||
EXPECT_EQ(b, a << 1u);
|
||||
EXPECT_EQ(b, c);
|
||||
}
|
||||
|
||||
TEST_CASE(simple_multiplication)
|
||||
{
|
||||
srand(0);
|
||||
for (int i = 0; i < test_iterations; ++i) {
|
||||
u256 a = get_random<u256>();
|
||||
|
||||
EXPECT_EQ(a * 0u, 0u);
|
||||
EXPECT_EQ(a * 1u, a);
|
||||
EXPECT_EQ(a >> 1u, a / 2u);
|
||||
if (!(a >> 255u & 1u)) {
|
||||
EXPECT_EQ(a << 1u, a * 2u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(div_mod)
|
||||
{
|
||||
srand(0);
|
||||
for (int i = 0; i < test_iterations; ++i) {
|
||||
u256 a = get_random<u256>();
|
||||
u256 b = get_random<u256>();
|
||||
u256 mod;
|
||||
u256 div = a.div_mod(b, mod);
|
||||
EXPECT_EQ(a, div * b + mod);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(mod_hardcoded)
|
||||
{
|
||||
EXPECT_EQ(u256(u128 { 0x8a4b08d32f8b8e48ULL, 0x8459322f67b8e26dULL }, u128 { 0xeea82af4312d1931ULL, 0x654fb5cfe82dbd58ULL }) % u256(u128 { 0x40a58652868d5d66ULL, 0x81d674bf7d6d6861ULL }, u128 { 0xa8314900e6188a82ULL, 0xc273ca947237b4aaULL }), u256(u128 { 0x8a4b08d32f8b8e48ULL, 0x8459322f67b8e26dULL }, u128 { 0xeea82af4312d1931ULL, 0x654fb5cfe82dbd58ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xda06d295caa75a3bULL, 0xe3ae0d460049948eULL }, u128 { 0x9a89d29a0325f27fULL, 0x1c8d90ebadec5607ULL }) % u256(u128 { 0x38bd4d49ff59fdf8ULL, 0xcba9acf09110de14ULL }, u128 { 0x51a376c68c4702feULL, 0x0d1b59dec8d2338bULL }), u256(u128 { 0x688c3801cbf35e4bULL, 0x4c5ab364de27d866ULL }, u128 { 0xf742e50cea97ec82ULL, 0x0256dd2e1c47eef0ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xdfb56d42706bdb28ULL, 0x6c3bd5ea790c7ef5ULL }, u128 { 0xfebec271d7c757baULL, 0x7dbd745d56bc9e0eULL }) % u256(u128 { 0x30a309a58aed2c01ULL, 0x64d58c8b485c113dULL }, u128 { 0xfa01f558732e9b78ULL, 0x5862b502ebb2dbe9ULL }), u256(u128 { 0xaf12639ce57eaf27ULL, 0x0766495f30b06db8ULL }, u128 { 0x04bccd196498bc42ULL, 0x255abf5a6b09c225ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x0a8473d84131f420ULL, 0x0471632bb018c1a2ULL }, u128 { 0x22865980ccd1014fULL, 0xcade79df2adf8fdfULL }) % u256(u128 { 0xd7da811f35db7de0ULL, 0x4e3d98062eae954fULL }, u128 { 0x23946cd23d470d7eULL, 0x6645d41afdc1f2e8ULL }), u256(u128 { 0x32a9f2b90b567640ULL, 0xb633cb25816a2c52ULL }, u128 { 0xfef1ecae8f89f3d0ULL, 0x6498a5c42d1d9cf6ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x68636d8d1b7ac40bULL, 0xcb04084ddc684d42ULL }, u128 { 0xaa43c0f6e4e0178cULL, 0x49edae817f27c32aULL }) % u256(u128 { 0xbcc52d96070b7046ULL, 0x2f8255f3c6f8d4bdULL }, u128 { 0x2423bb472eced919ULL, 0x2ed9534c1570b7faULL }), u256(u128 { 0xab9e3ff7146f53c5ULL, 0x9b81b25a156f7884ULL }, u128 { 0x862005afb6113e73ULL, 0x1b145b3569b70b30ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xad34ce382cd00226ULL, 0x39b1986d56a064afULL }, u128 { 0xa9410bbd86d9ab21ULL, 0x0fb980a5a7d4b99fULL }) % u256(u128 { 0xa7561893be8cd299ULL, 0x9c3cb9184f45878aULL }, u128 { 0x1e066270a27414efULL, 0xe0fbaa0b739890b8ULL }), u256(u128 { 0xad34ce382cd00226ULL, 0x39b1986d56a064afULL }, u128 { 0xa9410bbd86d9ab21ULL, 0x0fb980a5a7d4b99fULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x69a0ab23d9f81040ULL, 0xf509000f44fcadb3ULL }, u128 { 0x544310cc56ea051aULL, 0x968a003529f513c0ULL }) % u256(u128 { 0xd6db169628ba28edULL, 0xcf2417c98b765531ULL }, u128 { 0x27865ebfca2d945aULL, 0xcbd1257363cb86a1ULL }), u256(u128 { 0x69a0ab23d9f81040ULL, 0xf509000f44fcadb3ULL }, u128 { 0x544310cc56ea051aULL, 0x968a003529f513c0ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x5d41bcd96e47dfbdULL, 0x623a7c82c903789bULL }, u128 { 0x57c3723bfcfd7eeeULL, 0x8b1f21a0739fa6a8ULL }) % u256(u128 { 0xf918e7d73771d5c4ULL, 0xdd40e701852f4d68ULL }, u128 { 0x7c4ac424e3836a4dULL, 0xcb7a0bcc58701175ULL }), u256(u128 { 0x5d41bcd96e47dfbdULL, 0x623a7c82c903789bULL }, u128 { 0x57c3723bfcfd7eeeULL, 0x8b1f21a0739fa6a8ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xa4394401788e848aULL, 0x8a907db529ba2943ULL }, u128 { 0x4f3c13b9058d17d3ULL, 0xf17f01b5c1898104ULL }) % u256(u128 { 0x214097598f92cebeULL, 0x723b873f1f879305ULL }, u128 { 0x5f9352861d92ff91ULL, 0x527c65978f7d12ebULL }), u256(u128 { 0x61b8154e5968e70eULL, 0xa6196f36eaab0339ULL }, u128 { 0x90156eacca6718b0ULL, 0x4c863686a28f5b2dULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x324e46a2bd4d9c0dULL, 0xfb8980a6353814a8ULL }, u128 { 0x3605ef999901dc37ULL, 0xcc2493941c934b83ULL }) % u256(u128 { 0x45e1b8552ccd49b1ULL, 0xe61bd62768189e42ULL }, u128 { 0x859e83ed2f92c211ULL, 0xc7713b3893031cbdULL }), u256(u128 { 0xec6c8e4d9080525cULL, 0x156daa7ecd1f7665ULL }, u128 { 0xb0676bac696f1a26ULL, 0x04b3585b89902ec5ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x9a3b5f7c879d14f4ULL, 0xc437119868072180ULL }, u128 { 0xea395ae2238ada4eULL, 0x1aa5cc44c4c9deb5ULL }) % u256(u128 { 0x9535e4674b364058ULL, 0xbbf3d10e995c610dULL }, u128 { 0x8fac6f8ae200290aULL, 0x7832f747c56ae6dfULL }), u256(u128 { 0x9a3b5f7c879d14f4ULL, 0xc437119868072180ULL }, u128 { 0xea395ae2238ada4eULL, 0x1aa5cc44c4c9deb5ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xf2a2d399b73fd0c2ULL, 0x02b7155ee15525ffULL }, u128 { 0xcaaa7daf39923db6ULL, 0x8ccb6244075bb5bbULL }) % u256(u128 { 0xfc002da6ab396d95ULL, 0xd7d0ebd6242b7119ULL }, u128 { 0x7f2ec32021ce7d32ULL, 0x63cef84255b91414ULL }), u256(u128 { 0xf6a2a5f30c06632dULL, 0x2ae62988bd29b4e5ULL }, u128 { 0x4b7bba8f17c3c083ULL, 0x28fc6a01b1a2a1a7ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xfef71dab99335163ULL, 0xd1f1bc5f37570d67ULL }, u128 { 0x34bd2c7372eb8c4cULL, 0x15c0d3f1cc1613beULL }) % u256(u128 { 0x3978824c651c6cceULL, 0x5631f4d483e9f3ffULL }, u128 { 0xfd7c47d688e0d50fULL, 0xb3a9f99c7234d772ULL }), u256(u128 { 0xfef71dab99335163ULL, 0xd1f1bc5f37570d67ULL }, u128 { 0x34bd2c7372eb8c4cULL, 0x15c0d3f1cc1613beULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x19d69d0229db064eULL, 0x612eea6e8d79807bULL }, u128 { 0xe755c10d2b9e25adULL, 0x6a84d397b8e7da54ULL }) % u256(u128 { 0x9db6a18d292bc65fULL, 0xbdc7ccbcdb4f046cULL }, u128 { 0xd5be95d179cc1aa4ULL, 0x77c81421a604eb66ULL }), u256(u128 { 0x19d69d0229db064eULL, 0x612eea6e8d79807bULL }, u128 { 0xe755c10d2b9e25adULL, 0x6a84d397b8e7da54ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xcd6a8ed6185d098fULL, 0xcf17b08e6e3836e5ULL }, u128 { 0x52e187a75426d99dULL, 0x562e1c437b33a29dULL }) % u256(u128 { 0x0c3dd1aa87a4bd96ULL, 0xac333d8636735a23ULL }, u128 { 0x1a30abda1015e674ULL, 0xe968125d96bdc2e9ULL }), u256(u128 { 0xcd6a8ed6185d098fULL, 0xcf17b08e6e3836e5ULL }, u128 { 0x52e187a75426d99dULL, 0x562e1c437b33a29dULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x60151f3f11782d51ULL, 0xeecbc23fa60bd168ULL }, u128 { 0x825b67c89bce81f2ULL, 0x082fe85ba1a09583ULL }) % u256(u128 { 0x438123a283f8133aULL, 0x7b5936b727339a8eULL }, u128 { 0x36f2bc572018588cULL, 0xbdebe2b4033d3209ULL }), u256(u128 { 0x60151f3f11782d51ULL, 0xeecbc23fa60bd168ULL }, u128 { 0x825b67c89bce81f2ULL, 0x082fe85ba1a09583ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x6a98f75458b6c9daULL, 0xbe935c50e782e82fULL }, u128 { 0xf8f7479d9ba56379ULL, 0xfd3cb6194bc5966fULL }) % u256(u128 { 0xc0fb2a97d7368d96ULL, 0x306534301d4eadbeULL }, u128 { 0x30b2c8ff81066af6ULL, 0xd23116ef8d5eacf5ULL }), u256(u128 { 0xa99dccbc81803c44ULL, 0x8e2e2820ca343a70ULL }, u128 { 0xc8447e9e1a9ef883ULL, 0x2b0b9f29be66e97aULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xf90a6805c45be556ULL, 0x1d4a0c204a2dec7dULL }, u128 { 0x4a8c0d194584da59ULL, 0xcd1ab79a84dfccb6ULL }) % u256(u128 { 0xcedf80ed06c339b1ULL, 0x3a18231b09b21a3cULL }, u128 { 0xef2fedb7c3b237ddULL, 0x01d6223300a1f18aULL }), u256(u128 { 0x4621813fd5b5e197ULL, 0xecd2d36715f48c20ULL }, u128 { 0x94c3fa6b6b3ea16cULL, 0x0141e37d3ea81178ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x5cced259ff5b73fdULL, 0x223a2bc9d62d3714ULL }, u128 { 0xf1b7b34b45f3608fULL, 0xce2325cbc0e9734fULL }) % u256(u128 { 0xf5dc56158c242575ULL, 0xb3bf8578c1852fdcULL }, u128 { 0xd97725f998d1d289ULL, 0x053baa680c5abb16ULL }), u256(u128 { 0xe83db511a5d9bf2aULL, 0xc00cd6645ae2ec6aULL }, u128 { 0xd090ea44fdfc4d94ULL, 0x020c2ff1df16f2d4ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x6ce28a960af0ceb3ULL, 0x2da9808f962b0c43ULL }, u128 { 0x67cdac05a542bd66ULL, 0x5d3eb81aadf9479aULL }) % u256(u128 { 0x45f549795eab7c6cULL, 0x5643e85f6b4399eeULL }, u128 { 0x3b068fa03cb257dfULL, 0x3b42cfa16517b14cULL }), u256(u128 { 0x26ed411cac455247ULL, 0xd76598302ae77255ULL }, u128 { 0x2cc71c6568906586ULL, 0x21fbe87948e1964eULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x236f8081c4dc0d2aULL, 0xa7da15c4c15e83f3ULL }, u128 { 0x32c0948d497b78f0ULL, 0xf75ddc710601d2d0ULL }) % u256(u128 { 0x326f376465b287beULL, 0x5e24a7c87a45f4ebULL }, u128 { 0x1fa25aecc5a5a1f2ULL, 0x3490287aca77c399ULL }), u256(u128 { 0x59b2a2f02e11ee32ULL, 0x2f4776a2d846b046ULL }, u128 { 0xb43728da32e4f127ULL, 0x251d3a85dc22c46bULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xf5af1d760c381629ULL, 0x9f4d904501f9f6d6ULL }, u128 { 0xc23fe8d79d015270ULL, 0x3982c8897a86e837ULL }) % u256(u128 { 0xd3cc875eec2d5032ULL, 0x46e392089468f8cfULL }, u128 { 0x91c6762130826cedULL, 0x9e3b011ba58b4705ULL }), u256(u128 { 0xf5af1d760c381629ULL, 0x9f4d904501f9f6d6ULL }, u128 { 0xc23fe8d79d015270ULL, 0x3982c8897a86e837ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x6b60c428cac4f505ULL, 0xeac42ae8d7929fb7ULL }, u128 { 0x59a0ce8a7110df27ULL, 0xc0d5952f55096e15ULL }) % u256(u128 { 0x280419bd2d8fe3e8ULL, 0x13b50ec9c2bb7397ULL }, u128 { 0x8d8ef08f3ac8ce5eULL, 0x8912b53aa9279938ULL }), u256(u128 { 0x435caa6b9d35111dULL, 0xd70f1c1f14d72c20ULL }, u128 { 0xcc11ddfb364810c9ULL, 0x37c2dff4abe1d4dcULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x8068bf135ceead51ULL, 0xadda5b57797a3a27ULL }, u128 { 0x4c4e3fe186af2698ULL, 0xdfbab959987cb289ULL }) % u256(u128 { 0x93c99cb4fa9f36c0ULL, 0xe107948b8bf301d8ULL }, u128 { 0xab4e7570e6e8e177ULL, 0xdb95d36ef24543daULL }), u256(u128 { 0xec9f225e624f7691ULL, 0xccd2c6cbed87384eULL }, u128 { 0xa0ffca709fc64520ULL, 0x0424e5eaa6376eaeULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x036b4a64b2ab05bbULL, 0x6be175b3549f7440ULL }, u128 { 0x3c6839ecac5d4634ULL, 0x6a1939f6585dd1ddULL }) % u256(u128 { 0x329f61eaf9c14938ULL, 0x6653276323053388ULL }, u128 { 0x7e511a9611463f4dULL, 0x9898a93910722fd8ULL }), u256(u128 { 0x036b4a64b2ab05bbULL, 0x6be175b3549f7440ULL }, u128 { 0x3c6839ecac5d4634ULL, 0x6a1939f6585dd1ddULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xe5d0db9190bb01c1ULL, 0x20510645c252e9b1ULL }, u128 { 0x3b673f98db9a3038ULL, 0xbda4406d733b1c6cULL }) % u256(u128 { 0x4d67af71063282f2ULL, 0x594aa60bb2360bbdULL }, u128 { 0x4c2759ff1b2ffbd1ULL, 0xe29a2e0962d9bdbfULL }), u256(u128 { 0xe5d0db9190bb01c1ULL, 0x20510645c252e9b1ULL }, u128 { 0x3b673f98db9a3038ULL, 0xbda4406d733b1c6cULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x2f833c8cd20c43f9ULL, 0x405bd5f257ac19e1ULL }, u128 { 0xd9873917f32ca4adULL, 0x582dda480fecde28ULL }) % u256(u128 { 0xb56564a5a9dbc163ULL, 0x17b4076b2667c703ULL }, u128 { 0xdf0f26d9a66f513eULL, 0xb34c28d1e1a1953cULL }), u256(u128 { 0x2f833c8cd20c43f9ULL, 0x405bd5f257ac19e1ULL }, u128 { 0xd9873917f32ca4adULL, 0x582dda480fecde28ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x79ceb31188bc142bULL, 0xb2c083d1b0d1a172ULL }, u128 { 0x87a465799728fe9fULL, 0xe05c1c98eaa03994ULL }) % u256(u128 { 0x548b4f12f104f995ULL, 0x8d1d554e53ebc210ULL }, u128 { 0x4f5238bde10ce04aULL, 0x33da77cfa817ef7cULL }), u256(u128 { 0x27a176c5c4a82dd7ULL, 0x7e4b2e9861229931ULL }, u128 { 0x4a5b828212f57d75ULL, 0x10f23d5a4a407ba3ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0xfbcfb8e88417af84ULL, 0xfd35ec5ad38f6f00ULL }, u128 { 0x12d5c3e4e108cc62ULL, 0x09370460a41c637fULL }) % u256(u128 { 0x71faedeee5e0bf52ULL, 0x3d17ff54be8d686fULL }, u128 { 0x02e3ab47712e3d11ULL, 0x64da86270055e5eaULL }), u256(u128 { 0xfbcfb8e88417af84ULL, 0xfd35ec5ad38f6f00ULL }, u128 { 0x12d5c3e4e108cc62ULL, 0x09370460a41c637fULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x690f9145f7b1f8f8ULL, 0xe790aa66a2e08b63ULL }, u128 { 0x1d6ded50aa11aa3cULL, 0x601ec6f81fd1d57aULL }) % u256(u128 { 0x1256bdca9e0d6066ULL, 0xd19119919b026c0cULL }, u128 { 0x17a17b7df7689c40ULL, 0x97baf68d5f5622ddULL }), u256(u128 { 0x690f9145f7b1f8f8ULL, 0xe790aa66a2e08b63ULL }, u128 { 0x1d6ded50aa11aa3cULL, 0x601ec6f81fd1d57aULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x7f13e232d82a24c6ULL, 0x23d41447dd7f5bc6ULL }, u128 { 0xd89a3ed8b30527caULL, 0xa98ef2cc01e83685ULL }) % u256(u128 { 0x8d4f5b1983fc1f0eULL, 0xf54102ece15fb0faULL }, u128 { 0x17b8aec68556a16dULL, 0x4e1e5bea70cb9398ULL }), u256(u128 { 0x64752bffd031e6aaULL, 0x39520e6e1abff9d1ULL }, u128 { 0xa928e14ba857e4eeULL, 0x0d523af720510f55ULL }));
|
||||
EXPECT_EQ(u256(u128 { 0x49750d7f39d61607ULL, 0x58bdef1c3e00d18eULL }, u128 { 0xa651479cd1fd1933ULL, 0xd1834bc3d654b633ULL }) % u256(u128 { 0x1bda34f5ec68ef3bULL, 0x12c65ce5363a7616ULL }, u128 { 0x5a79c4d85da0071aULL, 0xffa6b6284559d1aaULL }), u256(u128 { 0x49750d7f39d61607ULL, 0x58bdef1c3e00d18eULL }, u128 { 0xa651479cd1fd1933ULL, 0xd1834bc3d654b633ULL }));
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
#include "ValueWithShadow.h"
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibX86/Types.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
|
||||
namespace UserspaceEmulator {
|
||||
|
||||
|
|
|
@ -1855,9 +1855,10 @@ void SoftCPU::FLD_RM80(const X86::Instruction& insn)
|
|||
// long doubles can be up to 128 bits wide in memory for reasons (alignment) and only uses 80 bits of precision
|
||||
// GCC uses 12 bytes in 32 bit and 16 bytes in 64 bit mode
|
||||
// so in the 32 bit case we read a bit to much, but that shouldn't be an issue.
|
||||
auto new_f80 = insn.modrm().read128(*this, insn);
|
||||
// FIXME: Respect shadow values
|
||||
fpu_push(*(long double*)new_f80.value().bytes());
|
||||
auto new_f80 = insn.modrm().read128(*this, insn).value();
|
||||
|
||||
fpu_push(*(long double*)new_f80.bytes().data());
|
||||
}
|
||||
|
||||
void SoftCPU::FUCOMI(const X86::Instruction& insn)
|
||||
|
@ -1908,7 +1909,7 @@ void SoftCPU::FSTP_RM80(const X86::Instruction& insn)
|
|||
if constexpr (sizeof(long double) == 12)
|
||||
f80 = insn.modrm().read128(*this, insn).value();
|
||||
|
||||
*(long double*)f80.bytes() = fpu_pop();
|
||||
*(long double*)f80.bytes().data() = fpu_pop();
|
||||
|
||||
insn.modrm().write128(*this, insn, shadow_wrap_as_initialized(f80));
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <LibX86/Types.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace UserspaceEmulator {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
set(SOURCES
|
||||
Instruction.cpp
|
||||
Types/Formatter.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibX86 x86)
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Types/u128.h"
|
||||
#include "Types/u256.h"
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "u128.h"
|
||||
#include "u256.h"
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <serenity.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void AK::Formatter<u128>::format(AK::FormatBuilder& builder, u128 value)
|
||||
{
|
||||
if (value.high() == 0) {
|
||||
AK::Formatter<u64> formatter { *this };
|
||||
return formatter.format(builder, value.low());
|
||||
}
|
||||
|
||||
if (m_precision.has_value())
|
||||
VERIFY_NOT_REACHED();
|
||||
|
||||
if (m_mode == Mode::Pointer) {
|
||||
// this is way to big for a pointer
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
u8 base = 0;
|
||||
bool upper_case = false;
|
||||
if (m_mode == Mode::Binary) {
|
||||
base = 2;
|
||||
} else if (m_mode == Mode::BinaryUppercase) {
|
||||
base = 2;
|
||||
upper_case = true;
|
||||
} else if (m_mode == Mode::Octal) {
|
||||
base = 8;
|
||||
} else if (m_mode == Mode::Decimal || m_mode == Mode::Default) {
|
||||
// FIXME: implement this
|
||||
TODO();
|
||||
} else if (m_mode == Mode::Hexadecimal) {
|
||||
base = 16;
|
||||
} else if (m_mode == Mode::HexadecimalUppercase) {
|
||||
base = 16;
|
||||
upper_case = true;
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
u16 lower_length = sizeof(u64) * 0xFF / base;
|
||||
if (m_width.value() > lower_length) {
|
||||
builder.put_u64(value.high(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - lower_length, m_fill, m_sign_mode);
|
||||
builder.put_u64(value.low(), base, false, upper_case, m_zero_pad, m_align, m_width.value(), m_fill, m_sign_mode);
|
||||
} else {
|
||||
builder.put_u64(value.low(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value(), m_fill, m_sign_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void AK::Formatter<u256>::format(AK::FormatBuilder& builder, u256 value)
|
||||
{
|
||||
if (value.high() == 0) {
|
||||
AK::Formatter<u128> formatter { *this };
|
||||
return formatter.format(builder, value.low());
|
||||
}
|
||||
|
||||
if (m_precision.has_value())
|
||||
VERIFY_NOT_REACHED();
|
||||
|
||||
if (m_mode == Mode::Pointer) {
|
||||
// this is way to big for a pointer
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
u8 base = 0;
|
||||
bool upper_case = false;
|
||||
if (m_mode == Mode::Binary) {
|
||||
base = 2;
|
||||
} else if (m_mode == Mode::BinaryUppercase) {
|
||||
base = 2;
|
||||
upper_case = true;
|
||||
} else if (m_mode == Mode::Octal) {
|
||||
base = 8;
|
||||
} else if (m_mode == Mode::Decimal || m_mode == Mode::Default) {
|
||||
// FIXME: implement this
|
||||
TODO();
|
||||
} else if (m_mode == Mode::Hexadecimal) {
|
||||
base = 16;
|
||||
} else if (m_mode == Mode::HexadecimalUppercase) {
|
||||
base = 16;
|
||||
upper_case = true;
|
||||
} else {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
u16 part_length = sizeof(u128) * 0xFF / base;
|
||||
if (m_width.value() > part_length * 3) {
|
||||
builder.put_u64(value.high().high(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - part_length * 3, m_fill, m_sign_mode);
|
||||
builder.put_u64(value.high().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
|
||||
builder.put_u64(value.low().high(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
|
||||
builder.put_u64(value.low().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
|
||||
} else if (m_width.value() > part_length * 2) {
|
||||
builder.put_u64(value.high().low(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - part_length * 2, m_fill, m_sign_mode);
|
||||
builder.put_u64(value.low().high(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
|
||||
builder.put_u64(value.low().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
|
||||
} else if (m_width.value() > part_length) {
|
||||
builder.put_u64(value.low().high(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - part_length, m_fill, m_sign_mode);
|
||||
builder.put_u64(value.low().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
|
||||
} else {
|
||||
builder.put_u64(value.low().low(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value(), m_fill, m_sign_mode);
|
||||
}
|
||||
}
|
|
@ -1,268 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace X86 {
|
||||
|
||||
class u128 {
|
||||
public:
|
||||
constexpr u128() = default;
|
||||
template<Unsigned T>
|
||||
constexpr u128(T val)
|
||||
: m_low(val)
|
||||
{
|
||||
}
|
||||
constexpr u128(u64 val_low, u64 val_high)
|
||||
: m_low(val_low)
|
||||
, m_high(val_high)
|
||||
{
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u8* bytes()
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
ALWAYS_INLINE const u8* bytes() const
|
||||
{
|
||||
return m_bytes;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u16* words()
|
||||
{
|
||||
return (u16*)m_bytes;
|
||||
}
|
||||
ALWAYS_INLINE const u16* words() const
|
||||
{
|
||||
return (const u16*)m_bytes;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32* double_words()
|
||||
{
|
||||
return (u32*)m_bytes;
|
||||
}
|
||||
ALWAYS_INLINE const u32* double_words() const
|
||||
{
|
||||
return (const u32*)m_bytes;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr u64& low()
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
ALWAYS_INLINE constexpr const u64& low() const
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr u64& high()
|
||||
{
|
||||
return m_high;
|
||||
}
|
||||
ALWAYS_INLINE constexpr const u64& high() const
|
||||
{
|
||||
return m_high;
|
||||
}
|
||||
|
||||
// conversion
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr operator T() const
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr operator bool() const
|
||||
{
|
||||
return m_low || m_high;
|
||||
}
|
||||
|
||||
// comparisons
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr bool operator==(const T& other) const
|
||||
{
|
||||
return (!m_high) && m_low == other;
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr bool operator!=(const T& other) const
|
||||
{
|
||||
return m_high || m_low != other;
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr bool operator>(const T& other) const
|
||||
{
|
||||
return m_high || m_low > other;
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr bool operator<(const T& other) const
|
||||
{
|
||||
return !m_high && m_low < other;
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr bool operator>=(const T& other) const
|
||||
{
|
||||
return *this == other || *this > other;
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr bool operator<=(const T& other) const
|
||||
{
|
||||
return *this == other || *this < other;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr bool operator==(const u128& other) const
|
||||
{
|
||||
return m_low == other.low() && m_high == other.high();
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator!=(const u128& other) const
|
||||
{
|
||||
return m_low != other.low() || m_high != other.high();
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator>(const u128& other) const
|
||||
{
|
||||
return m_high > other.high()
|
||||
|| (m_high == other.high() && m_low > other.low());
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator<(const u128& other) const
|
||||
{
|
||||
return m_high < other.high()
|
||||
|| (m_high == other.high() && m_low < other.low());
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator>=(const u128& other) const
|
||||
{
|
||||
return *this == other || *this > other;
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator<=(const u128& other) const
|
||||
{
|
||||
return *this == other || *this < other;
|
||||
}
|
||||
|
||||
// bitwise
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr T operator&(const T& other) const
|
||||
{
|
||||
return m_low & other;
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr u128 operator|(const T& other) const
|
||||
{
|
||||
return { m_low | other, m_high };
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr u128 operator^(const T& other) const
|
||||
{
|
||||
return { m_low ^ other, m_high };
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr u128 operator<<(const T& other) const
|
||||
{
|
||||
u64 overflow = m_low >> (64 - other);
|
||||
return { m_low << other, (m_high << other) | overflow };
|
||||
}
|
||||
template<Unsigned T>
|
||||
ALWAYS_INLINE constexpr u128 operator>>(const T& other) const
|
||||
{
|
||||
u64 underflow = m_high & other;
|
||||
return { (m_low >> other) | (underflow << (64 - other)), m_high >> other };
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr u128 operator&(const u128& other) const
|
||||
{
|
||||
return { m_low & other.low(), m_high & other.high() };
|
||||
}
|
||||
ALWAYS_INLINE constexpr u128 operator|(const u128& other) const
|
||||
{
|
||||
return { m_low | other.low(), m_high | other.high() };
|
||||
}
|
||||
ALWAYS_INLINE constexpr u128 operator^(const u128& other) const
|
||||
{
|
||||
return { m_low ^ other.low(), m_high ^ other.high() };
|
||||
}
|
||||
|
||||
// bitwise assign
|
||||
template<Unsigned T>
|
||||
constexpr u128& operator&=(const T& other)
|
||||
{
|
||||
m_high = 0;
|
||||
m_low &= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned T>
|
||||
constexpr u128& operator|=(const T& other)
|
||||
{
|
||||
m_low |= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned T>
|
||||
constexpr u128& operator^=(const T& other)
|
||||
{
|
||||
m_low ^= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned T>
|
||||
constexpr u128& operator>>=(const T& other)
|
||||
{
|
||||
*this = *this >> other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned T>
|
||||
constexpr u128& operator<<=(const T& other)
|
||||
{
|
||||
*this = *this << other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr u128& operator&=(const u128& other)
|
||||
{
|
||||
m_high &= other.high();
|
||||
m_low &= other.low();
|
||||
return *this;
|
||||
}
|
||||
constexpr u128& operator|=(const u128& other)
|
||||
{
|
||||
m_high |= other.high();
|
||||
m_low |= other.low();
|
||||
return *this;
|
||||
}
|
||||
constexpr u128& operator^=(const u128& other)
|
||||
{
|
||||
m_high ^= other.high();
|
||||
m_low ^= other.low();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
u8 m_bytes[16] = { 0 };
|
||||
struct {
|
||||
u64 m_low;
|
||||
u64 m_high;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(u128) == 16);
|
||||
|
||||
template<typename T>
|
||||
concept Unsigned_128 = IsUnsigned<T> || IsSame<T, u128>;
|
||||
|
||||
}
|
||||
|
||||
using X86::u128;
|
||||
using X86::Unsigned_128;
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<u128> : StandardFormatter {
|
||||
Formatter() = default;
|
||||
explicit Formatter(StandardFormatter formatter)
|
||||
: StandardFormatter(formatter)
|
||||
{
|
||||
}
|
||||
|
||||
void format(AK::FormatBuilder&, u128);
|
||||
};
|
|
@ -1,268 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "u128.h"
|
||||
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace X86 {
|
||||
|
||||
class u256 {
|
||||
public:
|
||||
constexpr u256() = default;
|
||||
constexpr u256(u64 val)
|
||||
: m_low(val)
|
||||
{
|
||||
}
|
||||
constexpr u256(u128 val)
|
||||
: m_low(val)
|
||||
{
|
||||
}
|
||||
constexpr u256(u128 val_low, u128 val_high)
|
||||
: m_low(val_low)
|
||||
, m_high(val_high)
|
||||
{
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u8* bytes()
|
||||
{
|
||||
return (u8*)this;
|
||||
}
|
||||
ALWAYS_INLINE const u8* bytes() const
|
||||
{
|
||||
return (const u8*)this;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u16* words()
|
||||
{
|
||||
return (u16*)this;
|
||||
}
|
||||
ALWAYS_INLINE const u16* words() const
|
||||
{
|
||||
return (const u16*)this;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32* double_words()
|
||||
{
|
||||
return (u32*)this;
|
||||
}
|
||||
ALWAYS_INLINE const u32* double_words() const
|
||||
{
|
||||
return (const u32*)this;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr u128& low()
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
ALWAYS_INLINE constexpr const u128& low() const
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr u128& high()
|
||||
{
|
||||
return m_high;
|
||||
}
|
||||
ALWAYS_INLINE constexpr const u128& high() const
|
||||
{
|
||||
return m_high;
|
||||
}
|
||||
// conversion
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr operator T() const
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr operator bool() const
|
||||
{
|
||||
return m_low || m_high;
|
||||
}
|
||||
|
||||
// comparisons
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr bool operator==(const T& other) const
|
||||
{
|
||||
return !m_high && m_low == other;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr bool operator!=(const T& other) const
|
||||
{
|
||||
return m_high || m_low != other;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr bool operator>(const T& other) const
|
||||
{
|
||||
return m_high || m_low > other;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr bool operator<(const T& other) const
|
||||
{
|
||||
return !m_high && m_low < other;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr bool operator>=(const T& other) const
|
||||
{
|
||||
return *this == other || *this > other;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr bool operator<=(const T& other) const
|
||||
{
|
||||
return *this == other || *this < other;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr bool operator==(const u256& other) const
|
||||
{
|
||||
return m_low == other.low() && m_high == other.high();
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator!=(const u256& other) const
|
||||
{
|
||||
return m_low != other.low() || m_high != other.high();
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator>(const u256& other) const
|
||||
{
|
||||
return m_high > other.high()
|
||||
|| (m_high == other.high() && m_low > other.low());
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator<(const u256& other) const
|
||||
{
|
||||
return m_high < other.high()
|
||||
|| (m_high == other.high() && m_low < other.low());
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator>=(const u256& other) const
|
||||
{
|
||||
return *this == other || *this > other;
|
||||
}
|
||||
ALWAYS_INLINE constexpr bool operator<=(const u256& other) const
|
||||
{
|
||||
return *this == other || *this < other;
|
||||
}
|
||||
|
||||
// bitwise
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr T operator&(const T& other) const
|
||||
{
|
||||
return m_low & other;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr u256 operator|(const T& other) const
|
||||
{
|
||||
return { m_low | other, m_high };
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr u256 operator^(const T& other) const
|
||||
{
|
||||
return { m_low ^ other, m_high };
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr u256 operator<<(const T& other) const
|
||||
{
|
||||
u128 overflow = m_low >> (128 - other);
|
||||
return { m_low << other, (m_high << other) | overflow };
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
ALWAYS_INLINE constexpr u256 operator>>(const T& other) const
|
||||
{
|
||||
u128 underflow = m_high & other;
|
||||
return { (m_low >> other) | (underflow << (128 - other)), m_high >> other };
|
||||
}
|
||||
|
||||
ALWAYS_INLINE constexpr u256 operator&(const u256& other) const
|
||||
{
|
||||
return { m_low & other.low(), m_high & other.high() };
|
||||
}
|
||||
ALWAYS_INLINE constexpr u256 operator|(const u256& other) const
|
||||
{
|
||||
return { m_low | other.low(), m_high | other.high() };
|
||||
}
|
||||
ALWAYS_INLINE constexpr u256 operator^(const u256& other) const
|
||||
{
|
||||
return { m_low ^ other.low(), m_high ^ other.high() };
|
||||
}
|
||||
|
||||
// bitwise assign
|
||||
template<Unsigned_128 T>
|
||||
constexpr u256& operator&=(const T& other)
|
||||
{
|
||||
m_high = 0;
|
||||
m_low &= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
constexpr u256& operator|=(const T& other)
|
||||
{
|
||||
m_low |= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
constexpr u256& operator^=(const T& other)
|
||||
{
|
||||
m_low ^= other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
constexpr u256& operator>>=(const T& other)
|
||||
{
|
||||
*this = *this >> other;
|
||||
return *this;
|
||||
}
|
||||
template<Unsigned_128 T>
|
||||
constexpr u256& operator<<=(const T& other)
|
||||
{
|
||||
*this = *this << other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr u256& operator&=(const u256& other)
|
||||
{
|
||||
m_high &= other.high();
|
||||
m_low &= other.low();
|
||||
return *this;
|
||||
}
|
||||
constexpr u256& operator|=(const u256& other)
|
||||
{
|
||||
m_high |= other.high();
|
||||
m_low |= other.low();
|
||||
return *this;
|
||||
}
|
||||
constexpr u256& operator^=(const u256& other)
|
||||
{
|
||||
m_high ^= other.high();
|
||||
m_low ^= other.low();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
// FIXME: Somehow make this a union to directly expose the bytes (see u128)
|
||||
u128 m_low {};
|
||||
u128 m_high {};
|
||||
};
|
||||
|
||||
static_assert(sizeof(u256) == 32);
|
||||
|
||||
template<typename T>
|
||||
concept Unsigned_256 = IsUnsigned<T> || IsSame<T, u128> || IsSame<T, u256>;
|
||||
|
||||
}
|
||||
|
||||
using X86::u256;
|
||||
using X86::Unsigned_256;
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<u256> : StandardFormatter {
|
||||
Formatter() = default;
|
||||
explicit Formatter(StandardFormatter formatter)
|
||||
: StandardFormatter(formatter)
|
||||
{
|
||||
}
|
||||
|
||||
void format(AK::FormatBuilder&, u256);
|
||||
};
|
Loading…
Reference in a new issue